01-LDA e STA - SMW Central

Propaganda
01-LDA e STA
Bom, começamos com o básico: LDA e STA.
Suponha que queiramos fazer um bloco que faz o Mario ficar grande, não importa seu
estado. Para isso, precisamos do RAM address, disponível no SMWCentral. Assim,
temos como a RAM que controla o powerup $19.
Para fazer o Mario ficar grande, temos que ter em mente que o RAM $19 suporta esses
valores:
#$00-Mario pequeno
#$01-Mario grande
#$02-Cape Mario
#$03-Fire Mario
Então, começamos:
LDA #$01
Simples não? Você pode fazer também LDA #xx, LDA $xx, LDA$xxxx ou
LDA$xxxxxx (esses três últimos são para ram, e LDA #xx é modo decimal). Tenha em
mente o fato de que você precisará de Hexadecimais, então tenha sempre aberta a
calculadora do Windows no modo científico.
Agora, para que o Mario fique grande, você tem que carregar o valor e botá-lo na RAM,
como se você pusesse um CD no computador. Agora, para fazer o Mario ficar grande,
temos o comando STA. STA pega o valor após o LDA e o transfere para o RAM. Isso
quer dizer que NÃO EXISTE STA#xx ou STA#$xx. Então para o Mario ficar grande,
temos:
LDA #$01
STA $19
RTL
Ahhh, sim. RTL. Como estou fazendo esse tutorial para criação de blocos para BTSD,
uso RTL. Para o velho BT, é RTS. NUNCA SE ESQUEÇA DE TERMINAR O
CÓDIGO COM RTL OU RTS OU ADEUS SUA ROM!!! RTL diz ao game que o
código parou, ou seja, que o game pode continuar a executar suas ações. Então por isso
RTL é importante.
Como queremos um Bloco para BTSD, temos que adicionar no início do arquivo:
JMP MarioBelow : JMP MarioAbove : JMP MarioSides : JMP SpriteV : JMP SpriteH :
JMP MarioCape : JMP MarioFireball
Explicarei a razão dos JMPs depois. Mas como são muitas labels, e queremos um bloco
simples, poderemos terminar o arquivo assim:
JMP Mario : JMP Mario : JMP Mario : JMP R : JMP R : JMP R : JMP R
Mario:
LDA #$01
STA $19
RTL ;posso OMITIR esse RTL, porque depois dessa label, a próxima SEMPRE é
executada, então...
R:
RTL
Agora, se você quer fazer o Mario ficar pequeno, voce terá que escrever LDA #$00,
STA $19 e RTL? Não. Para fazer com que uma RAM tenha seu valor zero, use STZ$xx
ou STZ$xxxx. NOTA: NÃO EXISTE STZ$xxxxxx!
Então, nosso bloco para o Mario ficar pequeno será assim:
JMP Mario : JMP Mario : JMP Mario : JMP R : JMP R : JMP R : JMP R
Mario:
STZ $19
RTL ;posso OMITIR esse RTL, porque depois dessa label, a próxima SEMPRE é
executada, então...
R:
RTL
Então, agora sabemos usar LDA, STA, STZ, RTS e RTL. Mas isso não é tudo. Agora
temos os condicionais (branches).
02-Condicional
Agora sim. Suponhamos que queremos que o Mario só fique pequeno se tiver
EXATAMENTE 10 moedas. Usando somente LDA e STA não dá para fazer esse tipo
de coisa. Daí, temos CMP e os Branch commands. Vamos ao assunto: CMP pega todos
os valores de uma RAM e compara com o valor estabelecido. Então, temos variadas
possibilidades de Branch. Agora, retornando ao problema original, escrevemos então:
JMP Mario : JMP Mario : JMP Mario : JMP R : JMP R : JMP R : JMP R
Mario:
LDA $0DBF ;agora temos que carregar RAM, não um valor (RAM do coin counter)
CMP #$0A ;compara o coin counter se o Mario tem 10 moedas
BNE R ;Se o Mario não tiver 10 moedas, o código pula diretamente à label
especificada, ou seja, o R (return)
STZ $19 ;Como o Mario tem 10 moedas, ele fica pequeno.
RTL
R:
RTL
Então, explicando os Branches:
BEQ - Só funciona se o valor comparado é IGUAL ao que está correndo na RAM.
BNE - O oposto de BEQ
BCC - Funciona se o valor comparado é MENOR do que está correndo na RAM.
BCS - O oposto de BCC
BPL - Funciona somente se o valor dentro da RAM está entre $00-$7F. Não é muito
usado no nosso caso
BMI - Igual ao BPL, mas funciona somente com $80-$FF.
Agora, no bloco acima: se usássemos BEQ, o que o código faria?
03-Operações
Suponhamos que queiramos um bloco que aumente o número de moedas do Mario.
Novamente precisamos de novos opcodes. Nesse caso, temos INC e DEC. Usam a
MESMA regra do STZ: Não existe INC $xxxxxx nem DEC $xxxxxx. Faça isso quando
isso acontecer:
LDA $xxxxxx
INC (ou DEC)
STA $xxxxxx
Então, para fazer um bloco que adicione 1 moeda ao mario, temos:
INC $0DBF
RTL
Simples não? Se você trocar por DEC, o que acontece?
Agora, INC e DEC SÓ FUNCIONAM NO LIMITE DE BITS DE UMA RAM. Isso
quer dizer que, se o valor de uma RAM for $FF, usando o INC o valor retorna a $00.
DEC em $00 faz o valor ir para $FF.
Mas se queremos fazer um bloco que dê 10 moedas ao Mario, escrever INC $0DBF é
inútil e não funciona. Para isso existe CLC e ADC. NOTA: Quando for fazer seus
blocos, NUCA SE ESQUEÇA DE BOTAR CLC ANTES E ADC. O mesmo vale para
SEC e SBC, da subtração. CLC evita que o valor em ADC seja adicionado +1. O
mesmo para SEC.
Agora suponhamos um bloco que adicione 10 moedas ao Mario se ele não for pequeno;
se ele for pequeno, o bloco RETIRA 10 moedas. Divertido efeito não? Mas vamos ao
que interessa:
Mario:
LDA $19
BCS AddCoin ; Se o Mario for pequeno, não pula (omiti o CMP #$00; ele não é
necessário)
LDA $0DBF
SEC
SBC #10 ;é preferível usar SBC #$0A
STA $0DBF ; sempre que você carrega um valor e operaciona ele, use sempre STA para
botar o valor novo
RTL
AddCoin:
LDA $0DBF
CLC
ADC #10 ;o mesmo com o SBC
STA $0DBF
RTL
R:
RTL
Para multiplicar, use ASL. Para dividir, LSR. Mesmas regras de INC e DEC.
04-Tables
Para ceros blocos, não dá para fazer apenas branches. Suponhamos que queremos um
bloco que adicione moedas ao Mario dependendo de seu powerup. Ficaria cansativo,
pois são QUATRO valores. Então, usamos Tables.
Até agora, quando trabalhamos com blocos, usamos o registro chamado acumulador.
Em tables, vamos precisar dos registros X e Y. Os comandos para eles são:
LDA - LDX e LDY
STA - STX e STY
CMP - CPX e CPY
INC - INX e INY
DEC - DEX e DEY
Como estamos usando ASM básico nesse tutorial, precisamos somente de LDX e LDY.
Então, voltando ao nosso problema, temos:
LDX $19
LDA $0DBF
CLC
ADC Table,x
STA $0DBF
RTL
Mas temos que especificar os valores que serão adiconados. Como temos 4 valores em
$10, temos 4 valores na Table. Tables começam com dcb e são formadas por $xx, $xx,
$xx, ...
Então, para o nosso código, temos:
LDX $19
LDA $0DBF
CLC
ADC Table,x
STA $0DBF
RTL
Table:
dcb $01, $03, $05, $07
Nesse bloco, mario ganha:
1 moeda se for pequeno
3 se for grande
5 se for caped
7 se for fire
Simples não? E você pode trocar LDX e ADC Table,x por LDY e ADC Table,y.
Agora você também pode indexar valores. Usando o exemplo anterior, só que fazendo o
Mario derrapar:
LDX $19
LDA Table,x
STA $86
RTL
Table:
dcb $40, $20, $25, $15
Bem simples, não?
05-Jumps
Lembra sobre o que eu disse sobre o começo dos blocos para BTSD? Você sempre
observa JMPs. Neste tópico, aprenderemos sobre os jumping opcodes.
Aindo no caso do bloco que faz o Mario ficar grande, temos:
LDA #$01
STA $19
RTL
Podemos substituir por:
LDA #$01
JSR RAM
RTL
RAM:
STA $19
RTS
Embora eu esteja trabalhando com BTSD, depois desse STA botei RTS. Por quê?
Rotinas que você acessa com JSR ou JMP SEMPRE terminam em RTS (exceção são os
JMPs do início dos blocos do BTSD, uma vez que são offsets), enquanto as acessadas
com JSL e JML sempre com RTL.
JSR também pode ser usado para pular para rotinas no DB (data bank) corrente. Mas
isso não terá muita importância agora. Para pular para DBs que são importantes, usamos
JSL.
Por exemplo: Queremos fazer um bloco que, se o Mario for pequeno, ele é morto, mas
se ele não for pequeno, perde todas as suas moedas. Como já disse, em blocos BTSD os
JMPs indicam os offsets que serão usados (como os offsets que você vê no BT, mas sem
os relocs, o que facilita as coisas). Então, voltando ao código, temos:
(Quero que esse bloco funcione somente se o Mario o tocar por baixo e por cima)
JMP Check : JMP Check : JMP Return : JMP Return : JMP Return : JMP Return : JMP
Return
Check:
LDA $19
BNE EraseCoins
JSL $00F606 ;Tem SEMPRE que ser JSL $yyxxxx (long addressing); esse valor eu
achei no ROM Map; mata o Mario
RTL
EraseCoins:
STZ $0DBF
RTL
Return:
RTL
JMP funciona como JSR, mas IGNORA o que foi escrito depois. A mesma coisa entre
JML e JSL. Exemplo (só valores inúteis):
JSR Rand_V (ou JSL)
;code
JMP Rand_V (ou JML)
;code
Comentários, Ponto-e-vírgula e Dois-pontos em ASM
Não há muito o que dizer. Nem precisa seguir.
Você normalmente vê um código em opcodes alinhados linha a linha, mas você pode
fazer isso também:
LDA #$xx : STA $yy : RTL
Mas isso é uma perda de tempo e não é aceito no SMWCentral. Então use dois-pontos
somente nas offsets determinadas pelos JMPs em blocos BTSD.
Você pode comentar seu código. Depois de um opcode que você queira comentar,
adicione ";", "\", "|" ou "/." Eles são ignorados na leitura do código. Mas antes dos sinais
"\|/" sempre bote ";". Evita erros. Botar ; ANTES de um opcode o ignora, então cuidado.
Exemplo de comentários:
LDA $0DBF ;RAM do contador de moedas
CMP #$15 ;se Mario não tiver MAIS de 15 moedas
BCS NoHurt ;ele se fere
JSL $00F5B7 ;Subrotina de ferimento
LDA #$04 ;\Som do Mario encolhendo
STA $1DF9 ;/
RTL ;fim do código
NoHurt:
RTL
Definições
Em alguns blocos do SMWCentral, há lugares onde se tem um valor que você pode
mudar, e em sprites também. basta dar uma olhada nos custom bosses do Maxx e do
Iceguy. São valores definidos, e num bloco, você pode colocá-los SEMPRE depois dos
JMPs.
Exemplo. Um bloco que faça um determinado som. Se você quer que outros editem o
som sem se perderem, você pode colocar dessa maneira:
!SOUND = $04
!RAM = $1DF9
LDA #!SOUND
STA !RAM
RTL
06-Introdução ao binário
Além de decimais e hexadecimais, o ASM suporta binários. Binários são números
compostos apenas por 0 e 1, em casas com potência de 2. O máximo que o ASM
comporta de valor é 1111 1111 ($FF). Lembra do comando LDA? Podemos ter também
LDA #%01000100, por exemplo, que é o mesmo que LDA #$44. Basicamente, temos:
#%00000001 = #$01
#%00000010 = #$02
#%00000100 = #$04
#%00001000 = #$08
#%00010000 = #$10 (#16)
#%00100000 = #$20 (#32)
#%01000000 = #$40 (#64)
#%10000000 = #$80 (#0128)
#%11111111 = #$FF (#0255) (valor máximo)
Então, como trabalhamos com o binário? Simples. Suponhamos que tenha um bloco que
teleporta se você apertar o botão para cima:
LDA $15
CMP #$08
BNE R
LDA #$06
STA $71
STZ $89
STZ $88
R:
RTL
O problema desse código é que ele roda SOMENTE se você apertar o botão para cima.
Suponhamos que eu apertei um outro botão:
Só com o botão para cima:
00001000 - #$08
Com outro botão genérico:
00001100 - #$0C!
Então temos a opção de usar AND. AND IGNORA outros bits, então, para esse bloco,
temos:
LDA $15
AND #$08
BEQ NoUp
LDA #$06
STA $71
STZ $89
STZ $88
RTL
NoUp:
RTL
Simples não? AND também operaciona:
LDA #%0101
AND #%0011
RE.: #$0001
AND só converte um bit a 1 nessa operação se o bit em LDA e em AND for 1. Lógica
do E: Só é verdade se os DOIS forem verdade.
Ex.:
LDA $13
AND #$07
BNE Skip
INC $19
Skip:
RTL
Esse código faz com que a cada frame de animação, o powerup do Mario sobe, até parar
no frame 8 (#$07+1).
Só vou cobrir essa parte do AND. ORA e EOR são quase inúteis num bloco, mas vou
ensiná-los para nosso uso.
ORA também faz operações:
LDA #%0101
ORA #%0011
RE.: #%0111
ORA segue a lógica do "OU": Só e falso se todos forem falsos.
ORA também checa se mais de uma RAM address é zero. Exemplo: Um bloco que zera
o contador de moedas do Mario se ele tiver na opção OFF (bloco ON/OFF), em cima do
Yoshi e dentro da água:
LDA $14AF
ORA $187A
ORA $75
BEQ Return
STZ $0DBF
RTL
Return:
RTL
Esse código zera o contador de moedas se os bits das RAM com LDA e ORA NÃO
sejam zero. Se todos forem zero, ele simplesmente retorna.
EOR não faz muita coisa em blocos, só inverte bits.
Operação com EOR:
LDA #% 0101
EOR #% 0011
RE.: #% 0110
Simples. Se os dois bits forem IGUAIS, EOR os torna zero. EOR é usado em sprites,
quando você quer flipá-los se eles tocam uma parede.
TRB e TSB
Simples: esses dois opcodes servem para tornar qualquer bit de uma RAM 0 (TRB) ou 1
(TSB). Suponhamos que queremos um bloco que faça o Mario pular se um fireball bter
nele. Aqui estamos:
JMP Return : JMP Return : JMP Return : JMP Return : JMP Return : JMP Return : JMP
Fiery
Fiery:
LDA #$80 ;Bit dos botões B e A, que fazem o Mario pular
TSB $15 ;Ativar bit na RAM
RTL
Return:
RTL
P=nvmxdizc
Não há muito o que dizer sobre essa flag, o processador de SNES, mas há coisas muito
úteis. Antes de mais nada, explicando as letras:
n - Negative Flag - será 1 se houver valores entre $80 e $FF, senão será 0.
v - Overflow Flag - subtrações de Hexadecimais $80-$FF que deem valores $00-$7F o
ativam. Usado em branches (BVC e BVS)
m - Muda o Acumulador de 8-bit para 16-bit e vice-versa (1 é 8-bit, então em nosssos
códigos atuais, m=1)
x - Muda o Index de 8-bit para 16-bit
d - MOdo decimal. Ativado com SED e desativado com CLD. Usando SED, o código
operará somente em decimal.
i - Modo Interrupção. Ative com SEI e desative com CLI. Até existe um opcode de
retorno, RTI!!!! Mas não use esse bit.
z - Zero flag. Ativado se o valor do Acumulador é zero.
c - Carry Flag. O nono bit de um valor ASM.
NOrmalmente, se ativa um desses pedaços do processador com dois comandos: REP e
SEP.
Exemplo: Os Shop Blocks para 6-digit coin counter patch. Usando só CLCs e ADCs
não daria para tirar, por exemplo, 256 moedas ($0100), porque o acumulador é 8-bit,
então só suportaria valores de $00 a $FF. Então, é ativado o modo 16-bit. Como é o bit
#$20 do Processador, e 16-bit o desativa, é colocado antes da subtração das moedas:
REP #$20
;Còdigo
SEP #$20
Mas só isso nos será útil agora: o 16-bit.
DICAS
1) Adicionando coins:
LDA $0DBF
CLC
ADC #$xx
STA $0DBF
RTS
; Não use esse ou terá glitches quando o counter passar de 100! Use esse abaixo:
LDA #$xx
STA $13CC
RTS
2) Machine cycles:
Evite usar long addressing quando o bank da RAM for 7E, por que assim, além de
acelerar o bloco, ainda torna possível o uso de STZ, que optimiza ainda mais o bloco.
LDA #$00
STA $7E0019 ; 7 ciclos
RTS
;Tirando Long addressing
LDA #$00
STA $19 ; 5 ciclos
RTS
; Usando STZ
STZ $19 ; 3 ciclos
RTS
3) Blocos que você ativa por baixo, mas só quer que eles ativem uma só vez:
Um bloco Mario-Luigi, pra funcionar direito, depois do código normal, tem que
adicionar:
LDA #$20
STA $7D
RTS
senão, quando você encosta no bloco, vai ficar trocando várias vezes entre o Mario e o
Luigi sem permitir seus movimentos!
4) Não usar os seguinte opcodes:
BRK - Significa "Break System"
COP - Quebra a ROM
SEI, CLI e RTI - Interrupt Flag
WAI - Espera por interrupção
XCE - Ativa modo emulador
STP - Congela o jogo
NOP - Não operaciona. Só usado em patches, para tirar certas rotinas do jogo, como
"Disable Score".
Stack
Como fazer pra recuperar um valor depois de usá-lo? É aí que entra o Stack. Stack é
como uma estante de bytes, onde você adciona ou tira "pacotes" de bits. Para isso,
usamos para o acumulador e os registros X e Y:
Para colocar um valor no Stack:
PHA, PHX, PHY
Para tirar um valor do Stack:
PLA, PLX e PLY
Agora, qual é a função do Stack? Preservação de valores:
LDA #$44 ; Stack = xx xx xx xx xx ...
PHA ;\ Stack = xx xx xx ... xx $44
;código ;/
PLA ;Stack = xx xx xx xx xx ...
Há outros opcodes Stack. Vamos nos focar Em três opcodes Stack para sprites:
PHB - Pega um 8-bit data bank e o coloca no Stack. Versão contrária: PLB
PHK - Pega o bank 8-bit corrente e o coloca no Stack. Versão contrária: Nenhuma (use
PLB)
Use esses três na criação de sprites, após o dcb "MAIN":
dcb "MAIN"
PHB
PHK
PLB
JSR SPRITE_ROUTINE
PLB
RTL
Outros opcodes stack:
PHP - Pega o status do P=nvmxdizc e o coloca no Stack. Versão contrária: PLP
PER - Pega o valor 16-bit de um label. Use como PER Label
PHD - Pega o valor 16-bit da página direta de registro e o coloca no Stack. Versão
contrária: PLD.
Há mais opcodes, mas são quase inutilizados em blocos.
BIT Test
Vamos a um dos comandos mais complicados: BIT. O que ele faz? Mexe com o
P=nvmxdizc. Mas como? Vamos supor que o valor do acumulador na RAM $0DBF
seja $40 (em Hex). Agora, fazemos:
BIT $0DBF
Vamos ao que interessa nessa tabela:
P=nvmxdizc N V M X D I
BIT test
Z C
80 40 20 10 08 04 02 01
Então, se o Valor em $0DBF é $40, e fazemos BIT $0DBF, estamos ativando a bit V do
processador. Ou seja: Overflow Flag. Por exemplo: Um bloco que só deixa o Mario
passar se ele tiver mais do que 64 moedas. Ele é assim:
LDA $0DBF
CMP #$40
BCS Return
LDY #$01
LDA #$30
STA $1693
RTL
Return:
RTL
Usando BIT:
BIT $0DBF (se A>=$40, V=set)
BVS Return (se V=set, branch)
LDY #$01
LDA #$30
STA $1693
RTL
Return:
RTL
XBA
XBA simplesmente FLIPA o valor da RAM. Exemplo:
LDA $0080
XBA
RAM=$8000
Taí. mais dois opcodes pro nosso conhecimento ASM...
Transferência de Registros
Usamos os comandos de transferência para colocar valores de um registro em outro.
Pricipais opcodes:
TAX
Copia o Acumulador em X
TAY
Copia o Acumulador em Y
TYX
Copia Y em X
TXY
Copia X em Y
TXS
Copia X no Stack. Como se fosse um PHX
TXA
Copia X no acumulador
TYA
Copia Y no Acumulador
TSX
Copia o Stack em X. Diferente de PLX
TCS
Copia o Acumulador no Stack. Como um PHA
TSC
Copia o Stack no Acumulador. Diferente de PLA
TCD
Copia o Acumulador no registro de página indireta 16-bit
TDC
Inverso de TCD
Exemplo:
LDA #$40 ; A=$40 e X=$F0
TAX ; A=$40 e X=$40
Usados muito em sprites, principalmente em dcb "INIT". SUB_HORZ_POS se encontra
no registro y, mas não daria para fazer STY $157C,x. Então, logo após JSR
SUB_HORZ_POS, coloca-se TYA STA $157C,x.
LDA ($xx)
Essa é complicada. Vamos ter que exemplificar:
Usemos LDA ($18). Vamos supor que:
$18=$80
$19=$02
LDA ($18) faz o seguinte:
LDA [$18+$19]
ou seja, sendo a soma dos valors RAM igual a $82, temos:
LDA ($18) = LDA $82!
É pouco prático, mas eficaz quando se trabalha com RAMs múltiplas...
Download