Funciones desde assembler

La entrada de hoy sera teórica, y surge de la necesidad de mejorar el código. Ya estamos entrando en las fases finales del juego, acabo de terminar la pantalla de presentación (que en próximas entregas mostraré), así como también definí la duración de la partida a 10 muertes y ademas agregué inmunidad durante la entrada de los jugadores.
Al querer armar el menú principal me encontré que tenía que reescribir/duplicar la rutina que copiaba la pantalla a la memoria de video (que mostraba el campo de juego), lo que me pareció una tremenda abominación.
En cualquier otro lenguaje de medio nivel lo habria resuelto con una función, lamentablemente aquí no disponemos de dichas herramientas… o si?

El modo de direccionamiento (Indirect),y

En las primeras entregas (6502 Basico – Registros) comenté muy por arriba los modos de direccionamiento del 6502, y claramente no explique los dos últimos debido a que en ese momento no comprendía su funcionamiento, ni tampoco encontraba su utilidad.
Pero “la necesidad tiene cara de hereje“, así que al momento de querer implementar algo parecido a una función me puse a investigar el modo (Indirect),y.  En este modo le pasamos una dirección de memoria de la Zero Page, y lo que hace es tomar esa posición como el byte menos significativo de un puntero de memoria, y la posición siguiente como el byte mas significativo, y finalmente le adiciona el valor de Y.

Ejemplo: vamos a suponer que en la posición de memoria $20 (Zero page) tenemos el valor $00, y en la posición $21 (la siguiente) tenemos el valor $04. Ademas, en el registro Y vamos a poner el valor $00.
Si introducimos el operando LDA ($20),Y el registro A va a tomar el valor la posición de memoria $0400 (el primer caracter de la pantalla), ya que $0400 se forma de con el valor contenido en las posiciones $20 y $21, y le adicionamos Y, que tiene el valor ‘0’.

Ajahm… y esto de que me sirve???

Bueno, bien no se, pero yo le encontré una utilidad para una función que copia una pantalla a la memoria de video. Basicamente le indicamos en 2 direcciones previamente definidas la dirección de origen de la pantalla, y la copiará a la memoria de video.

El código!


; punteros utilizados por copyScreen
;   CopyAddress 
;       lsbCopyAddress = $20
;       msbCopyAddress = $21           
; scrPtr    = $0400

copyToScreen
; posiciones de pantalla 0 - 255 
          ldy            #$00               ; Inicializo el indice
@loop
          lda            (lsbCopyAddress),y ; cargo en A CopyAddress + Y
          sta            scrPtr,y           ; y lo guardo en pantalla + y
          
          iny                               ; incremento indice
          cpy            #$00               
          bne            @loop              ; loopea si no completo 255 loops
          
; posiciones de pantalla 256 - 512
          ldy            #$00               ; inicializo Y en 0 (quiza innecesario...)
          inc            msbCopyAddress     ; incremento MSB de CopyAddress
                                            ; ya que voy a copiar los siguientes 255 bytes
                                            ; de la pantalla
@loop2
          lda            (lsbCopyAddress),y ; idem el loop anterior
          sta            scrPtr + $100,y    ; salvo que esta vez lo guardamos 
                                            ; en $0500 (el siguiente bloque de 255 bytes)
          iny
          cpy            #$00       
          bne            @loop2

; posiciones de pantalla 513 - 768 (idem loops anteriores)
          ldy            #$00
          inc            msbCopyAddress
@loop3
          lda            (lsbCopyAddress),y
          sta            scrPtr + $200,y   ; $0600
          
          iny
          cpy            #$00       
          bne            @loop3

; posiciones de pantalla 768 - 1000  (idem loops anteriores)
          ldy            #$00
          inc            msbCopyAddress
@loop4
          lda            (lsbCopyAddress),y
          sta            scrPtr + $300,y   ; $0700
          
          iny
          cpy            #232       
          bne            @loop4 ; pero este comparamos antes de 255, 
                                ; ya que solo queremos copiar 1000 bytes, no 1024
          
          rts

Y como usamos esta “función”?

Bien, supongamos que generamos una pantalla con el editor, y la guardamos con una etiqueta de nombre screen, entonces la llamaríamos de la siguiente manera:

    ldx #<screen
     stx lsbCopyAddress
     ldx #>screen
     stx msbCopyAddress
     jsr copyToScreen

Nótese que utilizamos ‘<‘ para indicar que queremos el LSB de screen, y ‘>’ para indicar que queremos el MSB de screen (una característica muy común de los ensambladores).

y esto es todo por hoy… en la próxima entrega vamos a ir armando el menu de entrada, y en las siguientes vamos a ir finalizando el tutorial. Nos vemos!

Recargas de combustible – parte 3 (final)

Y para terminar con los tanques de combustible, en esta entrada vamos a hacer la deteccion de colisiones. Aqui la idea es que cualquiera de los dos jugadores que agarre el tanque le incremente en 10 unidades el fuel. Y si el fuel lo alcanza un disparo, entonces lo destruye. Basicamente modificamos el archivo detectCollision.asm para que detecte las colisiones jugador1-fuel, jugador2-fuel, disparo1-fuel, disparo2-fuel.

Primero, las variables que necesitaremos (en vars.asm):

tempCollision = $12 ; guardo el estado de colision $d01e
FUEL = $10 ; 16, constante sprite
cantFUEL = 10 ; cantidad de combustible de cada fuel

Luego el código, una simplificación de lo que ya estaba, y luego el agregado de las partes que comprueban las colisiones (detectCollision.asm):

detectCollision
          lda            $d019     
          and            #$04      
          cmp            #$04      
          bne            @jmpSkipSprDetect0
          
          lda            $d01e
          sta            tempCollision  ; guarda los sprites que colisionaron

           ; detecto que ocurrio una colision de sprites
           ; colision disparo1 con jugador 2
           .
           .
           .

@checkJP1
                                   ; check JP1 & fire JP2
           ; colision disparo2 con jugador 1
           .
           .

@checkJP1Fuel                   
                                        ; detecta si jugador1 agarro fuel
          lda            tempCollision  ; recupero el estado de colision
          and            #JP1 + FUEL
          cmp            #JP1 + FUEL
          bne            @checkJP2Fuel  ; si no colisionaron compruebo 
                                        ; la siguiente
                                        ; si colisionaron:
          ldx            #4             ; paso el statusFuel a 4 
          stx            statusFuel
                                        
          lda            JP1Jet         ; y le sumo combustible al jugador 1
          adc            #cantFUEL 
          sta            JP1Jet    
          bcs            @setMaxFuel1   ; si ocurre un desbordamiento 
                                        ; seteo el maximo (255)
          
          jmp            @checkJP2Fuel  ; chequeo el siguiente
          
@setMaxFuel1
          lda            #$ff      
          sta            JP1Jet    

          
@checkJP2Fuel                   
          ; idem codigo que checkJP1Fuel
          .
          .
          .


@checkFireFuel1                  
                                          ; detecta si se destruyo con tiros 
          lda            tempCollision    ; lo recupero del temporal
          and            #FUEL + FJP1
          cmp            #FUEL + FJP1
          bne            @checkFireFuel2  ; si no chequea el siguiente caso
          
          jsr            turnOffFire1     ; apaga el disparo 1
          ldx            #4
          stx            statusFuel       ; y pasa el statusFuel a 4

@checkFireFuel2                  
          ; idem codigo checkFireFuel1
          .
          .
          .

@skipSprDetect
          rts

Como siempre, el código completo lo pueden bajar desde el repositorio:
https://github.com/moonorongo/jp_wars.git

Hasta la próxima!