Página 1

Página 1 - Página 2 - Página 3 - Página 4 - Página 5 - Página 6

 1a - 1b - 1c - 2a - 2b - 2c -2d -2e - 2f - 3a - 3b - 3c - 4a - 4b - 5translate this page  

Partiendo del conocimiento de los Mnemónicos de las instrucciones del 6510 y de algunas de las direcciones de memoria del Commodore 64 vamos realizar una serie de prácticas que nos ayudarán a aprender a programar en este entorno, utilizando como herramienta el ensamblador Turbo Assembler.

Página Cero

Basic ROM Kernal ROM
VIC II SID CIA
A = AcumuladorX = Registro XY = Registro Y

Nº 1a.- Alternar blanco y negro en el fondo y el borde
Por Wanja Gayk en GO64! Magazine

* = $2000 ; SYS 8192.
INICIOLDA #$00; Carga A con #$00, que es el código del color negro.
 STA $D020; Guarda el valor #$00 en la dirección $D020, que corresponde al borde de la pantalla.
 STA $D021; Guarda el valor #$00 en la dirección $D021, que corresponde al fondo de la pantalla.
 LDA #$01; Carga A con #$01, que es el código del color blanco.
 STA $D020; Guarda el valor #$01 en la dirección $D020.
 STA $D021; Guarda el valor #$01 en la dirección $D021.
 JMP INICIO; Salta a INICIO produciendo un bucle infinito.

Para entender más: Código de colores
Para aprender más: Cambia los colores. Añade más líneas con otros colores. Sólo cambia el color del borde. Sólo cambia el color del fondo.

Otra posible aplicación consiste en:

Nº 1b.- Arco iris
Por Richard_tnd en http://www.redizajn.sk/tnd64/assemble_it.html

* = $1000 ; SYS 4096.
 SEI; Prohíbe las interrupciones IRQ, necesario para evitar que la CPU se centre en "otras cosas".
BUCLE INC $D020; Incrementa el valor de la dirección $D020, que corresponde al borde de la pantalla. Este incremento, junto con un bucle, produce un efecto de colores.
 LDA $DC01; Carga A con el valor de la dirección $DC01 que corresponde al adaptador de interface CIA 1, encargado de leer el teclado y el joystick en el puerto 1.
 CMP #$EF; Compara el valor de A con #$EF, que es el código de la tecla ESPACIO o del botón FIRE del joystick en el puerto 1.
 BNE BUCLE; Salta a BUCLE hasta que se pulse ESPACIO o FIRE.
 RTS ; Entonces retorna al BASIC.

Para entender más: $DC00 - $DC01
Para aprender más: Incrementa el fondo. Incrementa el borde y el fondo. Haz lo mismo pulsando FIRE en el joystick en el puerto 2 (situado en $DC00 y comparándolo con #$6F).

En relación con este programa hemos descubierto una variante que consiste en:

Nº 1c.- Arco iris
Por fermhg, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
 SEI; Prohíbe las interrupciones IRQ, necesario para evitar que la CPU se centre en "otras cosas".
BUCLE INC $D020; Incrementa el valor de la dirección $D020, que corresponde al borde de la pantalla. Este incremento, junto con un bucle, produce un efecto de colores.
 LDA #$DF; Carga A con #$DF, que desactiva el bit 5.
 STA $DC00; Guarda el valor #$DF en la dirección $DC00, que corresponde al adaptador de interface CIA 1, que define un grupo de teclas, entre las cuales está la tecla @.
 LDA $DC01; Carga A con el valor de la dirección $DC01, que corresponde al adaptador de interface CIA 1, encargado de leer ese grupo de teclas.
 CMP #$BF; Compara el valor de A con #$BF, que es el código de la tecla @.
 BNE BUCLE; Salta a BUCLE hasta que se pulse la tecla @.
 RTS ; Entonces retorna al BASIC.

Para entender más: Keyboard Column Values
Para aprender más: Incrementa el fondo. Incrementa el borde y el fondo. Haz lo mismo pulsando otras teclas.


Nº 2a.- Llenado de la pantalla con un caracter
Por John P. Gibbons

* = $1000 ; SYS 4096.
 LDX #$00; Carga X con #$00, que es el valor inicial de un bucle.
BUCLELDA #$24; Carga A con #$24 , que corresponde al caracter “$”.
 STA $0400,X; Guarda el valor #$24 en la posición $0400 y siguientes #255 del mapa de memoria de pantalla.
 STA $0500,X; Guarda el valor #$24 en la posición $0500 y siguientes #255.
 STA $0600,X; Guarda el valor #$24 en la posición $0600 y siguientes #255.
 STA $0700,X; Guarda el valor #$24 en la posición $0700 y siguientes #255, con lo que se llenaría toda la pantalla.
 LDA #$0D; Carga A con #$0D, que corresponde al color verde claro.
 STA $D800,X; Guarda el valor #$0D en la posición $D800 y siguientes #255 del mapa de colores de pantalla.
 STA $D900,X; Guarda el valor #$0D en la posición $D900 y siguientes #255.
 STA $DA00,X; Guarda el valor #$0D en la posición $DA00 y siguientes #255
 STA $DB00,X; Guarda el valor #$0D en la posición $DB00 y siguientes #255.
 DEX; Decrementa el valor de X.
 BNE LP; Salta a BUCLE hasta que el valor de X=#$00.
 RTS; Entonces retorna al BASIC.

Para entender más: Código de caracteres en pantalla y código de colores
Para aprender más: Cambia los caracteres. Cambia los colores.

Una segunda aplicación del anterior programa consiste en:

Nº 2b.- Llenado de la Pantalla con un Caracter
En http://c64.nostalgia.pl/, fermhg, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
 LDA #$00; Carga A con #$00, que corresponde al byte bajo de la dirección $0400.
 STA $FB; Guarda el valor #$00 en la dirección $FB, que es el vector del byte bajo.
 LDA #$04; Carga A con #$04, que corresponde al byte alto de la dirección $0400.
 STA $FC; Guarda el valor #$04 en la dirección $FC, que es el vector del byte alto.
 LDA #"*"; Carga A con el código del caracter "*".
 LDY #$00; Carga Y con #$00, que es el valor inicial de un bucle.
BUCLESTA ($FB),Y; Guarda el código del carácter “*” en la posición $0400 y siguientes #1023.
 INY; Incrementa el valor de Y.
 BNE BUCLE; Salta a BUCLE hasta que el valor de Y=#$00.
 INC $FC; Entonces incrementa el valor del byte alto, pasando a las direcciones $0500 y siguientes.
 LDX $FC; Carga X con el valor de la dirección $FC.
 CPX #$08; Compara el valor de X con #$08, es decir, con la dirección $0800, que corresponde a la pantalla llena.
 BNE BUCLE; Salta a BUCLE hasta que el valor de X=#$08.
 RTS; Entonces retorna al BASIC.

Para entender más: Direccionamiento Indirecto Indexado
Para aprender más: Cambia los caracteres. Con el mismo sistema de direccionamiento, introduce el mapa de colores de pantalla dándole color al caracter introducido.

Una tercera aplicación del anterior programa consiste en:

Nº 2c.- Limpiar la Pantalla
Por Linus Åkerlund

* = $1000 ; SYS 4096.
 LDA #$00; Carga A con #$00, que es el código del color negro.
 STA $D020; Guarda el valor #$00 en la dirección $D020, que corresponde al borde de la pantalla.
 STA $D021; Guarda el valor #$00 en la dirección $D021, que corresponde al fondo de la pantalla.
 TAX; Transfiere el contenido de A al registro X.
 LDA #$20; Carga A con #$20, que corresponde al caracter ESPACIO.
BUCLESTA $0400,X; Guarda el valor #$20 en la posición $0400 y siguientes #255 del mapa de memoria de pantalla.
 STA $0500,X; Guarda el valor #$20 en la posición $0500 y siguientes #255.
 STA $0600,X; Guarda el valor #$20 en la posición $0600 y siguientes #255.
 STA $0700,X; Guarda el valor #$20 en la posición $0700 y siguientes #255.
 DEX; Decrementa el valor de X.
 BNE BUCLE; Salta a BUCLE hasta que el valor de X=#$00.
 RTS; Entonces retorna al BASIC.

Para entender más: Código de caracteres en pantalla y código de colores
Para aprender más: Cambia los caracteres. Cambia los colores.

Una cuarta aplicación de este tipo de programa consiste en:

Nº 2d.- RVS/ON en toda la pantalla
Por fermhg, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
 LDX #$00; Carga X con #$00, que es el valor inicial de un bucle.
BUCLELDA $0400,X; Carga A con el valor de la posición $0400 y siguientes #255 del mapa de memoria de pantalla.
 EOR #$80; Efectúa un OR-exclusive lógico entre A y el byte #$80, lo que genera el caracter en rvs/on.
 STA $0400,X; Guarda el código del caracter en rvs/on en misma posición en la que fue cargado.
 LDA $0500,X; Carga A con el valor de la posición $0500 y siguientes #255.
 EOR #$80; Efectúa un OR-exclusive lógico entre A y el byte #$80, lo que genera el caracter en rvs/on.
 STA $0500,X; Guarda el código del caracter en rvs/on en misma posición en la que fue cargado.
 LDA $0600,X; Carga A con el valor de la posición $0600 y siguientes #255.
 EOR #$80; Efectúa un OR-exclusive lógico entre A y el byte #$80, lo que genera el caracter en rvs/on.
 STA $0600,X; Guarda el código del caracter en rvs/on en misma posición en la que fue cargado.
 LDA $0700,X; Carga A con el valor de la posición $0700 y siguientes #255.
 EOR #$80; Efectúa un OR-exclusive lógico entre A y el byte #$80, lo que genera el caracter en rvs/on.
 STA $0700,X; Guarda el código del caracter en rvs/on en misma posición en la que fue cargado.
 INX; Incrementa el valor de X.
 BNE BUCLE; Salta a BUCLE hasta que X=#$00.
 RTS; Entonces retorna al BASIC.

Para entender más: Ejecutar un OR es como si sumásemos el valor del número inicial más #128.
Para aprender más: Escribir todos los caracteres y comprobar cuál es dicho caracter en modo rvs/on.

Una quinta aplicación del programa consiste en:

Nº 2e.- Limpiar la pantalla
Por Pontus Berg en comp.sys.cbm

* = $1000 ; SYS 4096.
 LDA #$00; Carga A con #$00, que es el código del color negro, que, como veremos, va a afectar tanto a los caracteres que hay en la pantalla como al fondo de la misma.
 STA $0286; Guarda el valor #$00 en la dirección $0286, que es la posición en de página cero extendida encargada de asignar los colores a los caracteres.
 STA $D021; Guarda el valor #$00 en la dirección $D021, que corresponde al fondo de la pantalla.
 JSR $E544; Salta a la subrutina $E544 en ROM, que realiza un CLear Screen en la pantalla.

Para entender más: Todo el proceso utilizado en el programa 2c podemos simplificarlo saltando a la subrutina $E544.
Para aprender más: Cambiar colores. Utilizar también el borde de la pantalla.

Una sexta aplicación del programa consiste en:

Nº 2f.- Limpiar la pantalla
Por Pontus Berg en comp.sys.cbm

* = $1000 ; SYS 4096.
 LDA #$0B; Carga A con #$0B, que en binario es %00001011..
 STA $D011; Guarda el valor #$0B en $D011, lo que desactiva el bit 4, haciendo que toda la pantalla tome el código del borde de la pantalla.
BUCLELDA #$00; Carga A con #$00, que es el código del color negro.
 STA $D020; Guarda el valor #$00 en la dirección $D020, que corresponde al borde de la pantalla siendo, en este caso, la pantalla completa.
 JMP BUCLE; Salta a BUCLE, produciendo un bucle sin fin. Esta línea puede ser sustituída por RTS.

Para entender más: $D011
Para aprender más: Cambiar colores. Utilizar también el borde de la pantalla. Realizar el siguiente cambio:

BUCLEINC $D020; Incrementa el valor de la dirección $D020, que corresponde al borde de la pantalla. Este incremento, junto con un bucle, produce un efecto de colores.
 JMP BUCLE; Salta a BUCLE, produciendo un bucle sin fin. Esta línea puede ser sustituída por RTS.

Nº 3a.- Convertidor decimal a hexadecimal
Por John P. Gibbons

* = $1000 ; SYS 4096.
 JSR $AEFD; Salta a la subrutina en ROM $AEFD, que comprueba la existencia de una coma después de SYS.
 JSR $AD8A; Si no está presente genera un mensaje de error (Syntax error) y retorna al BASIC.
 JSR $B7F7; Salta a la subrutina en ROM $B7F7, que convierte el número decimal a binario y lo guarda en forma de byte bajo / byte alto en las direcciones $14 (byte bajo) y $15 (byte alto).
 LDA $15; Carga A con el byte alto.
 JSR HEX; Salta a la subrutina HEX, que sacará el contenido de A a la pantalla como dos dígitos hexadecimales.
 LDA $14; Carga A con el byte bajo.
HEXPHA; Guarda en la pila el contenido de A.
 LSR A; Desplaza una vez el contenido de A hacia la derecha.
 LSR A; Desplaza una vez el contenido de A hacia la derecha.
 LSR A; Desplaza una vez el contenido de A hacia la derecha.
 LSR A; Desplaza una vez el contenido de A hacia la derecha. En total son 4 veces, con lo que obtenemos los cuatro bits de mayor peso.
 JSR PUT; Salta a la subrutina PUT que procesa estos cuatro bits.
 PLA; Restaura el valor original de A a partir de la pila.
 AND #$0F; AND entre A y el valor %00001111 ($0F) para obtener los cuatro bits de menor peso.
PUTCMP #$0A; Compara estos cuatro bits con el valor #10.
 BCC GUN; Si es menor que #10 salta a GUN.
 ADC #$06; Suma #$06 + acarreo (1) al acumulador, sumando en definitiva #$07. Esta suma deja C a cero.
GUNADC #$30; Suma #$30 + acarreo (c=0) al acumulador.
 JMP $FFD2; Retorna a través de la subrutina en ROM $FFD2, que saca a pantalla el caracter ASCII cuyo código está en A.

Para entender más: Vamos a hacer un ejemplo con el número 3521, que pasado a binario es el siguiente:0000-1101-1100-0001.
Como vemos, los primeros 4 bits son ceros, por lo tanto su valor en Hexadecimal será también 0.
El segundo grupo de cuatro bits es 1101, que llevado al byte alto quedaría así: 1101-0000.
El programa realiza 4 veces la función LSR atendiendo (0110-1000, 0011-0100, 0001-1010, 0000-1101). Así hemos obtenido el byte 1101. Su valor es 13, con lo que es mayor que #$0A, sumándole #$06 + acarreo, es decir #$07, y sumándole #$30 + acarreo (que para este caso es 0). Entonces #13 + #$07 + #$30 = #$44 (que corresponde a la letra D).
El tercer grupo de bits es 1100, tendría como valor #12, y se le sumaría #$06 + acarreo + #$30, es decir #$07 + #$30. Entonces #12 + #$07 + #$30 = #$43 (que corresponde a la letra C).
El cuarto grupo de bits es 0001, tendría como valor #1, y no se le sumaría #$06 + acarreo ya que es menor que 10, saltando a GUN donde se le sumaría #$30. Entonces #01 + #$30 = #$31 (que corresponde al número 1).
El valor en Hexadecimal de 3521 es DC1.

Para aprender más: Probar con distintos números ejecutando SYS 4096, [número].

Otro tipo de aplicación consiste en:

Nº 3b.- Multiplicación
Por arun en comp.sys.cbm, fermhg y _PaCo_, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
 LDA #30; Carga A con #30, valor del primer multiplicando, que en binario sería %00011110.
 STA $FB; Guarda el valor #30 en la dirección $FB.
 LDA #150; Carga A con #150, valor del segundo multiplicando, que en binario sería %10010110.
 STA $FC; Guarda el valor #150 en la dirección $FC.

; Ya sabemos que la multiplicación será 150 x 15 = 4500.

 LDA #0; Carga A con #0, que en binario sería %00000000.
 LDX #8; Carga X con el valor #8, que es el valor inicial de un bucle.
ROTARROR; Realiza una rotación de los bits hacia la derecha en A.
 ROR $FC; Realiza una rotación de los bits hacia la derecha en $FC.
 BCC NOSUMA; Salta a NOSUMA si la bandera de accarreo del byte de $FC está a 0.
 CLC; Pone la bandera de acarreo a 0 para poder sumar.
 ADC $FB; Suma el contenido de A más el contenido de $FB.
NOSUMADEX; Decrementa el valor de X.
 BPL ROTAR; Salta a ROTAR hasta que el valor de X<0.
 STA $FB; Si el resultado de la suma es negativo guarda el valor de A en $FB.
 RTS; Retorna al BASIC.

Para entender más: Gracias a _PaCo_ podemos dar una explicación de cuál es el funcionamiento del programa, aunque coincidimos en que es muy difícil de comprender. Suponiendo que los multiplicando son 30 y 150, el programa guarda en $FB el valor %00011110 y en $FC el valor %10010110. Una vez guardados empieza a trabajar con A y la posición $FC de la siguiente manera:
ESTADO ORIGINAL DE A: %00000000
ESTADO ORIGINAL DE LA POSICIÓN $FC: %10010110
8) ROR EN A: %00000000 ; ROR EN LA POSICIÓN $FC: %01001011 ; Acarreo=0 (No suma). Vemos como el bit de acarreo dE A pasa a byte $FC.
7) ROR EN A: %00000000 ; ROR EN LA POSICIÓN $FC: %00100101 ; Acarreo=1 (Sí suma)
SUMA %00000000 + %00011110 = %00011110. Este valor será guardado en A.
6) ROR EN A: %00001111 ; ROR EN LA POSICIÓN $FC: %00010010 ; Acarreo=1 (Sí suma)
SUMA %00001111 + %00011110 = %00101101. Este valor será guardado en A.
5) ROR EN A: %00010110 ; ROR EN LA POSICIÓN $FC: %
10001001 ; Acarreo=0 (No suma)
4) ROR EN A: %00001011 ; ROR EN LA POSICIÓN $FC: %0
1000100 ; Acarreo=1 (Sí suma)
SUMA %00001011 + %00011110 = %00101001. Este valor será guardado en A.
3) ROR EN A: %00010100 ; ROR EN LA POSICIÓN $FC: %
10100010 ; Acarreo=0 (No suma)
2) ROR EN A: %00001010 ; ROR EN LA POSICIÓN $FC: %
01010001 ; Acarreo=0 (No suma)
1) ROR EN A: %00000101 ; ROR EN LA POSICIÓN $FC: %
00101000 ; Acarreo=1 (Sí suma)
SUMA %00000101 + %00011110 = %00100011. Este valor será guardado en A.
0) ROR EN A: %00010001 ; ROR EN LA POSICIÓN $FC: %
10010100 ; Acarreo=0 (No suma, y como el valor de X=0 ya termina el programa).
El siguiente proceso será guardar el valor de A en
$FB = %00010001 (#17), que sería el byte alto del resultado y el valor de $FC = %10010100 (#148).
Lo que nos queda es imprimir el resultado maunalmente desde el BASIC, de la siguiente manera:
PRINT 256 * PEEK (251) + PEEK (252)
Para aprender más: Probar con distintos multiplicandos para extraer otros productos (recordar que para visualizar el resultado de la multiplicación debemos teclear PRINT 256 * PEEK (251) + PEEK (252).

Otro tipo de aplicación consiste en:

Nº 3c.- Calcular el entero de (A+B) / 4
Por Marko Mäkelä en comp.sys.cbm, fermhg, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
 LDA #45; Carga A con #45, valor del primer sumando, que en binario es %00101101.
 CLC; Pone la bandera de acarreo a 0 para poder sumar.
 ADC #18; Suma el valor #45 con #18, que en binario sería %00010010.

; Ya sabemos que el cálculo será: INT (45 + 18) / 4 = 15.

 PHP; Guarda en la pila el contenido del registro de estado.
 ROR; Realiza una rotación de los bits hacia la derecha en A.
 PLP; Extrae un byte de la pila y lo deja en el registro de estado.
ROTARROR; Realiza una rotación de los bits hacia la derecha en A.
 STA $FB; Guarda el valor de A en $FB.
 RTS; Retorna al BASIC.

Para entender más: Cara ROR es como dividir entre 2.
Para aprender más: Probar con distintos sumandos para extraer otros resultados (recordar que para visualizar el resultado de la operación debemos teclear PRINT PEEK (251). Probar a dividir entre 2, entre 8, etc.

El programa necesita ejecutar un SYS 4096. Una vez ejecutado hay que utilizar la instrucción PRINT PEEK (251)


Nº 4a.- Visualizar un fichero creado con el Art Studio
Por _PaCo_, canal #c64 del irc-hispano

Todos sabemos la facilidad para crear mapa de bits a todo color con este genial programa...pero ¿cómo hacer para ver el fichero desde el Basic del Commodore o desde C.M.? Carga el fichero MPIC desde tu disquetera o tu datasette, utilizando el parámetro (,1), es decir, que conserve su ubicación original en la memoria.
Ejemplo:
LOAD "nombre mpic",8,1 ó LOAD "nombre mpic",1,1. Una vez cargado, aplica el comando NEW y teclea este simple programa:

*= $1000 ; SYS 4096.
INILDA #59; Carga A con #59, que en binario es %00111011, lo que activa los bits 0,1,3,4 y 5.
 STA $D011; Guarda el valor #$59 en la dirección $D011, lo que inicia el modo grafico de 8kb en alta resolución.
 LDA #216; Carga A con #216, que en binario es %11011000, lo que activa los bits 3,4,6 y 7.
 STA $D016; Guarda el valor #216 en la dirección $D016, lo que inicia el modo multicolor.
 LDA #24; Carga A con #24, que en binario es %00011000, lo que activa los bits 3 y 4.
 STA $D018; Guarda el valor #216 en la dirección $D016, lo que selecciona el segundo banco de memoria ($2000-$4000).
 LDA #151; Carga A con #151, que en binario es %10010111, lo que activa los bits 0,1,2,4 y 7.
 STA $DD00; Guarda el valor #151 en la dirección $DD00, lo que selecciona el segmento (página) de $0000-$4000.
 LDA $4328; Carga A con el valor de la dirección $4328.
 STA $D020; Guarda el valor de A en la dirección $D020, que corresponde al borde de la pantalla.
 LDA $4329; Carga A con el valor de la dirección $4329.
 STA $D021; Guarda el valor de A en la dirección $D021, que corresponde al fondo de la pantalla.

; Los valores del color primario se almacenan entre las posiciones $3f40 y $4380.

 LDA #$40; Carga A con el byte bajo de $3F40.
 STA $4B; Guarda el valor de A en la dirección $4B, que es el vector del byte bajo (de dónde leer).
 LDA #$3F; Carga A con el byte alto de $3F40.
 STA $4C; Guarda el valor de A en la dirección $4C, que es el vector del byte alto (de dónde leer).

; La memoria de pantalla se encuentra entre $0400 y $07E8.

 LDA #$00; Carga A con el byte bajo de $0400.
 STA $4D; Guarda el valor de A en la dirección $4D, que es el vector del byte bajo (destino de datos).
 LDA #$04; Carga A con el byte alto de $0400.
 STA $4E; Guarda el valor de A en la dirección $4E, que es el vector del byte alto (destino de datos).

; Queremos mover 1000 (25*40) caracteres, que en hexadeciamal es $03E8.

 LDA #$E8; Carga A con el byte bajo de $03E8.
 STA $4F; Guarda el valor de A en la dirección $4F, que es el vector del byte bajo (usado como contador).
 LDA #$03; Carga A con el byte alto de $03E8.
 STA $50; Guarda el valor de A en la dirección $50, que es el vector del byte alto (usado como contador).
 JSR TRPTE; Salta a la subrutina TRPTE que hará el transporte de datos.

; Ahora vamos a transportar el código de color secundario que está almacenado entre $4338 y $4720.

 LDA #$38; Carga A con el byte bajo de $4338.
 STA $4B; Guarda el valor de A en la dirección $4B, que es el vector del byte bajo (de dónde leer).
 LDA #$43; Carga A con el byte alto de $4338.
 STA $4C; Guarda el valor de A en la dirección $4C, que es el vector del byte alto (de dónde leer).

; Ponemos el código de color secundario en el VIC-II $D800-$DBE8.

 LDA #$00; Carga A con el byte bajo de $D800.
 STA $4D; Guarda el valor de A en la dirección $4D, que es el vector del byte bajo (destino de datos).
 LDA #$D8; Carga A con el byte alto de $D800.
 STA $4E; Guarda el valor de A en la dirección $4E, que es el vector del byte alto (destino de datos).

; Movemos 1000 caracteres, como antes.

 LDA #$E8; Carga A con el byte bajo de $03E8.
 STA $4F; Guarda el valor de A en la dirección $4F, que es el vector del byte bajo (usado como contador).
 LDA #$03; Carga A con el byte alto de $03E8.
 STA $50; Guarda el valor de A en la dirección $50, que es el vector del byte alto (usado como contador).
 JSR TRPTE; Salta a la subrutina TRPTE que hará el transporte de datos.
 JSR TECLA; Salta a la subrutina TECLA que hará la lectura del teclado.
 RTS; Retorna al BASIC.
TRPTEINC $50; Compensamos con 1 el contador en byte alto.
 LDX #$00; Carga X con #$00, que es el valor inicial de un bucle.
BUCLEXLDA ($4B,X); Carga A con el  modo indirecto indexado con el valor de $4B y $4C, que es el inicio.
 STA ($4D,X); Guarda el valor de A en la dirección $4D y $4E, lo que almacena el valor de A en RAM, siendo el destino.
 DEC $4F; Decrementa el vector del byte bajo del contador.
 BNE SEMOS; Salta a SEMOS hasta que el valor de X=#$00.
 DEC $50; Si es 0, decrementa el vector del byte alto del contador.
 BEQ SAMOS; Si es 0, salta a SAMOS.
SEMOSINC $4D; Si no, seguimos incrementando el vector del byte bajo del destino.
 BNE CONT; Salta a CONT hasta que el valor de la dirección $4D sea igual a #$00.
 INC $4E; Si es 0 incrementa el vector del byte alto del destino.
CONTINC $4B; Incrementa el vector del byte bajo del inicio.
 BNE CONT2; Salta a CONT2 hasta que sea 0.
 INC $4C; .Si es 0, incrementa el vector del byte alto del inicio.
CONT2JMP BUCLEX; Sata a BUCLEX, y seguimos sumando.
SAMOSRTS; Retorna al BASIC.
TECLALDA #0; Carga A con #0.
 STA $C6; Guarda el valor de A en la posición $C6, lo que pone el valor de A en el buffer del teclado. Es una forma de limpiar el buffer antes de proceder a su lectura.
VUVELDA $C6; Carga A con el valor del buffer del teclado.
 BEQ VUVE; Si aún no se ha pulsado una tecla, volvemos a VUVE.
 LDA #27; Carga A con #27, que en binario es %00011011, lo que activa los bits 0,1,3 y 4.
 STA $D011; Guarda el valor #$27 en la dirección $D011, lo que inicia el modo texto.
 LDA #21; Carga A con #21, que en binario es %00010101, lo que activa los bits 0,2 y 4.
 STA $D018; Guarda el valor #21 en la dirección $D016, lo que selecciona el set de caracteres en Rom.
 LDA #200; Carga A con #200, que en binario es %11001000, lo que activa los bits 3,6 y 4.
 STA $D016; Guarda el valor #200 en la dirección $D016, lo que inicia el modo modo monocolor.
 LDA #147; Carga A con #147, que es como si pulsaramos (SHIFT+CLR+HOME), lo que borra la pantalla.
 JSR $F1CA; Salta a la subrutina en ROM $F1CA, que imprime el valor de A, con lo que se borraría la pantalla.
 RTS; Retorna al BASIC.

Para entender más: VIC II
Para aprender más: Puedes realizar tus propios dibujos con el Art Studio, guardarlos y cargarlos desde este programa en Código Máquina. Aquí tienes un bitmap de ejemplo que deberás cargar primero, seguido de este programa y hacer un sys 4096.

Una segunda aplicación del programa consiste en:

Nº 4b.- Visualizar un fichero creado con KOALA
Por Linus Åkerlund

*= $1000 ; SYS 4096.
 LDA #$00; Carga A con #$00, que es el código del color negro.
 STA $D020; Guarda el valor #$00 en la dirección $D020, que corresponde al borde de la pantalla.
 STA $D021; Guarda el valor #$00 en la dirección $D021, que corresponde al fondo de la pantalla.
 TAX; Transfiere el contenido de A a X. De esta manera carga X con #$00, que es el valor inicial de un bucle, ahorrando espacio de memoria.
COPIALDA $3F40,X; Carga A con el valor de la dirección $3F40 y siguientes.
 STA $0400,X; Guarda el valor de A en la posición $0400 y siguientes del mapa de memoria de la pantalla.
 LDA $4040,X; Carga A con el valor de la dirección $4040 y siguientes.
 STA $0500,X; Guarda el valor de A en la posición $0500 y siguientes.
 LDA $4140,X; Carga A con el valor de la dirección $4140 y siguientes.
 STA $0600,X; Guarda el valor de A en la posición $0600 y siguientes.
 LDA $4240,X; Carga A con el valor de la dirección $4240 y siguientes.
 STA $0700,X; Guarda el valor de A en la posición $0700 y siguientes.
 LDA $4328,X; Carga A con el valor de la dirección $4328 y siguientes.
 STA $D800,X; Guarda el valor de A en la posición $D800 y siguientes del mapa de colores de pantalla.
 LDA $4428,X; Carga A con el valor de la dirección $4428 y siguientes.
 STA $D900,X; Guarda el valor de A en la posición $D800 y siguientes.
 LDA $4528,X; Carga A con el valor de la dirección $4528 y siguientes.
 STA $DA00,X; Guarda el valor de A en la posición $D800 y siguientes.
 LDA $4628,X; Carga A con el valor de la dirección $4628 y siguientes.
 STA $DB00,X; Guarda el valor de A en la posición $D800 y siguientes.
 DEX; Decrementa el valor de X.
 BNE COPIA; Salta a COPIA hasta que el valor de X=#$00.
 LDA #$3B; Carga A con #$3B
 LDX #$18; Carga X con #$18
 LDY #$18; Carga Y con #$18, que determina que el código del bitmap realizado con el KOALA necesariamente debe ensamblarse a partir de la dirección de memoria $2000.
 STA $D011; Guarda el valor de A en la dirección $D011, que selecciona el modo bitmap.
 STX $D016; Guarda el valor de X en la dirección $D016, que selecciona el modo multicolor.
 STY $D018; Guarda el valor de Y en la dirección $D018, que determina que el código del bitmap realizado con el KOALA necesariamente debe ensamblarse a partir de la dirección de memoria $2000.
BUCLEJMP BUCLE; Salta a BUCLE, produciendo un bucle infinito.

Para entender más: VIC II. Las imágines KOALA normalmente están cargadas a partir de la posición $6000. Sin embargo este programa está configurado para cargar este tipo de imágenes a partir de la dirección $2000. Para ello debemos realizar un pequeño truco con el monitor del emulador VICE: l "<nombre del archivo>" <2000>
Para aprender más: Puedes descargar imágenes KOALA y reproducirlas con este programa.


Nº 5.- Texto y Sonido
Por _PaCo_, canal #c64 del irc-hispano

* = $1000 ; SYS 4096.
INICIOLDX #0; Carga X con #0.
 LDA #$0F; Carga A con #$0F, que en binario es %00001111 y en momdo decimal es 15, lo que activa los bits 0,1,2 y 3.
 STA $D418; Guarda el valor #$0F en la dirección $D418, lo que selecciona el volumen del sonido al máximo (15 es el volumen máximo).
 JSR CALCPOS; Salta a la subrutina CALCPOS que calcula el inicio en memoria y final del texto.
SIGUELDX #0; Carga X con #0.
 LDA ($FB,X); Carga el valor de la dirección $FB+$FC, que son vectores del lugar donde empieza el siguiente caracter.
 JSR $FFD2; Salta a la subrutina en ROM $FFD2 que saca a pantalla el caracter ASCII cuyo código está en A.
 JSR SONIDO2; Salta a la subrutina SONIDO2, que es la generadora del sonido.
 JSR INCREMENTA; Salta a la subrutina INCREMENTA, que incrementa el vector $FA+$FB al siguiente caracter.
 BNE SIGUE; Salta a SIGUE si el valor del caracter es distinto de 0.
 RTS; Retorna al BASIC.
SONIDO2STA FINALPROG+2; Guarda el valor de A al final del programa. Sería mejor usar una dirección de memoria de la página cero, lo que disminuiría en un byte la ejecución dando más velocidad.

; Primera comparación.

 CMP #$20; Compara A con el valor #$20, que corresponde al caracter ESPACIO.
 BNE SALTA; Salta a SALTA si el valor del caracter es distinto de #$20.
 LDA #$0F; Carga A con #$0F, que es el valor de retardo variable.
 STA CONTADOR; Guarda el valor #$0F en CONTADOR, lo que hará el retardo.
 JSR RETARDO; Salta a la subrutina RETARDO, que es un bucle de espera variable.
 JMP SALIDA; Salta a SALIDA, al detectar el espacio no genera sonido.

; Segunda comparación.

SALTACMP #13; Compara A con #13, que corresponde al caracter RETURN.
 BNE SALTA2; Salta a SALTA2 si el valor del caracter es distinto de #13.
 LDA #$20; Carga A con #$20, que es el valor de retardo variable.
 STA CONTADOR; Guarda el valor #$20 en CONTADOR, lo que hará el retardo.
 JSR RETARDO; Salta a la subrutina RETARDO, que es un bucle de espera variable.
 JMP SALIDA; Sata a SALIDA, al detectar el RETURN no genera sonido.

; No es ni ESPACIO ni RETURN entonces hay sonido.

SALTA2LDA #0; Carga A con #0.
 STA $D406; Guarda el valor #0 en la dirección $D406, que corresponde al SID generador de envolvente.
 LDA #3; Carga A con #3.
 STA $D405; Guarda el valor #3 en la dirección $D405, que corresponde al SID, ataque/decaimiento 4+4 bits.
 LDA #180; Carga A con #180.
 STA $D401; Guarda el valor #180 en la dirección $D401, que corresponde al SID, control de frecuencia byte alto.
 LDA #33; Carga A con #33.
 STA $D404; Guarda el valor #33 en la dirección $D404, que corresponde al SID, generador de onda, selecciona sierra y acciona el ataque-decaimiento-sostenimiento-relajación.
 LDA #$08; Carga A con #$08.
 STA CONTADOR; Guarda el valor #$08 en CONTADOR.
 JSR RETARDO; Salta a la subrutina RETARDO, que es un bucle de espera variable.
 LDA #0; Carga A con #0.
 STA $D404; Guarda el valor #0 en el SID, se acabó el sonido.
SALIDALDA FINALPROG+2; Carga A con el valor situado en FINALPROG+2.
 RTS; Volvemos.
RETARDOLDA CONTADOR; Carga A con el valor del retardo variable situado en CONTADOR.
 STA FINALPROG; Guarda dicho valor en FINALPROG.
 LDA #$00; Carga A con #$00.
 STA FINALPROG+1; Guarda el valor #$00 en la posición FINALPROG+1.
RETARDO1DEC FINALPROG+1; Decrementa el valor situaldo en la posición FINALPROG+1, lo que genera un bucle de 255 (FF).
 BNE RETARDO1; Salta a RETARDO1 si el valor del caracter es distinto de #$20.
 DEC FINALPROG; Decrementa el valor situaldo en FINALPROG, lo que genera un bucle de retardo variable.
 BNE RETARDO1; Salta a RETARDO1 si el valor del caracter es distinto de #$20.
 RTS; Volvemos.

; ¿Dónde empieza y acaba el texto?

CALCPOSLDA #<ETIQUETA; Carga A con el byte bajo de donde empieza el texto.
 STA $FB; Guarda el valor de A en la dirección $FB, vector del byte bajo.
 LDA #>ETIQUETA; Carga A con el byte alto de donde empieza el texto.
 STA $FC; Guarda el valor de A en la dirección $FC, vector del byte alto.
 LDA #<FINALPROG; Carga A con el byte bajo de donde acaba el texto.
 STA $FD; Guarda el valor de A en la dirección $FD, vector del byte bajo.
 LDA #>FINALPROG; Carga A con el byte alto donde acaba el texto.
 STA $FE; Guarda el valor de A en la dirección $FE, vector del byte alto.
 RTS; Volvemos.
INCREMENTATAY; Transfiere el contenido dA al registro Y.
 INC $FB; Incrementa el valor del vector del byte bajo (inicio).
 CMP $FD; el valor es comparado con el vector del byte bajo de final de texto.
 BEQ COMPARA; Si es igual, salta a COMPARA.
 LDA $FB; Carga A con el vector del byte bajo (inicio).
 BEQ COMPARA2; Si es igual, salta a COMPARA2.
 JMP SALE; Sata a SALE, y seguimos sumando.
COMPARALDA $FC; Carga A con el vector del byte alto (inicio).
 CMP $FE; el valor es comparado con el apuntador alto de final de texto.
 BEQ SALEFIN; Si es igual, salta a SALEFIN.
 INC $FB; Incrementa el vector byte bajo (inicio).
 JMP SALE; Sata a SALE, y seguimos sumando.
COMPARA2LDA $FC; Carga A con el vector del byte alto (inicio).
 CMP $FE; el valor es comparado con el vector del byte alto de final de texto.
 BEQ SALEFIN; Si es igual, salta a SALEFIN.
 INC $FC; Incrementa el vector byte alto (inicio).
SALETYA; Transfiere el contenido de Y al acumulador .
 RTS; Volvemos.
SALEFINLDA #0; Carga A con #0.
 RTS; Retorna al BASIC.
ETIQUETA.TEXT "ESTO ES UNA PRUEBA DE"; Texto que se imprimirá en pantalla.
 .TEXT " TEXTO Y BEEP." 
 .BYTE 13; El valor #13 corresponde a la tecla RETURN.
 .TEXT "YA ADMITE 65535" 
 .TEXT "  CARACTERES." 
 .BYTE 13,13 
 .TEXT " NO PODRAS METER" 
 .TEXT " TANTO EN LA RAM " 
 .BYTE 13,13 
 .TEXT " PERO LA RUTINA " 
 .TEXT " ADMITE TODA LA MEMORIA " 
 .TEXT " .....   " 
 .BYTE 13,13 
 .TEXT " YA SABES... PON EL TEXTO " 
 .TEXT "  QUE QUIERAS Y RECOMPILA" 
 .BYTE 13 
 .TEXT " EL PROGRAMA. " 
 .BYTE 13,13 
 .TEXT "..Y LO QUE HAGA FALTA!" 
 .BYTE 13,13,13 
 .TEXT "OK!" 
 .BYTE 0; Indica el final del texto. Cuando encuentra un 0 hace un RTS.
FINALPROG.BYTE 0,0,0; Guarda 3 bytes para variables.
CONTADOR.BYTE 0; Aquí se almacena el contador.

Para entender más: SID
Para aprender más: Para imprimir otro texto deberemos cambiar los comandos .TEXT. Otra cosa, si queremos cambiar el tiempo de retardo, y así conseguir que la escritura sea más lenta o rápida, hay que cambiar el valor del retardo. También podemos realizar otro sonido cambiando los parámetros del SID.


Página 2