Autor Tema: Assembler - Cosas que voy aprediendo  (Leído 39423 veces)

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Assembler - Cosas que voy aprediendo
« en: Julio 07, 2015, 23:14:51 »
¡Hola programadores!

Esto no pretende ser un curso de assembler, ni nada parecido.
Solo quiero compartir con Uds. las cosas que voy aprendiendo mientras desarrollo un jueguito. Nada de lo que les voy a contar es nuevo... todo respecto a la c64 ya esta descubierto y bien detallado en codebase64 y otros sitios. De hecho, es de ahí de donde yo aprendo casi todo de la c64.

¿Entonces por qué hago esto si ya esta todo descubierto? Bueno, porque no todo lo que esta en codebase64 se entiende facilmente. Creo que varios artículos presuponen que uno sabe ciertas cosas, y cuesta bastante seguirlos. Y además porque tengo ganas de hacerlo :)

Yendo al grano, mi primer aprendizaje es...

Cómo hacer un IRQ estable

Si alguna vez trataron de hacer un "raster bar", o un simple scroll fino usando IRQ, habrán notado que la primer linea del raster hace "flicker"... es como que se corre a veces un poco hacia la derecha, o la izquierda y no se sabe bien porque.

Lo que aprendí estos días, es saber porqué pasa eso, y como prevenirlo y de esa manera lograr efectos que se vean bien.

Así es como se ve la intro del jueguito que estoy haciendo:


Basicamente tiene 4 IRQs (Código https://github.com/ricardoquesada/c64-the-race/blob/ec5f2f6b6ec8e1b4c4ad32ca95834af1398aead2/src/intro.s#L108 )

irq1: Pone una barrita violeta. Pone color gris de fondo. Comienza el scroll fino de arriba.
irq2: Pone una barrita violeta. Cambia los colores a negro. Termina el scroll fino.
irq3: Comienza el scroll fino de abajo y pone color gris de fondo.
irq4: Termina con el scroll fino y pone los colores a negro nuevamente.

Parece sencillo, pero para poner las barritas violetas me costó una eternidad.

El tema es que cuando uno esta dentro de una IRQ, el "raster beam" (o haz de luz... no se como se traduce) se sigue moviendo, entonces hay que cambiar los colores en los momentos justos.



( Para saber bien qué es el raster y demás recomiendo leer este articulo: http://dustlayer.com/vic-ii/2013/4/25/vic-ii-for-beginners-beyond-the-screen-rasters-cycle )

Para simplificar las cosas, vamos a asumir que tenemos una c64 PAL (no NTSC).
Del gráfico se puede ver que el raster para hacer una linea entera tarda 63 ciclos de CPU. Y lo más interesante de todo que hay momentos en los que el raster no es visible.
Y es justamente en esos momentos cuando uno tiene que cambiar el color del fondo de pantalla... porque sino el color aparece cortado.

Ejemplo: Aca la barra violeta aparece "cortada"...y es que cambié el color mientras el "raster beam" estaba por la mitad.


Entonces la teoría es sencilla: cambiar el color cuando el "raster beam" esta invisible.

Ciclos, interrupciones y demás:

Cuando uno instala una interrupción raster, uno no puede saber con exactitud cuantos ciclos fueron ejecutados por la siguiente razón:
Supongamos que tengo mi IRQ instalado, y en estoy ejecutando en mi ciclo principal un código que hace algo.
Justo en ese momento se prende mi IRQ ("raster beam" esta en la posición deseada).
El tema es que como la CPU puede estar ejecutando una instrucción, hay que dejar que se termine de ejecutar esa instrucción para que se llama a mi IRQ. Y eso consume ciclos.
Y como hay instrucciones de hasta 7 ciclos, cuando se llama a mi IRQ yo no se cuantos ciclos entre 0 y 7 se ejecutaron de más.

Y es exactamente por este motivo que a veces los raster bars o efectos hacen "flicker"... ya que a veces estan corridos 0, 1, ... y hasta 7 ciclos hacia la "derecha".

Lo que aprendí es que hay varias técnicas para hacer el raster estable: http://codebase64.org/doku.php?id=base:making_stable_raster_routines
Pero la que yo estoy usando en la intro es la "doble IRQ": Basicamente uso una IRQ para lograr que la CPU esta ejecutando un NOP cuando se produzca la siguiente IRQ. Eso se logra poniendo varios NOPs dentro de la primera IRQ, y luego nunca volver (no usar "rti"). De esa manera me garantizo que solo va a haber entre 0 y 1 ciclos de diferencia (y no hasta 7 como antes)... y luego ese ciclo de "sobra" se arregla con una comparación.
Los detalles están aca: http://codebase64.org/doku.php?id=base:stable_raster_routine Si alguien no entiende algo de la técnica "Doble IRQ" con gusto lo explico.

Y eso... basicamente para poner esas 2 barras violetas, tuve que aprender a usar la "doble IRQ" y contar ciclos :)
« última modificación: Julio 19, 2015, 14:42:05 por josepzin »

Maniako

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 1008
  • SYS 8*4096
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #1 en: Julio 08, 2015, 00:24:03 »
En su momento jugué con el raster pero no a ese nivel.
Muchas gracias por colgar el código. Ahora toca estudiarlo. ;)

A ver si me animo a colgar algunas rutinas útiles para noveles como yo.
LDA #$50
STA $0400
RTS
Lloré cuando conseguí hacer esto con el monitor del FC1.

josepzin

  • Administrador
  • Commodore Master
  • *****
  • Mensajes: 13628
  • Commodoreador web
    • Ver Perfil
    • Mi blog
Re:Assembler - Cosas que voy aprediendo
« Respuesta #2 en: Julio 08, 2015, 01:07:02 »
Este hilo va a ser MUY interesante :)
www.retroinvaders.com | www.commodoreplus.org  | josepzin.blogspot.com

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #3 en: Julio 08, 2015, 04:21:01 »
Las famosas "bad lines"

¿Qué carajo son las "bad lines"?
Algo que molesta mucho, pero al parecer se pueden hacer trucos muy buenos abusando de estas. Pero vamos a lo que nos importa:

Antes dije que el raster beam tardaba 63 ciclos en recorrer una linea de pantalla horizontalmente. Y eso sigue siendo verdad... pero cada 8 lineas, el VIC se consume como 40 ciclos para leer los punteros a los caracteres de esa linea. O sea que no siempre tenemos 63 ciclos por "raster line".
El algoritmo es:
Código: [Seleccionar]
// pseudo code
// puede que tenga que compara con 7 o 5 en vez de con 0... no hice muchas pruebas aún.
if (($d012 & scroll_fino_vertical) ==0)
   tengo_23_ciclos();
else
   tengo_63_ciclos();

¿y por qué molestan las Bad Lines? Basicamente si yo quiero cambiar el color de fondo en cada "raster line", lo que tengo que hacer es:

1) cambiar color
2) consumir los ciclos necesarios hasta llegar a 63
3) comparar si es el último color
4) si no, volver a 1)

Pero eso no funciona con las bad lines, porque cada 8 lines, solo tengo alrededor de 20 ciclos. Esto es lo que pasa:



Como pueden ver, cada 8 lineas, se desfasa todo porque estoy consumiendo 63 ciclos en vez de ~20.
Intenté comparar si estaba en una bad-line, y consumir solo ~20 ciclos... pero no me daban los ciclos ya que cada instrucción consume ciclos, y hacer todo ese calculo era muy caro.

Así que probé una rutinita distinta. Algo asi como esto:
Código: [Seleccionar]
        ; los ciclos consumidos estan a la derecha
; raster bars
ldx #0
: lda $d012 ; +4
cmp $d012 ; +4
beq *-3 ; +2
; wait for new raster line, change color then
lda raster_colors,x ; +4
sta $d021 ; +4
inx
cmp #$ff
bne :- ; +3

Y casi funciona bien...


Pero si prestan atención cada 8 lineas se repite una linea (las malditas bad lines). Mucho mejor que el test anterior, pero molesto aún.

Todavía no se como solucionar este problema... lo que hice, es que en vez de modificar el color en cada linea, lo modifica cada 2, y así esta todo uniforme.

El código para dibujar cada 2 lineas es muy similar al anterior:
Código: [Seleccionar]
; raster bars
ldx #0
: lda $d012 ; +4
clc ; +2
adc #$02 ; +2
cmp $d012 ; +4
bne *-3 ; +2
; wait for new raster line, change color then
lda raster_colors,x ; +4
sta $d021 ; +4
inx ; +2
cmp #$ff ; +2
bne :- ; +3

El resultado final en la intro es algo así:


La última versión del PRG siempre la van a poder bajar de aca: https://www.dropbox.com/s/x2grp14do6w4jyb/therace.prg?dl=0

Y el código fuente siempre va a estar aca: https://github.com/ricardoquesada/c64-the-race

Muy buena información sobre las "bad lines" se puede encontrar aca: http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt
« última modificación: Julio 08, 2015, 04:53:04 por riq »

josepzin

  • Administrador
  • Commodore Master
  • *****
  • Mensajes: 13628
  • Commodoreador web
    • Ver Perfil
    • Mi blog
Re:Assembler - Cosas que voy aprediendo
« Respuesta #4 en: Julio 08, 2015, 04:52:21 »
No tenía ni pero ni idea de esto...
www.retroinvaders.com | www.commodoreplus.org  | josepzin.blogspot.com

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #5 en: Julio 08, 2015, 07:00:53 »
Desenrollando bucles
Ja ja... la solución era mucho más fácil de lo que creía :-)

El algoritmo que había presentado era el correcto, pero había que desenrollar el bucle. ¿Qué significa eso?
Código: [Seleccionar]
// pseudo código. Código enrollado:
for (int i=0;i<4;i++)
   inc $d020
Código: [Seleccionar]
; Mismo algoritmo pero con el bucle desenrollado
; me ahorro las comparaciones. Ocupa más espacio, pero es más rápido.
inc $d020
inc $d020
inc $d020
inc $d020

Eso hace que uno se ahorra las comparaciones (recordemos que solo tengo ~20 ciclos), y si bien el código es más feo y ocupa más bytes, es más rápido.

Al final el código me quedó así. Son 3 "repeats" anidados:
Código: [Seleccionar]
; 9 lines per char
.repeat 9
; 7 "buenas" lineas: Tengo que consumir 63 ciclos
.repeat 7
inx ; +2
lda raster_colors,x ; +4
sta $d021 ; +4
.repeat 25
nop ; +2 * 30
.endrepeat
bit $00 ; +3 = 63 ciclos
.endrepeat
; 1 "linea mala": Tengo que consumir 20 ciclos
inx ; +2
lda raster_colors,x ; +4
sta $d021 ; +4
.repeat 5
nop ; +2 * 5 = 20 ciclos
.endrepeat
.endrepeat

Gracias al poder de las macros, el código no es tan horrible y el resultado visual el que quería: Un color por linea sin que haya lineas duplicadas y sin "desfasajes":


Creo recordar que en este video hablaban que había que desenrollar todo para tener velocidad en la c64: https://www.youtube.com/watch?v=po9IY5Kf0Mo

Aclaración: Hacer "raster bars" en el borde ($d020) es más fácil que en el fondo ($d021) ya que no se nota el desfasaje por las "bad lines"... el desfasaje esta, pero no se nota.
« última modificación: Julio 08, 2015, 18:04:51 por riq »

Maniako

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 1008
  • SYS 8*4096
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #6 en: Julio 08, 2015, 08:50:39 »
Me mata no saber inglés  :-[

Pero lo que explicas es "semicomprensible" para mi. Esta claro que tengo que practicar con ello para poder entenderlo del todo.
 
LDA #$50
STA $0400
RTS
Lloré cuando conseguí hacer esto con el monitor del FC1.

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #7 en: Julio 08, 2015, 08:58:45 »
@maniako: cualquier duda que tengas, preguntá nomás.

pastbytes

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 556
  • SYS 0
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #8 en: Julio 08, 2015, 11:13:44 »
Supongo que eso se podria traducir como "desenrrollando bucles", y deduzco que es una manera de ver al bucle como un rollo de papel que tiene tantas capas como iteraciones tiene el bucle, al desenrrollarlo queda un codigo mas largo con lo mismo repetido varias veces.
Y si, cuando se necesita velocidad o tiempos muy criticos o predecibles, se suele escribir el codigo repetido varias veces aunque los cambios entre una y otra repeticion sean minimos, por lo general solo parametros distintos.

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #9 en: Julio 08, 2015, 18:07:44 »
@pastbytes:
gracias. corregido.
Disculpen si a veces uso algunas palabras en ingles, pero es que muchas veces no se cual es la palabra en español. No se bien porque, pero por alguna razón en Argentina se usan muchas palabras del ingles en el mundo de la programación, sumado que al comienzo (digamos hace 20 años) los libros de programación que se conseguian en Argentina eran todos en ingles.
« última modificación: Julio 08, 2015, 20:01:58 por riq »

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #10 en: Julio 13, 2015, 08:48:29 »
Cargando un archivo Koala

Se pueden usar varios modos gráficos en la Commodore 64: http://www.studiostyle.sk/dmagic/gallery/gfxmodes.htm
pero yo solo se usar los oficiales: o sea el "bitmap alta resolución" y el "bitmap multicolor".

Y parece que en la C64 había un programa de dibujo llamado Koala Paint que generaba un formato bastante sencillo para los archivos que generaba.

Basicamente primero guardaba la información del bitmap, luego la definición de los colores %01, %10, luego del color %11, y luego el color del fondo (%00).

- bitmap (160x200/4) = 8000 bytes. Son 2 bits por color. Y se define por "celdas" de 8x8.
- color %01 y %10: (40x25) = 1000 bytes
- color %11: (40x25) = 1000 bytes
- color %00: 1 byte.

Más información sobre este modo aca: http://dustlayer.com/vic-ii/2013/4/26/vic-ii-for-beginners-screen-modes-cheaper-by-the-dozen

La rutinita para poner los colores en su lugar es esta, bastante sencilla:

Código: [Seleccionar]
;--------------------------------------------------------------------------
; init_koala_colors(void)
;--------------------------------------------------------------------------
; Args: -
; puts the koala colors in the correct address
; Assumes that bimap data was loaded in the correct position
;--------------------------------------------------------------------------
.proc init_koala_colors
KOALA_BITMAP_DATA = __GFX_LOAD__
KOALA_CHARMEM_DATA = KOALA_BITMAP_DATA + $1f40
KOALA_COLORMEM_DATA = KOALA_BITMAP_DATA + $2328
KOALA_BACKGROUND_DATA = KOALA_BITMAP_DATA + $2710

; Koala format
; bitmap:           $0000 - $1f3f = $1f40 ( 8000) bytes
; color %01 - %10:  $1f40 - $2327 = $03e8 ( 1000) bytes
; color %11:        $2328 - $270f = $03e8 ( 1000) bytes
; color %00:        $2710         =     1 (    1) byte
; total:                    $2710 (10001) bytes

ldx #$00
@loop:
; $0400: colors %01, %10
lda KOALA_CHARMEM_DATA,x
sta $0400,x
lda KOALA_CHARMEM_DATA+$0100,x
sta $0400+$0100,x
lda KOALA_CHARMEM_DATA+$0200,x
sta $0400+$0200,x
lda KOALA_CHARMEM_DATA+$02e8,x
sta $0400+$02e8,x

; $d800: color %11
lda KOALA_COLORMEM_DATA,x
sta $d800,x
lda KOALA_COLORMEM_DATA+$0100,x
sta $d800+$100,x
lda KOALA_COLORMEM_DATA+$0200,x
sta $d800+$200,x
lda KOALA_COLORMEM_DATA+$02e8,x
sta $d800+$02e8,x

inx
bne @loop

lda KOALA_BACKGROUND_DATA
sta $d021

rts
.endproc

El programa que estoy usando para generar imagenes Koalas es Timanthes: http://csdb.dk/release/?id=75871 Esta bueno, pero no es trivial como generar koalas validos.

En algún momento me gustaría aprender a generar y cargar imágenes avanzadas como la FLI y demás... pero sera en el futuro.

Mientras tanto estoy contento con el modo "160x200x16".


Así va quedando la intro del jueguito.
« última modificación: Julio 14, 2015, 05:03:33 por riq »

R. INTERNATIONAL

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 667
  • THE NEW IRON AGE HAS COME!!
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #11 en: Julio 17, 2015, 13:34:37 »
  El tema del Raster es algo que desconozco y que tengo que aprender a medio plazo si o si,..por desgracia ahora estoy incomunicado por un virus y muchos de los enlaces del hilo no puedo verlos,..pero el hilo se le ve interesantisimo..

  Cuando tenga solucionado el problema prometo estudiarme el hilo porque aparte necesito por peles dominar ésta tecnica.

   Chao-

Maniako

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 1008
  • SYS 8*4096
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #12 en: Julio 17, 2015, 21:54:13 »
Llevo ya dos semanas peleandome con este tema y esto es a lo que he llegado gracias a un libro en pdf.

Código: [Seleccionar]
;Escrito con CBM prg studio

; 10 SYS (4096)

*=$801

        BYTE    $0E, $08, $0A, $00, $9E, $20, $28,  $34, $30, $39, $36, $29, $00, $00, $00
*=$1000

        sei        ;Stop IRQ's
        lda #27
        sta $D011 ;53265 Registro de contrl del VIC-II 00011011
        lda<inte  ;Guardando lobyte direccion del programa
        sta$0314  ;Desviando al programa hibyte
        lda>inte  ;Guardando hibyte direccion del programa
        sta$0315  ;Desviando al prorama lobyte     
        lda #127
        sta $DC0D ;56333 Registro control CIA 01111111
        lda#129
        sta $D01A ;53274 Registro mascara IRQ 10000001
        cli       ;Start IRQ's
        rts             ;Vuelta al basic
       
inte    LDA #$01
        STA $D019       ;Bandera de registro de IRQ. Activa bit 0 para comparacion de barrido
        LDX #$92        ;Altura del barrido para poner el negro
        LDY #$02 ;06    ;Color primera mitad pantalla   
        LDA $D012       ;Valor de barrido para comparar en IRQ
        BPL aa             ;Si es positivo...(<127)
        LDX #$32;01 32 justo empieza parte sup. pantalla    ;Altura de barrido para la primera pantalla
        LDY #$00        ;Color segunda mitad pantalla.
aa    STX $D012       ;Nuevo valor de barrido para comparar en IRQ.
        STY $D021       ;Color de fondo 0 o 2 segun sea.
        LDA $DC0D       ;Registro de control de la CIA 
        AND #$01        ;Bit interrupcion timer A.
        BEQ bb          ;Si no esta activado pasando
        JMP $EA31       ;Rutina ROM interrupt.
bb    JMP $FEBC       ;Continua con lo suyo.


Es lo mas sencillo que he llegado a comprender al 70%. Lo que hace Riq es de otro universo paralelo para mi  ;)
LDA #$50
STA $0400
RTS
Lloré cuando conseguí hacer esto con el monitor del FC1.

R. INTERNATIONAL

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 667
  • THE NEW IRON AGE HAS COME!!
    • Ver Perfil
Re:Assembler - Cosas que voy aprediendo
« Respuesta #13 en: Julio 17, 2015, 22:27:22 »
  Pues fijate para un Carnicero,...en cualquier caso lo importante es EL HIERRO!!! y luchar y luchar y aunque perezcamos en la batalla moriremos combatiendo!!,...ja,j,aj,aj,ja

  Tengo que probar todas estas rutinas,..buff se me acumula el trabajo, como siempre!!

   saludos.

riq

  • Desarrolladores
  • Commodore Master
  • ******
  • Mensajes: 567
    • Ver Perfil
    • From the C64 until today, a developer's perspective
Re:Assembler - Cosas que voy aprediendo
« Respuesta #14 en: Julio 17, 2015, 22:28:44 »
@maniako: Va bien eso. Una pequeña optimización que podes hacer, es en vez de esperar que el raster llegue a cierta linea, le podes decir que el IRQ se llame cuando el raster este en una linea determinada:

Código: [Seleccionar]

; irq raster que se llame en la linea $80
lda #$80
sta $d012

; setear la irq
lda #<inter
sta $0314
lda #>inter
sta $0315

una cosa que me llamó la atención es que estas haciendo:

Código: [Seleccionar]
;
lda #127
sta $dc0d

... probablemente sea correcto y este perfecto. Pero yo no lo había visto antes dentro la rutina de IRQ. Pero ojo, yo tampoco tengo mucha experiencia en esto asi que como dije, probablemente este perfecto llamarla.