Volver
Capítulo 2: Instrucciones Básicas

Por Dark-N: hernaldog@gmail.com
http://darknromhacking.com


Versiones

  • 1.2:
       -Pasada a html.
       -La releí y mejoré explicaciones. Se agrega sección "Gusto a Poco".
  • 1.1:
       -Revisado completamente sobre todo la semántica de lo que trato de explicar.
       -Corregí lo que me dijeron en el Foro de Todotradus.
  • 1.0:
       -Versión original del tutorial.


    Introducción

    En este capitulo se verán las instrucciones ASM principales y las que más se repiten en un Log (Tracer.log) que arroja el Snes9X trace.


    Operaciones Binarias

    Existen 3 operaciones lógicas: AND, OR y NOT, pero existe un operador compuesto llamado XOR, todos se aplican sobre varios bit, pero en la SNES generalmente es sobre 2 bits.
    
       AND  (ambos bits deben ser 1 para que el resultado sea 1)
    1 AND 1	 = 1	=> aquí los 2 bits son 1 y 1
    1 AND 0	 = 0
    0 AND 1	 = 0  	=> aquí los 2 bits son 0 y 1
    0 AND 0	 = 0
    
    
       OR  (basta un 1 para que el resultado sea 1)
    1 OR 1  = 1
    1 OR 0  = 1
    0 OR 1  = 1
    0 OR 0  = 0
    
    
     NOT (es el negado de algo, el NOT de "algo" da como resultado lo contrario)
    NOT 1  = 0
    NOT 0  = 1
    
    
    XOR (si todos son 1 o si todos son 0 entonces el resultado es 0)
    1 OR 1	= 0
    1 OR 0	= 1
    0 OR 1	= 1
    0 OR 0	= 0
    
    Ejemplos:
    
        11011011       11101100          01101011
    AND 01101000    OR 11001111      XOR 10101000
       ----------     ----------        ----------
        01001000       11101111          11000011

    Shifting (Rotaciones de Dígitos)

    Cualquier número decimal se puede componer de varias sumas de otros números que tienen una base. Tenemos varios base: 10 (decimal), 2(binario) o 8(octal). Por ejemplo el 12 en base 2 sería:
    12 = 1*2^3  + 1*2^2  + 0*2^1 + 0*2^0  = 8 + 4 + 0 + 0 = >  en binario es 1100
    
    Ahora si un dígito Binario lo invertimos o rotamos a la izquierda se multiplica por 2 ese número:
    
    20          *          2      =       40
    010100  =>  rotar a la izquierda =>  101000 = 40 decimal
    
    
    Y podemos rotar ese dígito a la derecha para dividir.
    
    20          /           2     =      10
    010100  =>  rotar a la derecha =>   001010 = 10 decimal
    Shifting se usa mucho para el dibujo de fuentes, ya que permite crear las sombras de las letras. Para más información mira este post que hice en Fortaleza Romhack cuando el foro estaba hospedado en Nekein.


    Cargas y Almacenamientos

    Todo se resume en meter datos a la memoria y sacar datos desde la memoria.
    En la snes un valor es un dígito hexadecimal (hex abreviado) y se representa como #$Valor o 0xValor, por ejemplo #$AC, #$123, 0x12CA, 0xA, etc.
    Otra forma sencilla de escribir un valor es usar hex o una h por ejemplo: BC hex o 0Ah. Esta forma sencilla solo es aplicable para enseñarles, no se usa en los LOG ni al programar en asm.
    Una dirección en la snes igual es hexadecimal y se representa con $Dirección, por ejemplo la dirección de 16 bits $1238, o esta de 8 bits $FF.

    LDA
    Carga al acumulador un valor de 8/16 bits.

    Ejemplos:
    LDA $05 ; carga acumulador con el valor que está dentro de la dirección $05.
    LDA #$AC ; carga acumulador con el valor AC hex.
    LDA $CCE602, X ; carga el valor que esta en la dirección [CCE602 +X], X es un índice que se puede incrementar así podemos sacar valores desde $CCE602, $CCE603, $CCE604, etc.

    LDX, LDY
    Carga al índice X/Y un valor de 8/16 bits.

    Ejemplos:
    LDX #$AC ; carga X con el valor AC hex.
    LDX $AC ; carga X con el valor que esta dentro de la dirección AC.

    STA, STX, STY
    Almacena en una dirección de memoria un valor dado por A, X o Y.

    Ejemplos:
    STA $3AFF ; guarda lo que está en A en la dirección $3AFF.
    STX $BB ; guarda lo que está en X en la dirección $00BB.


    Modificaciones del ancho de bit

    Se puede cambiar el ancho de bits desde 8 a 16 bits y viceversa del Acumulador e Índices. Esto sirve para muchas cosas, que lo veremos luego, pero como ejemplo práctico, si queremos meter el valor 0xCA en el acumulador, conviene más tener un acumulador definido con un ancho de 8 bits (ya que 0xCA es de 8 bits), si A esta seteado con un ancho de 16 bits esta bien pero no es tan adecuado.
    Otro ejemplo sería si queremos meter en A un 0x123D conviene tener un ancho de 16 bits.

    Ejemplos:
    REP #$20 ; Hacemos que el acumulador esté en modo de 16 bits (puede almacenar valores de 16 bits)
    REP #$30 ; pone los índices X e Y en modo 16 bits.
    SEP #$30 ; X,Y,A los dejamos en modo de 8 bit.
    SEP #$20 ; Acumulador lo dejamos en modo de 8 bit.


    Incrementos/Decrementos

    Para decrementar o aumentar en 1 el Acumulador o el Índice X/Y tenemos 2 instrucciones básicas: DEC que resta 1 y INC que suma 1.
    Si conoces PHP, JAVA o C#, DEC es igual a i-- o el i=i-1 de C++ y INC es como el i++ o i=i+1.
    Ejemplos:
    INC 	 ; incremento el valor del Acumulador en 1hex.
    INC A	 ; incremento el valor del Acumulador en 1hex (da lo mismo que poner INC).
    DEC X	 ; decremento el valor del registro X en 1hex.
    DEX 	 ; decrementa el registro X en 1hex.
    DEY 	 ; decrementa el registro X en 1hex.
    INX 	 ; incrementa el registro X en 1hex.
    INY 	 ; incrementa el registro Y en 1hex.
    DEC $Dir ; decrementa lo que haya dentro de la dirección Dir en 1.
    INC $Dir ; incrementa lo que haya dentro de la dirección Dir en 1.
    
    Código comentado 1:
    Si A=0x123C
    INC   ; A valdrá 0x123D
    DEC
    DEC   ; aquí A valdría 0x123B
    
    Código comentado 2:
    $1234: 0xBC ; dentro de $1234 esta el valor 0xBC
    INC $1234   ; $1234 contiene ahora un 0xBD


    Saltos y Ciclos

    Con algunas instrucciones podemos saltar a un cierto label o dirección. También puedo emular un típico IF-Then o incluso un ciclo for ya que puedo comparar y luego saltar.
    En general tenemos 2 tipos de salto:

  • 1. Instrucciones de Salto sin condición: JSR que salta a una subrutina, JSL que salta a una dirección o label.
    Ejemplo 1: uso del JSR
    
      LDA #$00
    hola   ; esto es un Label
      INC A
      JSR hola    
    
    Este ciclo hace que se carge A=0x00, luego se incremente a A=01, luego vaya a "hola", 
    es decir que ejecute de nuevo INC (A=0x02), y así sucesivamente, A llegará a 0xFF si 
    esta en modo de 8 bits o a 0xFFFF si esta en modo de 16 bits.
    
    Ejemplo 2: uso de RTS
    
    STA $05
    JSR Hola2	; salta a Hola2 sin condición
    LDA #$10
    
    Hola2
      LDA #$12	; cargamos A=12h   
      RTS 		; retorna de la subrutina (sale de ella) y llega a la instrucción que sigue a 
                    ; JSR que la llamo, es decir que al hacer RTS ejecuta LDA #$10.
  • 2. Instrucciones de Salto con condición o con comparación: las que permiten simular un if-else o ciclos for/while.

    Hay que tener claro el tema de los Flag C y Z del registro P que se vio en el capítulo anterior.
    Aquí 9 tipos de saltos que dependen de los flag Z, C y V:

    BRA (branch Always)
    BEQ (branch on equal)
    BNE (branch on not equal)
    BCC (branch on carry clear)
    BCS (branch on carry set)
    BVC (branch on overflow clear)
    BVS (branch on overflow set)
    Branchea (salta) siempre.
    Branchea si el flag z está a 1
    Branchea si el flag z está a 0
    Branchea si el flag c está a 0
    Branchea si el flag c está a 1
    Branchea si el flag v está a 0
    Branchea si el flag v está a 1
    -BPL es igual a BCS
    -BMI es igual a BCC

    Si no sabes que es C, recuerda el Capitulo 1 del curso.
    Para aprender a hacer una condición if-else, debes aprender a comparar. Las 6 comparaciones que básicas son:
    CMP #$valor compara valor con Acumulador
    CMP $Dir compara lo que hay en la dirección con Acumulador
    CPX #$valor compara el valor con el registro X
    CPY #$valor compara el valor con el registro Y
    CPX $Dir compara lo que hay en la dirección con el registro X
    CPY $Dir compara lo que hay en la dirección con el registro Y

    Además los CMP/CPX/CPY pueden modificar el flag Carry (C) o Zero (Z) de esta forma:

    Si el operando es mayor a A/X/Y pone el flag c a 0 y el flag z a 0
    Si el operando es menor a A/X/Y pone el flag c a 1 y el flag z a 0
    Si el operando es igual a A/X/Y pone el flag z a 1
    Y el flag v (overflow) se activa cuando una operación hace desbordamiento en su resultado. Ver capítulo 1.
    Ejemplo 1: uso del CMP
    
    CMP $CCE600 	; compara A con lo que hay en $CC:E600, esta instrucción siempre va seguida de 
    		; un de salto, así si la comparación se cumple salta a otra dirección (nos 
    		; salemos de esta rutina y vamos a otra), si no se cumple la comparación, sigue leyendo.
    
    Ejemplo 2: CPX con BNE
    
       LDX #$0800	; cargamos X con 0800h
    bucle:
       LDA $7EC800,x  ;cargamos a con lo que está en la dirección $7EC800 + $X=$7ED000
       STA $7F0000,x  ;ese valor se guarda en la dirección $7F0000+$800=$7F0800
       DEX		  ;X=X-1, es decir, X = 0800-1 = 07FFh
       CPX #$0000	  ;se compara con X con 0000h ¿son iguales? No. ¿El operando es MENOR a X? Si. Entonces el Flag c=1 y Flag z=0.
       BNE bucle	  ;como carry=0, BCC salta a "bucle". Entonces sube y hace el ciclo de nuevo.
       ...		  ;Pero ahora X=07FF y así sigue haciendo el ciclo hasta que X sea 0000. Luego se pregunta 
    		  ;¿X es igual a 0000h? Si. El Flag Z cambia a 1. BNE salta si el Flag Z=0, por lo tanto no salta y sigue con ....
    		  ;En resumen se copiaron 800 datos desde la RAM (ya sabes según en al capitulo anterior, 
                      ;que si se habla de $7EXXXX o $7FXXXX es que accedimos a la RAM) y se dejan en
                      ;posición RAM de $7F0000 en adelante.
    
    

    Transferencia

    Estas instrucciones sirven para copiar el contenido de un registro a otro.
    Las transferencias mas conocidas son:

    TAX ; se transfiere (es lo mismo que "copiar") el valor de A a X.
    TAY ; se transfiere el valor de A a Y.
    TCD ; se transfiere el valor de A al registro DP.
    TXS ; Transfiere de X a la Pila.
    TXY ; Transfiere de X a Y.
    TYX ; Transfiere de Y a X.
    TYA ; Transfiere de Y al acumulador.
    TXA ; Transfiere de X al acumulador.



    Gusto a Poco

    1. Si tenemos
    LDA #$0000
    BEQ $1234
    BNE $4321
    BRA $0001

    ¿Salta a $1234, a $4321 o a $0001?

    R: Salta a $1234 ya que LDA #$0000 pone Z=1 y BEQ salta si Z=1.


    2. ¿Con cuanto queda Y?

    Direc / Instrucción
    ------ ----------
    $0998: LDY #$0000
    $1000: LDA #$0002
    $1002: INC
    $1003: INY
    $1004: CMP #$0A
    $1006: BEQ $2000
    $1008: BRA $1002
    $1010: DEC
    ...
    $2000: DEC

    R: Es un ciclo que aumenta A e Y hasta que A sea igual a 0Ah. A parte con 0002h, y hasta 0Ah se le suman 8hex. El registro Y cuenta cada iteración por lo que Y=8.



    Referencia Rápida de Instrucciones

    Aquí está la mayoría de las instrucciones en Ingles claro, no es necesario aprenderlas todas, pero es para que empiecen a acostumbrarse.
    ADC - add with carry
    AND - logical AND
    BCC - branch if carry clear
    BCS - branch if carry set
    BEQ - branch if equal
    BIT - bit test
    BMI - branch if minus
    BNE - branch if not equal
    BPL - branch if plus
    BRA - branch always
    BRK - break point instruction
    BVC - branch if overflow clear
    BVS - branch if overflow set
    CLC - clear the carry flag
    CLD - clear the decimal flag
    CLI - clear the interrupt flag
    CLP - clear bits in P
    CLR - store a zero into memory
    CMP - compare accumulator
    CPX - compare x register
    CPY - compare y register
    CSP - call system procedure
    DEC - decrement acc or memory
    DEX - decrement x register
    DEY - decrement y register
    EOR - exclusive-or accumulator
    HLT - halt (stop) the clock
    INC - increment acc or memory
    INX - increment x register
    INY - increment y register
    JMP - jump to new location
    JSR - jump to subroutine
    LDA - load accumulator
    LDX - load x register
    LDY - load y register
    MVN - block move (decrement)
    MVP - block move (increment)
    NOP - no operation
    ORA - logical or accumulator
    PHA - push accumulator
    PHP - push p
    PHX - push x register
    PHY - push y register
    PLA - pop accumulator
    PLP - pop p
    PLX - pop x register
    PLY - pop y register
    PSH - push operand
    PUL - pop operand
    RET - return from subroutine
    ROL - rotate left acc/mem
    ROR - rotate right acc/mem
    RTI - return from interrupt
    RTL - return from long subroutine
    RTS - return from short subroutine
    SBC - subtract with carry
    SED - set decimal flag
    SEI - set interrupt flag
    SEP - set bits in P
    SHL - shift left acc/mem
    SHR - shift right acc/mem
    STA - store accumulator
    STX - store x register
    STY - store y register
    SWA - swap accumulator halves
    TAD - transfer acc to D
    TAS - transfer acc to S
    TAX - transfer acc to x
    TAY - transfer acc to y
    TCB - test and clear bit
    TDA - transfer D to acc
    TSA - transfer S to acc
    TSB - test and set bit
    TSX - transfer S to X
    TXA - transfer x to acc
    TXS - transfer x to S
    TXY - transfer x to y
    TYA - transfer y to acc
    TYX - transfer y to x
    WAI - wait for interrupt
    XCE - exchange carry with emulation bit

    Volver
    blog comments powered by Disqus
    2003 - 2018    La Web de Dark-N