Idioma

Gráficos y sonidos en lenguaje Ensamblador

GRÁFICOS


Una de las características más interesantes y únicas de las computadoras ATARI son sus excelentes gráficos. En comparación con otras microcomputadoras populares por la calidad de sus gráficos, ATARI es generalmente la clara ganadora. De hecho, la mayoría de los juegos tipo arcade disponibles para varias computadoras diferentes se ven mejor en ATARI y la publicidad habitualmente utiliza fotografías tomadas de la pantalla generada en un ATARI.


"Pero", dirá, "eso sólo está disponible en BASIC". Existe la idea errónea entre los propietarios de ATARI de que los comandos gráficos como PLOT y DRAWTO no se pueden usar sin el cartucho BASIC instalado. De hecho, todas las rutinas gráficas se encuentran en el Sistema Operativo y, por tanto, están disponibles en cualquier lenguaje. Ahora veremos cómo utilizar estas rutinas desde el lenguaje Ensamblador.


Cualquier programa que requiera comandos como GRAPHICS n, PLOT u otros comandos gráficos, generalmente los utiliza muchas veces. Por lo tanto, es más fácil presentar estas rutinas como un conjunto de subrutinas en lenguaje Ensamblador, que puedan llamarse desde cualquier programa. Estas rutinas se pueden guardar en un disco como un grupo y hacerles ENTER en cualquier programa que requiera rutinas gráficas. Para utilizar las rutinas en el programa, tendrá que cargar los registros X e Y y el Acumulador con los parámetros que desea implementar, y luego hacer JSR a la rutina apropiada. Tenga en cuenta que este paso de parámetros se analiza en los comentarios de cada rutina, para que su uso sea claro. La discusión detallada de las subrutinas aparece en la sección que sigue a los listados de los programas.


LAS SUBRUTINAS DE GRÁFICOS DEL LENGUAJE ENSAMBLADOR


Listado 10.1


            0100 ; ******************************
            0110 ; Equivalencias CIO
            0120 ; ******************************
0340        0130 ICHID =    $0340
0341        0140 ICDNO =    $0341
0342        0150 ICCOM =    $0342
0343        0160 ICSTA =    $0343
0344        0170 ICBAL =    $0344
0345        0180 ICBAH =    $0345
0346        0190 ICPTL =    $0346
0347        0200 ICPTH =    $0347
0348        0210 ICBLL =    $0348
0349        0220 ICBLH =    $0349
034A        0230 ICAX1 =    $034A
034B        0240 ICAX2 =    $034B
E456        0250 CIOV  =    $E456
            0260 ; ******************************
            0270 ; Se necesitan otras equivalencias
            0280 ; ******************************
02C4        0290 COLOR0 =   $02C4
0055        0300 COLCRS =   $55
0054        0310 ROWCRS =   $54
02FB        0320 ATACHR =   $02FB
00CC        0330 STORE1 =   $CC
00CD        0340 STOCOL =   $CD
0000        0350        *=  $600
            0360 ; ******************************
            0370 ; La rutina SETCOLOR
            0380 ; ******************************
            0390 ; Antes de llamar a esta rutina,
            0400 ; los registros deben configurarse
            0410 ; al igual que el SETCOLOR de BASIC:
            0420 ; SETCOLOR color, tono, luminancia
            0430 ; almacenado respectivamente en
            0440 ; Reg. X, Acumulador, Reg. Y.
            0450 SETCOL
0600 0A     0460        ASL A         ; Necesitamos multiplicar el
0601 0A     0470        ASL A         ; tono por 16, y
0602 0A     0480        ASL A         ; sumarlo a la lumimancia.
0603 0A     0490        ASL A         ; Ahora que el tono esta * por 16
0604 85CC   0500        STA STORE1    ; temporalmente
0606 98     0510        TYA           ; Entonces podemos sumar
0607 18     0520        CLC           ; Antes de la suma
0608 65CC   0530        ADC STORE1    ; Ahora tenemos la suma
060A 9DC402 0540        STA COLOR0,X  ; SETCOLOR real
060D 60     0550        RTS           ; Todo listo
            0560 ; ******************************
            0570 ; El comando COLOR
            0580 ; ******************************
            0590 ; Para estas rutinas, simplemente
            0600 ; almacenamos el valor actual de COLOR
            0610 ; en STOCOL, de manera que el comando
            0620 ;  COLOR simplemente requiere que
            0630 ; el Acumulador contenga el valor
            0640 ; "n" del comando COLOR n
            0650 COLOR
060E 85CD   0660        STA STOCOL    ; !Eso es todo!
0610 60     0670        RTS           ; Todo listo
            0680 ; ******************************
            6900 ; El comando GRAPHICS
            0700 ; ******************************
            0710 ; El parametro "n" del
            0720 ; comando GRAPHICS n es
            0730 ; pasado a esta rutina en el
            0740 ; Acumulador
            0750 GRAFIC
0611 48     0760        PHA           ; Almacenar en la Pila
0612 A260   0770        LDX #$60      ; IOCB6 para pantalla
0614 A90C   0780        LDA #$C       ; Comando CLOSE
0616 9D4203 0790        STA ICCOM,X   ; en byte del comando
0619 2056E4 0800        JSR CIOV      ; Haga el CLOSE
061C A260   0810        LDX #$60      ; La pantalla otra vez
061E A903   0820        LDA #3        ; Comando OPEN
0620 9D4203 0830        STA ICCOM,X   ; en byte del comando
0623 A9AD   0840        LDA #NAME&255 ; El nombre es "S:"
0625 9D4403 0850        STA ICBAL,X   ; Byte bajo
0628 A906   0860        LDA #NAME/256 ; Byte alto
062A 9D4503 0870        STA ICBAH,X
062D 68     0880        PLA           ; Obtener GRAPHICS n
062E 9D4B03 0890        STA ICAX2,X   ; Modo grafico
0631 29F0   0900        AND #$F0      ; Obtener los 4 bits mas altos
0633 4910   0910        EOR #$10      ; Dar vuelta el bit alto
0635 090C   0920        ORA #$C       ; Lectura o escritura
0637 9D4A03 0930        STA ICAX1,X   ; n+16, n+32 etc.
063A 2056E4 0940        JSR CIOV      ; Configurar GRAPHICS n
063D 60     0950        RTS           ; Todo listo
            0960 ; ******************************
            0970 ; El comando POSITION
            0980 ; ******************************
            0990 ; Identico al comando
            1000 ; POSITION X,Y del BASIC.
            1010 ; Dado que X puede ser mayor que
            1020 ; 255 en GRAPHICS 8, necesitamos
            1030 ; utilizar el Acumulador para el
            1040 ; byte alto de X.
            1050 POSITN
063E 8655   1060        STX COLCRS    ; Byte bajo de X
0640 8556   1070        STA COLCRS+1  ; Byte alto de X
0642 8454   1080        STY ROWCRS    ; Posicion Y
0644 60     1090        RTS           ; Todo listo
            1100 ; ******************************
            1110 ; El comando PLOT
            1120 ; ******************************
            1130 ; Usaremos X,Y, y A tal
            1140 ; como en el comando POSITION.
            1150 PLOT
0645 203E06 1160        JSR POSITN    ; Para almacenar informacion
0648 A260   1170        LDX #$60      ; Para la pantalla
064A A90B   1180        LDA #$B       ; Poner registro
064C 9D4203 1190        STA ICCOM,X   ; Byte del comando
064F A900   1200        LDA #0        ; Caso especial de
0651 9D4803 1210        STA ICBLL,X   ;  E/S usando el
0654 9D4903 1220        STA ICBLH,X   ;  Acumulador
0657 A5CD   1230        LDA STOCOL    ; Obtener COLOR a usar
0659 2056E4 1240        JSR CIOV      ; Trazar el punto
065C 60     1250        RTS           ; Todo listo
            1260 ; ******************************
            1270 ; El comando DRAWTO
            1280 ; ******************************
            1290 ; Usaremos X,Y, y A tal
            1300 ; como en el comando POSITION
            1310 DRAWTO
065D 203E06 1320        JSR POSITN    ; Para almacenar informacion
0660 A5CD   1330        LDA STOCOL    ; Obtener COLOR
0662 8DFB02 1340        STA ATACHR    ; Mantenga contento al CIO
0665 A260   1350        LDX #$60      ; La pantalla otra vez
0667 A911   1360        LDA #$11      ; Para DRAWTO
0669 9D4203 1370        STA ICCOM,X   ; Byte del comando
066C A90C   1380        LDA #$C       ; Como en XIO
066E 9D4A03 1390        STA ICAX1,X   ; Auxiliar 1
0671 A900   1400        LDA #0        ; Borrar
0673 9D4B03 1410        STA ICAX2,X   ; Auxiliar 2
0676 2056E4 1420        JSR CIOV      ; Trazar la linea
0679 60     1430        RTS           ; Todo listo
            1440 ; ******************************
            1450 ; El comando FILL
            1460 ; ******************************
            1470 ; Usaremos X,Y, y A tal
            1480 ; como en el comando POSITION.
            1490 ; Esto es similar a DRAWTO
            1500 FILL
067A 203E06 1510        JSR POSITN    ; Para almacenar informacion
067D A5CD   1520        LDA STOCOL    ; Obtener COLOR
067F 8DFB02 1530        STA ATACHR    ; Mantenga contento al CIO
0682 A260   1540        LDX #$60      ; La pantalla otra vez
0684 A912   1550        LDA #$12      ; Para FILL
0686 9D4203 1560        STA ICCOM,X   ; Bytel del comando
0689 A90C   1570        LDA #$C       ; Como en XIO
068B 9D4A03 1580        STA ICAX1,X   ; Auxiliar 1
068E A900   1590        LDA #0        ; Limpiar
0690 9D4B03 1600        STA ICAX2,X   ; Auxiliar 2
0693 2056E4 1610        JSR CIOV      ; Hacer FILL al area
0696 60     1620        RTS           ; Todo listo
            1630 ; ******************************
            1640 ; El comando LOCATE
            1650 ; ******************************
            1660 ; Usaremos X,Y, y A tal
            1670 ; como en el comando POSITION
            1680 ; y el Acumulador
            1690 ; contendra el color de LOCATE
            1700 LOCATE
0697 203E06 1710        JSR POSITN    ; Para almacenar informacion
069A A260   1720        LDX #$60      ; La pantalla otra vez
069C A907   1730        LDA #7        ; Obtener registro
069E 9D4203 1740        STA ICCOM,X   ; Byte del comando
06A1 A900   1750        LDA #0        ; Caso especial de
06A3 9D4803 1760        STA ICBLL,X   ; transferencia de datos
06A6 9D4903 1770        STA ICBLH,X   ; en el Acumulador
06A9 2056E4 1780        JSR CIOV      ; Haga el LOCATE
06AC 60     1790        RTS           ; Todo listo
            1800 ; ******************************
            1810 ; El nombre de la pantalla
            1820 ; ******************************
06AD 53     1830 NAME   .BYTE "S:",$9B
06AE 3A
06AF 9B	

DISCUSIÓN SOBRE LAS SUBRUTINAS GRÁFICAS


El primer punto a tener en cuenta acerca de estas rutinas es que simplemente usan las equivalencias estándar del CIO, que hemos visto tantas veces antes, más seis nuevas. No necesitamos un conjunto completamente nuevo de equivalencias, ya que estamos usando las rutinas estándar del CIO de ATARI para todos los comandos gráficos. De las 6 nuevas equivalencias, 2 son simplemente ubicaciones de almacenamiento: STOCOL se usa para almacenar la información de COLOR utilizada en varias de las rutinas, y STORE1 se usa para el almacenamiento temporal de información. Estos se encuentran ubicados arbitrariamente en $CD y $CC respectivamente, pero puede ponerlos en cualquier ubicación de memoria segura. Uno de esos lugares sería $100 y $101, que son las dos ubicaciones inferiores de la Pila. Otra de las nuevas equivalencias es COLOR0, que es la primera de las 5 ubicaciones utilizadas para almacenar información de color en las computadoras ATARI, que se encuentra en las posiciones decimales 708 al 712. La segunda es COLCRS, una ubicación de almacenamiento de 2 bytes ubicada en las direcciones $55 y $56, que siempre almacena la ubicación de la columna actual del cursor. Dado que en GRAPHICS 8 hay 320 ubicaciones horizontales posibles y sabemos que cada byte puede almacenar solo 256 valores posibles, necesitamos 2 bytes para almacenar todas las posiciones horizontales posibles del cursor. Sin embargo, en todos los modos gráficos distintos del GRAPHICS 8, es obvio que la ubicación $56 siempre será igual a cero. La tercera nueva equivalencia es ROWCRS, ubicación $54, que simplemente realiza un seguimiento de la posición vertical del cursor. Ningún modo gráfico tiene más de 192 posibles posiciones verticales del cursor, por lo que sólo se requiere 1 byte para almacenar esta información. La nueva equivalencia final es ATACHR, ubicación $2FB, que se utiliza para almacenar el color de la línea que se dibuja en las rutinas FILL y DRAWTO.


Estas rutinas se han ensamblado utilizando un origen de $600 por conveniencia. Si planea usarlos en un programa de lenguaje Ensamblador más grande, simplemente vuelva a numerar estas subrutinas a algunos números de línea altos, como 25000 y más, y combine estas rutinas con su programa antes de ensamblarlo. De esta manera, tendrá todos los comandos gráficos normales disponibles en lenguaje Ensamblador, sin necesidad de ingresarlos laboriosamente en cada programa que escriba.


La primera rutina es el equivalente en lenguaje Ensamblador del comando SETCOLOR del BASIC. Sabemos que esta es la forma estándar del comando:


SETCOLOR registro de color, tono, luminancia	

En la subrutina en lenguaje Ensamblador, primero necesitamos cargar los registros del 6502 con la información equivalente. Una rutina de llamada típica para simular el comando BASIC


SETCOLOR 2,4,10	

sería la siguiente:


25 LDX #2
30 LDA #4
35 LDY #10
40 JSR SETCOL	

Usamos la forma de 6 letras del nombre SETCOL en vez de SETCOLOR para que esta rutina sea compatible con todos los ensambladores disponibles para su ATARI. Si el ensamblador que está utilizando permite nombres de etiquetas de más de 6 caracteres, no dude en utilizar el nombre completo de la rutina. Esta misma convención se utilizará para todas las rutinas gráficas, por ejemplo, POSITN en vez de POSITION.


Para ejecutar el comando SETCOLOR, necesitamos sumar la luminancia con 16 veces el tono y almacenar el resultado en el registro de color apropiado. Para multiplicar el tono por 16, simplemente usaremos el Acumulador y realizaremos cuatro instrucciones ASL A. Como cada uno duplica el valor contenido en el Acumulador, el resultado es 16 veces el valor inicial. Después de la multiplicación, almacenaremos el resultado en nuestra ubicación de almacenamiento temporal y colocaremos la luminancia en el Acumulador con una instrucción TYA, preparándolo para la suma. Luego borramos el bit de Acarreo, como es habitual antes de la suma, y sumamos el resultado de nuestra multiplicación anterior a la luminancia. Finalmente, utilizamos el valor en el registro X, que es el registro de color deseado, como índice de las cinco ubicaciones de registros de color descritas anteriormente. Como en este ejemplo queremos hacer SETCOLOR 2, cargamos el registro X con 2 antes de llamar a la subrutina y la información del color se almacena en $2C6.


La siguiente rutina, el comando COLOR, es con diferencia la más sencilla de todas las rutinas. Para llamar al equivalente COLOR del comando BASIC


COLOR 3	

simplemente necesitamos el siguiente código en lenguaje Ensamblador:


25 LDA #3
30 JSR COLOR	

La rutina simplemente almacena el color seleccionado en nuestra ubicación de almacenamiento de color, STOCOL, donde estará disponible para las otras rutinas gráficas que lo requieran.


El comando GRAPHICS se implementa de manera similar. Para imitar el comando BASIC


GRAPHICS 23	

simplemente usamos el siguiente código en lenguaje Ensamblador:


25 LDA #23
30 JSR GRAFIC	

Lo primero que debemos hacer es almacenar el modo gráfico requerido. Podríamos almacenarlo en STORE1, pero colocarlo en la Pila es más rápido en este caso. No necesitamos hacer sumas o multiplicaciones, como hicimos en SETCOLOR. Las siguientes cuatro líneas de código simplemente cierran la pantalla como dispositivo. Esto es para estar seguros. Si la pantalla ya está cerrada, no hace daño. Sin embargo, si está abierto e intentamos volver a abrirlo, obtendremos un error, por lo que lo cerramos primero por seguridad. Tenga en cuenta que simplemente usando IOCB6 (Cargando el registro X con $60), especificamos la pantalla, usando el número de dispositivo predeterminado asignado por el ATARI.


El resto del comando GRAPHICS simplemente abre la pantalla en el modo gráfico particular que deseemos. En la línea 830 usamos nuevamente IOCB6, almacenando el comando OPEN en el byte de comando del IOCB. El nombre de la pantalla es S:, y cargamos la dirección de este nombre en ICBAL e ICBAH. Luego, el modo gráfico se recupera desde la Pila y se almacena en el segundo byte auxiliar. Los únicos bits importantes del modo gráfico en ICAX2 son los 4 bits inferiores, que especifican el modo gráfico en sí, en este caso, GRAPHICS 7. Los 4 bits superiores controlan el borrado de la pantalla, la presencia de la ventana de texto, etc., tal como se describe en el Capítulo 8. En este caso, hemos sumado 16 al modo gráfico, para eliminar la ventana de texto. Para aislar estos bits, hacemos AND entre el modo gráfico con $F0, lo que produce el nibble alto del modo gráfico. El Sistema Operativo requiere que el bit superior de esta información se invierta, por lo que A continuación, aplicamos EOR a este nibble con $10 para invertir el bit superior. Finalmente, configuramos el nibble bajo de este byte en $C, para permitir leer o escribir en la pantalla, y almacenamos el byte en ICAX1 del IOCB. La llamada al CIO completa nuestra rutina gráfica y configura la pantalla tal como queremos.


Como ya hemos comentado, el comando POSITION para GRAPHICS 8 requiere 320 ubicaciones X posibles, por lo que necesitamos 2 bytes para contener un número tan grande. Por lo tanto, para simular el comando


POSITION 285,73	

Almacenamos el byte bajo de la coordenada X en el registro X, el byte alto en el Acumulador y la coordenada Y en el registro Y, de la siguiente manera:


25 LDX #30
30 LDA #1
35 LDY #73
40 JSR POSITN	

Obviamente, en cualquier modo gráfico que no sea GRAPHICS 8, el Acumulador siempre se carga con un cero antes de llamar a POSITN, y el registro X simplemente contiene la coordenada X. La rutina misma almacena la información apropiada en las ubicaciones requeridas. La coordenada X se almacena en COLCRS y COLCRS+1, y la coordenada Y se almacena en ROWCRS.


El comando PLOT de


PLOT 258,13	

en BASIC se simula mediante el siguiente código en lenguaje Ensamblador:


25 LDX #3
30 LDA #1
35 LDY #13
40 JSR PLOT	

Esto utiliza la misma convención que el comando POSITN anterior. De hecho, la rutina PLOT comienza con un JSR POSITN, que almacena la información pasada a la rutina en las ubicaciones correctas para que las utilice el Sistema Operativo después de la llamada al CIO. Como queremos mostrar la salida en la pantalla, usamos IOCB6 y el byte de comando es $B para poner registro (put record). En este caso, simplemente queremos generar un solo byte de información, por lo que usamos el caso especial de E/S del Acumulador al que se accede estableciendo la longitud del buffer de salida en cero. Luego cargamos el Acumulador con la información del color que queremos trazar y la llamada al CIO traza el punto por nosotros.


Las rutinas para DRAWTO y FILL son tan similares que las discutiremos juntas. La secuencia de llamada es idéntica a los comandos PLOT y POSITION, por lo que para imitar el comando BASIC


DRAWTO 42,80	

usamos la secuencia


25 LDX #42
30 LDA #0
35 LDY #80
40 JSR DRAWTO	

Para usar el comando FILL, simplemente cambie la línea 40 a JSR FILL.


La rutina comienza con una llamada a la rutina POSITN para almacenar la información requerida. La información de color luego se almacena en ATACHR, y usamos IOCB6 nuevamente, cargando ICCOM con $11 para DRAWTO y con $12 para FILL. ICAX1 necesita un $C y limpiamos ICAX2 antes de completar la rutina llamando al CIO. Estas rutinas son absolutamente análogas a los respectivos comandos BASIC XIO que logran los mismos fines. Por ejemplo, para dibujar una línea, podemos usar este comando:


XIO 17,#6,12,0,"S:"	

En este comando, el 17 es el byte del comando $11, el #6 es el número IOCB, el 12 se almacena en ICAX1, el cero en ICAX2 y el nombre del dispositivo es S:. Nuevamente, se puede usar exactamente el mismo comando XIO para llenar un área, simplemente cambiando el 17 por el 18 ($12).


La rutina final, el comando LOCATE, es prácticamente idéntica al comando PLOT, excepto que usamos el comando obtener registro (get record), en lugar del comando poner registro. Se hace el mismo uso del modo especial de E/S del Acumulador de un solo byte, poniendo a cero tanto ICBLL como ICBLH. La rutina de llamada para duplicar el comando BASIC


LOCATE 10,12,A	

es como sigue:


25 LDX #10
30 LDA #0
35 LDY #12
40 JSR LOCATE	

En este caso, el Acumulador contendrá el valor de color encontrado en las coordenadas 10, 12 después de la llamada a la rutina LOCATE, por lo que un comando STA podría guardar esta información, o podría usarse inmediatamente, comparándola con algún valor deseado. O de alguna otra manera.


Con esto concluye la discusión sobre las contrapartes en lenguaje Ensamblador de los comandos gráficos del BASIC. Úselo en algunos programas sencillos y verá cómo pronto le resultan familiares y fáciles de usar. De hecho, son casi tan fáciles de usar como los comandos BASIC. Sin embargo, dado que tanto BASIC como el lenguaje Ensamblador utilizan las mismas rutinas del Sistema Operativo para realizar operaciones como DRAWTO y FILL, no espere que las rutinas del lenguaje Ensamblador sean mucho más rápidas que las rutinas BASIC a las que está acostumbrado. Serán un poco más rápidos, ya que no tiene que pagar los gastos generales que BASIC requiere en términos de tiempo, pero no experimentará ni de lejos la diferencia en velocidad que ahora espera al convertir de BASIC a programación en lenguaje Ensamblador. Para lograr este tipo de aceleración, tendrá que escribir sus propias rutinas DRAWTO y FILL, usando una lógica totalmente diferente a la utilizada por el Sistema Operativo del ATARI. Estas rutinas son mucho más rápidas que las rutinas del Sistema Operativo, pero no son de dominio público y tendrá que escribir las suyas si la velocidad es un punto crítico.


Ahora que domina el lenguaje Ensamblador, es posible que desee cambiar las rutinas centrales del ATARI para sus propios fines. Si desea probar esto, se recomienda encarecidamente comprar las notas técnicas del usuario y los listados del Sistema Operativo. Luego puede consultar el código fuente comentado de las rutinas del Sistema Operativo y modificarlo para sus propias rutinas. Simplemente inclúyalos como parte de sus propios programas, realizando las modificaciones que desee. Sin embargo, recuerde que el código del Sistema Operativo pertenece a ATARI. Puede utilizar dichas modificaciones en programas para su propio uso, pero asegúrese de obtener el permiso de ATARI antes de intentar ofrecer a la venta cualquier programa que contenga partes del Sistema Operativo. Un cambio fácil de probar es permitir trazar y dibujar sin comprobar si el cursor está fuera de rango, lo que ralentiza bastante el proceso. Sólo asegúrese de que su programa calcule los valores correctamente, o si no...


Recuerde que todo lo que es posible desde el BASIC también lo es en lenguaje Ensamblador. Un ejemplo frecuentemente utilizado de esto es la animación mediante la rotación de los registros de color, posible usando los modos especiales GTIA o incluso los modos gráficos regulares. Una rutina muy sencilla puede rotar los registros de color estándar de forma prácticamente instantánea:


15 LDA $708
20 STA STOCOL
25 LDA $709
30 STA $708
35 LDA $710
40 STA $709
45 LDA $711
50 STA $710
55 LDA $712
60 STA $711
65 LDA STOCOL
70 STA $712	

Ahora que puede dibujar gráficos detallados a partir de programas en lenguaje Ensamblador. Este truco se puede utilizar para animar imágenes prácticamente sin ralentización en la ejecución del programa. Por ejemplo, la implementación de un juego del tipo "abajo de la trinchera" es sencilla, ya que permite que la rotación de los colores dé la ilusión de movimiento a lo largo de la trinchera.


GRÁFICOS PLAYER-MISSILE DESDE EL LENGUAJE ENSAMBLADOR


Otra característica interesante de las computadoras ATARI son los gráficos Player-Missile. Ya hemos visto un ejemplo usando una subrutina en lenguaje Ensamblador para mover un Player. Pero en ese programa, toda la configuración para los gráficos Player-Missile estaba en BASIC, y sólo la rutina para mover el Player estaba en lenguaje Ensamblador. Para mostrar cómo realizar estas mismas operaciones en un programa puramente en lenguaje Ensamblador, se presenta A continuación, este programa BASIC totalmente traducido al lenguaje Ensamblador En este programa se utilizan en su mayor parte direcciones decimales, ya que así fue escrito el programa BASIC; este programa en lenguaje Ensamblador es lo más similar al programa BASIC.


Listado 10.2


            0100 ; ******************************
            0110 ; Equivalencias CIO
            0120 ; ******************************
0340        0130 ICHID =    $0340
0341        0140 ICDNO =    $0341
0342        0150 ICCOM =    $0342
0343        0160 ICSTA =    $0343
0344        0170 ICBAL =    $0344
0345        0180 ICBAH =    $0345
0346        0190 ICPTL =    $0346
0347        0200 ICPTH =    $0347
0348        0210 ICBLL =    $0348
0349        0220 ICBLH =    $0349
034A        0230 ICAX1 =    $034A
034B        0240 ICAX2 =    $034B
E456        0250 CIOV  =    $E456
            0260 ; ******************************
            0270 ; Se necesitan otras equivalencias
            0280 ; ******************************
00CC        0290 YLOC   =   $CC       ; Direccion indirecta para Y
00CE        0300 XLOC   =   $CE       ; Para recordar la posicion X
00D0        0310 INITX  =   $D0       ; Valor inicial de X
00D1        0320 INITY  =   $D1       ; Valor inicial de Y
0100        0330 STOTOP =   $100      ; Almacenamiento temporal (ADRESS)
D300        0340 STICK  =   $D300     ; PORTA - Ubicacion Hardware STICK(0)
D000        0350 HPOSP0 =   $D000     ; Posicion Horizontal del Player 0
0000        0360        *=  $600
            0370 ; ******************************
            0380 ; Primero, bajamos la parte superior de la RAM
            0390 ; ******************************
0600 A56A   0400        LDA 106       ; Obtenemos la parte superior de la RAM
0602 8D0001 0410        STA STOTOP    ; Almacenamiento temporal
0605 38     0420        SEC           ; Configurar para restar
0606 E908   0430        SBC #8        ; Guardar 8 paginas para PMG
0608 856A   0440        STA 106       ; Digale a Atari cual es el nuevo RAMTOP
060A 8D07D4 0450        STA 54279     ; PMBASE
060D 85CF   0460        STA XLOC+1    ; Para borrar RAM de PM
060F A900   0470        LDA #0        ; ponga la direccion
0611 85CE   0480        STA XLOC      ; indirecta aqui.
            0490 ; ******************************
            0500 ; A continuacion, reinicie GRAPHICS 0
            0510 ; ******************************
0613 A900   0520        LDA #0        ; GRAPHICS 0
0615 48     0530        PHA           ; Almacenar en la Pila
0616 A260   0540        LDX #$60      ; IOCB6 para pantalla
0618 A90C   0550        LDA #$C       ; comando CLOSE
061A 9D4203 0560        STA ICCOM,X   ; en byte del comando
061D 2056E4 0570        JSR CIOV      ; Haga CLOSE
0620 A260   0580        LDX #$60      ; La pantalla otra vez
0622 A903   0590        LDA #3        ; comando OPEN
0624 9D4203 0600        STA ICCOM,X   ; en byte del comando
0627 A9ED   0610        LDA #NAME&255 ; El nombre es "S:"
0629 9D4403 0620        STA ICBAL,X   ;  Byte bajo
062C A906   0630        LDA #NAME/256 ;  Byte alto
062E 9D4503 0640        STA ICBAH,X
0631 68     0650        PLA           ; Obtener GRAPHICS 0
0632 9D4B03 0660        STA ICAX2,X   ; Modo gradicto
0635 29F0   0670        AND #$F0      ; Obtener los 4 bits altos
0637 4910   0680        EOR #$10      ; Voltear bit alto
0639 090C   0690        ORA #$C       ; Leer o escribir
063B 9D4A03 0700        STA ICAX1,X   ; n+16, n+32 etc.
063E 2056E4 0710        JSR CIOV      ; Configurar GRAPHICS 0
            0720 ; ******************************
            0730 ; Ahora configure los graficos Player/Missile
            0740 ; ******************************
0641 A978   0750        LDA #120      ; Valor X inicial
0643 85D0   0760        STA INITX     ; Pongalo en su lugar
0645 A932   0770        LDA #50       ; Valor Y inicial
0647 85D1   0780        STA INITY     ; Pongalo en su lugar
0649 A92E   0790        LDA #46       ; Linea de doble
064B 8D2F02 0800        STA 559       ; resolucion - SDMCTL
            0810 ; ******************************
            0820 ; Ahora borre el area de los PM en la RAM
            0830 ; ******************************
064E A000   0840        LDY #0        ; Usar como contador
            0850 CLEAR
0650 A900   0860        LDA #0        ; Byte a almacenar
0652 91CE   0870        STA (XLOC),Y  ; Borrar el 1er byte
0654 88     0880        DEY           ; ?Pagina terminada?
0655 D0FB   0890        BNE CLEAR     ; Pagina no terminada
0657 E6CF   0900        INC XLOC+1    ; Pagina terminada
0659 A5CF   0910        LDA XLOC+1    ; On to next page
065B CD0001 0920        CMP STOTOP    ; ?Terminamos?
065E F0F2   0930        BEQ CLEAR     ; Una pagina mas
0660 90F0   0940        BCC CLEAR     ; Continue
            0950 ; ******************************
            0960 ; Ahora insertaremos el Player en
            0970 ; el lugar apropiado en el 
            0980 ; area RAM de los PMG
            0990 ; ******************************
0662 A56A   1000        LDA 106       ; Primero, calcule la 
0664 18     1010        CLC           ; posicion correcta de Y.
0665 6902   1020        ADC #2        ; PMBASE+512 (2 paginas)
0667 85CD   1030        STA YLOC+1    ; Byte alto de YLOC
0669 A5D1   1040        LDA INITY     ; Sumar coordenada Y de la pantalla
066B 85CC   1050        STA YLOC      ; Para el byte bajo
066D A000   1060        LDY #0        ; Como contador
            1070 INSERT
066F B9F006 1080        LDA PLAYER,Y  ; Obtener byte del Player
0672 91CC   1090        STA (YLOC),Y  ; Pongalo en su lugar
0674 C8     1100        INY           ; Para el siguiente byte
0675 C008   1110        CPY #8        ; ?Terminamos?
0677 D0F6   1120        BNE INSERT    ; No
0679 A5D0   1130        LDA INITX     ; Obtener X inicial
067B 8D00D0 1140        STA HPOSP0    ; Digale al Atari
067E 85CE   1150        STA XLOC      ; Tambien aqui
0680 A944   1160        LDA #68       ; Hacer que el Player sea rojo
0682 8DC002 1170        STA 704       ; como en BASIC - PCOLR0
0685 A903   1180        LDA #3        ; Para habilitar los graficos
0687 8D1DD0 1190        STA 53277     ;  Player/Missile - GRACTL
            1200 ; ******************************
            1210 ; El ciclo principal - muy corto
            1220 ; ******************************
            1230 MAIN
068A 209A06 1240        JSR RDSTK     ; Leer palanca - mover el player
068D A205   1250        LDX #5        ; Para controlar al
068F A000   1260        LDY #0        ; Player, tenemos
            1270 DELAY
0691 88     1280        DEY           ; que agregar
0692 D0FD   1290        BNE DELAY     ; un retraso: esta
0694 CA     1300        DEX           ; rutina ralentiza
0695 D0FA   1310        BNE DELAY     ; las cosas.
0697 4C8A06 1320        JMP MAIN      ; Y hazlo de nuevo
            1330 ; ******************************
            1340 ; Ahora lea el joystick #1
            1350 ; ******************************
            1360 RDSTK
069A AD00D3 1370        LDA STICK     ; Obtener el valor del joystick
069D 2901   1380        AND #1        ; ?Es el bit 0 = 1?
069F F016   1390        BEQ UP        ; No - 11, 12 o 1 en punto
06A1 AD00D3 1400        LDA STICK     ; Lo obtenemos de nuevo
06A4 2902   1410        AND #2        ; ?Es el bit 1 = 1?
06A6 F020   1420        BEQ DOWN      ; No - 5, 6 o 7 en punto
06A8 AD00D3 1430 SIDE   LDA STICK     ; Lo obtenemos de nuevo
06AB 2904   1440        AND #4        ; ?Es el bit 3 = 1?
06AD F02E   1450        BEQ LEFT      ; No - 8, 9 o 10 en punto
06AF AD00D3 1460        LDA STICK     ; Lo obtenemos de nuevo
06B2 2908   1470        AND #8        ; ?Es el bit 4 = 1?
06B4 F02F   1480        BEQ RIGHT     ; No - 2, 3 o 4 en punto
06B6 60     1490        RTS           ; Joystick hacia arriba
            1500 ; ******************************
            1510 ; Ahora mueva el Player apropiadamente,
            1520 ; comenzando con un movimiento ascendente.
            1530 ; ******************************
06B7 A001   1540 UP     LDY #1        ; Configuracion para mover el byte 1
06B9 C6CC   1550        DEC YLOC      ; Ahora 1 menos que YLOC
06BB B1CC   1560 UP1    LDA (YLOC),Y  ; Obtener 1er byte
06BD 88     1570        DEY           ; Para subirlo una posicion
06BE 91CC   1580        STA (YLOC),Y  ; Muevalo
06C0 C8     1590        INY           ; Ahora el valor original
06C1 C8     1600        INY           ; Ahora configurado para el siguiente byte
06C2 C00A   1610        CPY #10       ; ?Terminamos?
06C4 90F5   1620        BCC UP1       ; No
06C6 B0E0   1630        BCS SIDE      ; !!!Bifurcacion forzada!!!
            1640 ; ******************************
            1650 ; Mover el Player hacia abajo
            1660 ; ******************************
06C8 A007   1670 DOWN   LDY #7       ; Mover el byte superior primero
06CA B1CC   1680 DOWN1  LDA (YLOC),Y ; Obtener el byte superior
06CC C8     1690        INY          ; para moverlo hacia abajo en la pantalla
06CD 91CC   1700        STA (YLOC),Y ; Muevalo
06CF 88     1710        DEY          ; Ahora volvamos al valor inicial
06D0 88     1720        DEY          ; Prepararse para el siguiente byte inferior
06D1 10F7   1730        BPL DOWN1    ; Si Y >= 0 continue
06D3 C8     1740        INY          ; Establecer en cero
06D4 A900   1750        LDA #0       ; para borrar el byte superior
06D6 91CC   1760        STA (YLOC),Y ; Limpielo
06D8 E6CC   1770        INC YLOC     ; Ahora es 1 mayor
06DA 18     1780        CLC          ; Configuracion para bifurcacion forzada
06DB 90CB   1790        BCC SIDE     ; Bifurcacion forzada otra vez
            1800 ; ******************************
            1810 ; Ahora de lado a lado: primero a la izquierda
            1820 ; ******************************
06DD C6CE   1830 LEFT   DEC XLOC     ; Para moverlo a la izquierda
06DF A5CE   1840        LDA XLOC     ; Obtengalo
06E1 8D00D0 1850        STA HPOSP0   ; Muevalo
06E4 60     1860        RTS          ; Volver a  MAIN - terminamos
            1870 ; ******************************
            1880 ; Ahora movimiento a la derecha
            1890 ; ******************************
06E5 E6CE   1900 RIGHT  INC XLOC     ; Para moverlo a la derecha
06E7 A5CE   1910        LDA XLOC     ; Obtengalo
06E9 8D00D0 1920        STA HPOSP0   ; Muevalo
06EC 60     1930        RTS          ; Volver a MAIN - terminamos
            1940 ; ******************************
            1950 ; Declaraciones DATA
            1960 ; ******************************
06ED 53     1970 NAME   .BYTE "S:",$9B
06EE 3A
06EF 9B
06F0 FF     1980 PLAYER .BYTE 255,129,129,129,129,129,129,255
06F1 81
06F2 81
06F3 81
06F4 81
06F5 81
06F6 81
06F7 FF	

Este programa utiliza muchas de las rutinas que ya hemos comentado: una rutina para borrar el área de memoria de los Player/Missile, lectura del joystick y movimiento del Player, y el comando GRAPHICS 0. Aquí, simplemente los juntamos todos en un programa grande que realiza todas las tareas necesarias para implementar un ejemplo simple de gráficos Player/Missile en lenguaje Ensamblador.


Dado que es análogo al programa BASIC que ya hemos escrito, la versión en Assembler comienza reduciendo RAMTOP en 8 páginas para dejar espacio para la memoria de los Player/Missile: Las líneas 400 a 440 realizan esta función. La línea 410 almacena el valor antiguo de RAMTOP para la rutina de borrado posterior. La línea 450 le dice al ATARI cual es la ubicación de PMBASE, el nuevo valor de RAMTOP. Usaremos XLOC y XLOC+1 como ubicación de dirección indirecta temporal en la Página Cero, para ayudar en la rutina de borrado. Las líneas 520 a 710 luego restablecen una pantalla GRAPHICS 0 debajo de la nueva ubicación de RAMTOP. Las líneas 750 a 780 simplemente almacenan los valores iniciales de X e Y, las coordenadas en la pantalla donde queremos que aparezca el Player. Estos valores se utilizarán más adelante y estas líneas están aquí sólo para mantener la analogía con el programa BASIC. No es necesario almacenar estos valores. También podríamos haber usado los números directamente más adelante en el programa. Sin embargo, cualquiera de las dos formas funciona y es un poco más fácil cambiar el programa más adelante si está escrito de esta manera. Las líneas 790 y 800 configuran una resolución de doble línea, y luego borramos toda el área del Player/Missile en las líneas 840 a 940.


En el programa BASIC, el lugar en la memoria donde insertamos el Player para lograr la posición Y correcta en la pantalla es:


PMBASE+512+INITY	

Sabemos que 512 bytes por encima de PMBASE son 2 páginas, ya que cada página contiene 256 bytes. Por lo tanto, sabemos que el byte alto de esta dirección, en nuestro programa en lenguaje Ensamblador, debe ser 2 más que PMBASE. En las líneas 1000 a 1030, obtenemos PMBASE, sumamos 2 y almacenamos el resultado en YLOC+1, el byte alto de la posición Y en la memoria. El byte bajo es simplemente INITY, la posición Y inicial. Recuerde: Cuanto más abajo en la pantalla desee que aparezca el Player, más arriba en la memoria deberá almacenarse.


Para insertar el Player en el lugar correcto de la memoria, leemos un byte a la vez de la tabla de datos llamada PLAYER y lo almacenamos mediante direccionamiento indirecto en la ubicación de memoria que acabamos de configurar. Cuando Y, nuestro contador, es igual a 8, terminamos, ya que comenzamos en cero y solo tenemos 8 bytes para transferir. Si nuestro Player hubiera sido más grande, simplemente habríamos tenido que cambiar el byte único en la línea 1110 a 1 byte más que el número de bytes en el Player. La posición X inicial del Player se lee desde INITX y se almacena en el registro de posición horizontal para el Player Cero, 53248. También se almacena en XLOC para que la utilice la rutina de mover Player.


Luego hacemos que el Player sea rojo almacenando el número 68 en el registro de color para el Player Cero, 704, y habilitamos gráficos Player/Missile almacenando un 3 en 53277, GRACTL.


El ciclo principal de este programa es la simplicidad misma. Realizamos JSR a la rutina que lee el joystick y mueve al Player, y luego ingresamos a un breve ciclo de retardo. ¡Si omitimos este ciclo, el Player se moverá tan rápido que no podremos controlarlo en absoluto! A continuación, simplemente retrocedemos para leer el joystick y mover el Player nuevamente.


Obviamente, si desea agregar algo de interés a este programa, puede insertar su propia lógica de programa en este ciclo principal, para detectar colisiones entre Players y campos de juego (Playfield), crear obstáculos o cualquier otra cosa que desee en su juego. Sin embargo, si va a alargar el programa, debe cambiar el origen a algún lugar más alto en la memoria. Tal como están las cosas, este programa ya ocupa prácticamente toda la Página 6 por lo que, si lo agranda sin cambiar el origen, comenzará a sobrescribir el DOS y no podrá guardarlo ni cargarlo. Simplemente cambie el origen a $6000 o alguna otra ubicación segura de memoria alta. Para probar el programa después de ensamblarlo, simplemente escriba BUG para ingresar al depurador y luego escriba G600 para la versión original (o G6000 si cambia el origen).


Ahora que ha visto cómo implementar gráficos Player/Missile desde el lenguaje Ensamblador, puede escribir sus propios programas utilizando estas mismas técnicas. Al hacer esto, como ya hemos visto por la necesidad de insertar un ciclo de retardo en el programa anterior, acelerará enormemente las cosas y creará movimientos suaves de los Players que mejorarán enormemente sus juegos. ¡Diviértase!


CREAR SONIDO EN LAS COMPUTADORAS ATARI


Comenzaremos nuestra discusión sobre el sonido aprendiendo cómo el ATARI produce sonidos y luego escribiremos una subrutina en lenguaje Ensamblador para imitar el comando SOUND del BASIC.


Veamos primero las equivalencias utilizadas para la generación de sonido. El chip POKEY es responsable de la creación de todos los sonidos en las computadoras ATARI y reside en la memoria, desde la dirección $D200 hasta la $D2FF. Los sonidos que añaden mucho al disfrute de los juegos e incluso pueden contribuir a la facilidad de uso de los programas comerciales si se usan correctamente, se dividen en cuatro voces. Cada voz está controlada por dos registros, ubicados en pares desde $D200 hasta $D207. El primero de cada par es el control de frecuencia y el segundo de cada par controla tanto el volumen como la distorsión del sonido producido por ese canal. Estos son:


100 AUDF1 = $D200 ; Frecuencia del canal de audio 1
110 AUDC1 = $D201; Control del canal de audio 1
120 AUDF2 = $D202 ; Frecuencia del canal de audio 2
130 AUDC2 = $D203; Control del canal de audio 2
140 AUDF3 = $D204 ; Frecuencia del canal de audio 3
150 AUDC3 = $D205; Control del canal de audio 3
160 AUDF4 = $D206 ; Frecuencia del canal de audio 4
170 AUDC4 = $D207; Control del canal de audio 4	

Los respectivos registros de frecuencia controlan el tono del sonido o nota que se reproduce. Estos registros en realidad dividen la frecuencia del sonido por el número almacenado aquí. Es decir, si almacenamos un 12, entonces la frecuencia producida es una doceava parte de la frecuencia de entrada.


La frecuencia de entrada se controla mediante la inicialización de POKEY, configurando AUDCTL. El siguiente cuadro describe el uso de cada bit en AUDCTL:


BitUso
0Establecer en 1 para cambiar el reloj principal de 64 kHz a 15 kHz
1Establecer en 1 para insertar un filtro de paso alto en el canal 2
2Establecer en 1 para insertar un filtro de paso alto en el canal 1
3Establecer en 1 para unir los canales 4 y 3 para una resolución de 16 bits
4Establecer en 1 para unir los canales 2 y 1 para una resolución de 16 bits
5Establecer en 1 para sincronizar el canal 3 con 1,79 MHz
6Establecer en 1 para sincronizar el canal 1 con 1,79 MHz
7Establecer en 1 para convertir el policontador de 17 bits en 9 bits

¿Qué significa todo esto? Veámoslo poco a poco. Supongamos que se almacena un 10 en el registro de frecuencia de la voz 1. Ya sabemos que esto hará que salga un pulso de esa voz por cada diez que entren. El bit 0 de AUDCTL puede cambiar la frecuencia de los pulsos entrantes entre 64 kHz y 15 kHz. Kilohertz (kHz) significa miles de ciclos o pulsos por segundo. Obviamente, si AUDCTL se establece con el bit 0 igual a cero, entonces la frecuencia de salida de la voz 1 es de 6,4 kHz. Sin embargo, si almacenamos un uno en el bit 0 de AUDCTL, entonces la frecuencia de salida de la voz 1 será de 1,5 kHz y el resultado será un tono notablemente más bajo. Los bits 5 y 6 funcionan exactamente igual, pero si los establecemos en 1, las voces controladas por ellos, los canales 3 y 1 respectivamente, producirán tonos mucho más altos, ya que estarían sincronizados a 1,79 MHz (millones de ciclos por segundo), muchas veces más rápido que cualquiera de las frecuencias anteriores.


Los bits 1 y 2 de AUDCTL insertan filtros de paso alto en los canales 2 y 1, respectivamente. Estos filtros de paso alto están sincronizados por los canales 4 y 3, respectivamente. Es decir, solo los sonidos con una frecuencia más alta que los que suenan actualmente en el canal 3 se escucharán en el canal 1, y solo los sonidos con una frecuencia más alta que los que suenan actualmente en el canal 4 se escucharán en el canal 2. Usando estos filtros de paso alto es posible realizar algunos efectos especiales espectaculares. Seguramente querrá experimentar para ver qué se puede hacer.


Dado que la frecuencia se almacena en un solo byte, las voces del ATARI están limitadas a un rango de aproximadamente cinco octavas. Sin embargo, utilizando AUDCTL, es posible emparejar conjuntos de dos voces utilizando los bits 3 ó 4. Esto permite que los dos registros de frecuencia de estas voces formen un número de 16 bits, lo que da un rango de nueve octavas. Esto disminuye el número de voces disponibles, pero sería perfectamente factible producir una voz de nueve octavas y dos voces de cinco octavas, o incluso dos voces de nueve octavas. Cuando dos registros de frecuencia se combinan en uno, el mayor de los dos registros de frecuencia controla el byte alto del número de 2 bytes y el menor de los dos controla el byte inferior.


El bit alto de AUDCTL controla el contador polinomial. Este es quizás el concepto más difícil de comprender sobre la generación de sonido en las computadoras ATARI. Básicamente, los policontadores producen una secuencia aleatoria de pulsos que se repite después de un tiempo. Cuanto mayor sea el número de bits en el policontador, más larga será la secuencia aleatoria antes de que se repita el patrón. Hay tres policontadores diferentes en el ATARI y todos funcionan de la siguiente manera.


Supongamos que desea que se produzca algo de ruido. La música tiene un tono regular, pero el ruido es irregular y es más difícil de producir. El ATARI genera ruido al producir una secuencia aleatoria de pulsos desde el policontador y haciendo AND de manera efectiva con estos pulsos junto con la salida de los registros de frecuencia discutidos anteriormente. Sólo cuando ambos pulsos están activados se produce un sonido. Por ejemplo, si el registro de frecuencia dice que se debe producir un pulso o sonido, pero el patrón aleatorio del policontador está desactivado en ese momento, no se produce ningún sonido. Por lo tanto, aunque la salida del sistema de división por n del registro de frecuencia es una frecuencia pura, la combinación AND de estos pulsos con la secuencia aleatoria generada por los policontadores produce ruido. Debería ser evidente que diferentes policontadores producen diferentes sonidos de ruido, y el policontador utilizado puede seleccionarse mediante el bit 7 de AUDCTL, que es un policontador de 9 o 17 bits.


Además, la distorsión o tipo de ruido del sonido producido también se puede cambiar mediante el ajuste de distorsión, tal como en BASIC. La distorsión está controlada por los 3 bits superiores del registro de control de cada voz, AUDC1-4. Básicamente, estos bits eligen cómo se tratará el sonido y cuál de los tres policontadores se utilizará para la distorsión, de la siguiente manera:


Bit 7Bit 6Bit 5Significado
000Seleccione usando 5 bits, luego un policontador de 17 bits, divida por 2
0E1Seleccione usando un policontador de 5 bits, divida por 2
010Seleccione usando 5 bits, luego un policontador de 4 bits , divida por 2
100Seleccione usando un policontador de 17 bits, divida por 2
1E1No se utilizan contadores policontador, solo divida por 2
110Seleccione usando un policontador de 4 bits, divida por 2

"E" en la tabla anterior significa que el bit puede ser 1 ó 0. Primero, por supuesto, la velocidad del reloj se divide por la frecuencia. Veamos un ejemplo de cómo funciona el sistema de distorsión. Supondremos que el reloj funciona a 15 kHz, que hemos almacenado 30 en el registro de frecuencia apropiado y que los 3 bits altos del registro de control para esa voz son 010. Primero, la velocidad del reloj se divide por registro de frecuencia, en este caso, 15000/30 = 500 Hz. A continuación, dado que los bits de distorsión son 010, los pulsos a 500 Hz de salida de esta operación se combinan efectivamente haciendo AND con la salida del policontador de 5 bits. Luego, la salida de esta operación se aplica efectivamente haciendo AND con la salida del policontador de 4 bits, y la frecuencia de los pulsos que logran completar toda esta operación se divide por 2 para producir el sonido distorsionado final.


Debería ser obvio que, con tantas opciones para elegir, ATARI es capaz de generar muchos, muchos, muchos efectos de sonido diferentes. Es evidente que aquí es necesario realizar algo de experimentación. ¡Es posible que escuche algunos sonidos realmente extraños!


UNA SUBRUTINA DE SONIDO


La creación de sonidos en las computadoras ATARI es extremadamente fácil en BASIC, ya que el comando SOUND nos permite activar o desactivar cualquiera de las cuatro voces disponibles con cualquier distorsión, tono, volumen y frecuencia deseada. Exactamente las mismas funciones están disponibles en lenguaje Ensamblador. Podemos escribir una subrutina para imitar los efectos del comando SOUND del BASIC, tal como lo hicimos con los comandos gráficos en la sección anterior.


Listado 10.3


            0100 ; ******************************
            0110 ; Equivalencias SOUND
            0120 ; ******************************
D200        0130 AUDF1  =    $D200    ; Frecuencia de Audio 1
D201        0140 AUDC1  =    $D201    ; Control de Audio 1
D208        0150 AUDCTL =    $D208    ; Control de Audio
D20F        0160 SKCTL  =    $D20F    ; Control del puerto serie
0101        0170 STORE2 =    $101     ; Almacenamiento temporal
0000        0180        *=   $600
            0190 ; ******************************
            0200 ; El comando SOUND
            0210 ; ******************************
            0220 ; Antes de llamar a esta rutina,
            0230 ; el registro X debe contener la
            0240 ; voz deseada, el Acumulador
            0250 ; debe contener la distorsion,
            0260 ; el registro Y debe contener
            0270 ; el volumen deseado y STORE2
            0280 ; debe contener la frecuencia
            0290 ; deseada.
            0300 SOUND
0600 48     0310        PHA           ; Almacenar distorcion
0601 8A     0320        TXA           ; Valor de voz doble
0602 0A     0330        ASL A         ; para desplazamiento del
0603 AA     0340        TAX           ; control de la voz
0604 AD0101 0350        LDA STORE2    ; Frecuencia en el
0607 9D00D2 0360        STA AUDF1,X   ; canal correcto
060A 8C0101 0370        STY STORE2    ; Para uso posterior
060D A900   0380        LDA #0        ; Para inicializar el
060F 8D08D2 0390        STA AUDCTL    ; chip POKEY
0612 A903   0400        LDA #3        ; configurelos tal como se
0614 8D0FD2 0410        STA SKCTL     ; indica
0617 68     0420        PLA           ; Recuperar distorcion
0618 0A     0430        ASL A         ; Ahora multiplicar por
0619 0A     0440        ASL A         ; 16 para obtener la
061A 0A     0450        ASL A         ; distorcion en
061B 0A     0460        ASL A         ; el nibble alto
061C 18     0470        CLC           ; Configuracion para sumar
061D 6D0101 0480        ADC STORE2    ; Sume el volumen
0620 9D01D2 0490        STA AUDC1,X   ; en la voz correcta
0623 60     0500        RTS           ; Eso es todo	

En este programa, duplicamos el valor originalmente almacenado en el registro X, que seleccionará la voz particular que se utilizará. Esto se debe a que los registros de sonido están dispuestos en pares, por lo que cada uno de los registros de control y cada uno de los registros de frecuencia están separados por dos bytes en la memoria. Luego, la frecuencia se recupera desde STORE2, que se utiliza porque necesitamos cuatro datos de información para el sonido, y entre los registros X e Y y el Acumulador, solo tenemos tres ubicaciones de almacenamiento a mano. Luego, la frecuencia se almacena en el registro de frecuencia apropiado en la línea 360 y el volumen se almacena temporalmente hasta que lo necesitemos. Luego inicializamos POKEY en las líneas 380 a 410 y convertimos la distorsión a los bits superiores del Acumulador, agregando el volumen para obtener el número que debe almacenarse en el registro de control de audio apropiado para producir el sonido deseado. ¡Eso es todo al respecto!


Sin embargo, la generación de sonido en lenguaje Ensamblador sufre el mismo problema que en BASIC: no se puede especificar la duración del sonido producido. Ya sea el comando SOUND en BASIC, o nuestra rutina equivalente en lenguaje Ensamblador, estos simplemente inician el sonido. Deberemos apagar el sonido una vez transcurrido un tiempo predeterminado, para crear la nota o efecto de sonido que queramos. Para apagar el sonido, simplemente almacene un cero en el registro de control apropiado, AUDC1-4. Para medir el tiempo transcurrido, use los temporizadores ya discutidos, en las ubicaciones del 18 al 20 decimal, o use una rutina como la que usamos en el ejemplo del Player/Missile para retrasos cortos:


LDX #50
LDY #0
LOOP DEY
BNE LOOP
DEX
BNE LOOP	

El retardo de tiempo en este tipo de rutina está controlado por el valor inicial cargado en el registro X; cuanto mayor sea el número, mayor será el retraso. Estos ciclos anidados tardarían una eternidad en ejecutarse si programáramos la contraparte de esta rutina en BASIC, pero después del ensamblaje el retraso es muy corto. Pruébelo para ver qué tan rápido pueden ser 256 X 50, ó 12.800 ciclos.


TEMPORIZADORES DE CUENTA REGRESIVA


La tercera forma de realizar un seguimiento del tiempo en las computadoras ATARI implica el uso de temporizadores de cuenta regresiva. AUDF1, AUDF2 y AUDF4 pueden actuar como temporizadores de cuenta regresiva de la siguiente manera: Cuando se almacena cualquier valor distinto de cero en STIMER ($D209), los valores almacenados en AUDF1, AUDF2 y AUDF4 comienzan a disminuir. Cada uno de estos tres registros tiene una ubicación vectorial apropiada, tal como se describe a continuación:


TemporizadorUbicación del vector
AUDF1$210, $211 (VTIMR1)
AUDF2$212, $213 (VTIMR2)
AUDF4$214, $215 (VTIMR4)

Si colocamos la dirección de una rutina corta para apagar el sonido en una de estas ubicaciones vectoriales, se generará una interrupción cuando el temporizador apropiado haya llegado a cero y el control cambiará a su rutina para apagar el sonido. Sólo recuerde finalizar esta rutina con un RTI en lugar de un RTS. Antes de usar este tipo de temporizador, debe colocar el valor apropiado en el byte de habilitación de solicitud de interrupción, IRQEN ($D20E). Para habilitar VTIMR1, establezca el bit 0 de IRQEN; para habilitar VTIMR2, configure el bit 1 de IRQEN; y para habilitar VTIMR4, configure el bit 2 de IRQEN.


Ahora debe poder crear cualquier sonido disponible desde BASIC usando lenguaje Ensamblador, y aquí hay uno que no se puede producir desde BASIC debido a la velocidad requerida. Si el bit 4 de cualquiera de los registros de control de audio se establece en 1, se puede escuchar un breve "pop". Esto se debe a que se empuje el cono del altavoz hacia afuera una vez, comprimiendo el aire frente al altavoz, lo que se escucha como un "pop": si configuramos y reiniciamos este bit rápidamente, podemos producir un sonido simplemente comprimiendo el aire frente al altavoz. Pruebe esto:


LOOP LDA #16
STA AUDC1
(insertar retardo para frecuencia)
LDA #31
STA AUDC1
(insertar retardo para frecuencia)
JMP LOOP	

El altavoz de su televisor o monitor vibrará hacia adelante y hacia atrás, produciendo un sonido totalmente diferente al descrito anteriormente. En este ejemplo, simplemente movemos el altavoz directamente para producir sonido.



Índice de Contenido | Capítulo anterior | Siguiente apéndice