> 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 - 5 | translate 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 = Acumulador | X = Registro X | Y = Registro Y

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

> * = $2000 |  | ; SYS 8192.
> INICIO | 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.
> | 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.
> BUCLE | LDA #$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.
> BUCLE | STA ($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.
> BUCLE | STA $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.
> BUCLE | LDA $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.
> BUCLE | 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 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:

> 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.
> | 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.
> HEX | PHA | ; 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.
> PUT | CMP #$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.
> GUN | ADC #$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.
> ROTAR | ROR | ; 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.
> NOSUMA | DEX | ; 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: %1 0001001 ; Acarreo=0 (No suma)
> 4) ROR EN A: %00001011 ; ROR EN LA POSICIÓN $FC: %0 1000 100 ; 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: %101000 10 ; Acarreo=0 (No suma)
> 2) ROR EN A: %00001010 ; ROR EN LA POSICIÓN $FC: %0101000 1 ; 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.
> ROTAR | ROR | ; 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.
> INI | LDA #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.
> TRPTE | INC $50 | ; Compensamos con 1 el contador en byte alto.
> | LDX #$00 | ; Carga X con #$00, que es el valor inicial de un bucle.
> BUCLEX | LDA ($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.
> SEMOS | INC $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.
> CONT | INC $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.
> CONT2 | JMP BUCLEX | ; Sata a BUCLEX, y seguimos sumando.
> SAMOS | RTS | ; Retorna al BASIC.
> TECLA | LDA #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.
> VUVE | LDA $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.
> COPIA | LDA $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.
> BUCLE | JMP 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.
> INICIO | LDX #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.
> SIGUE | LDX #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.
> SONIDO2 | STA 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.
> SALTA | CMP #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.
> SALTA2 | LDA #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.
> SALIDA | LDA FINALPROG+2 | ; Carga A con el valor situado en FINALPROG+2.
> | RTS | ; Volvemos.
> RETARDO | LDA 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.
> RETARDO1 | DEC 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?
> CALCPOS | LDA #<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.
> INCREMENTA | TAY | ; 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.
> COMPARA | LDA $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.
> COMPARA2 | LDA $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).
> SALE | TYA | ; Transfiere el contenido de Y al acumulador .
> | RTS | ; Volvemos.
> SALEFIN | LDA #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.