Arvorezinha – 6502 Assembly (NES)
Olá jovens garotos!
Desde sexta-feira que andei com ideias de fazer uma arvorezinha para a clássica NES, comecei então a minha jornada de ver como funcionava esta maravilha da minha juventude!
Achei o site nesdev que tem montes de informação sobre a consola, e tambem tem alguns sores para algumas roms, mas não achei nada que printasse texto para o ecrã, que é o pretendido. Num dos textos que li vi uma referência ao canal #nesdev da efnet, então lá fui eu, e não é que existe uma grande comunidade de pessoal que percebe e ama isto, e pelos vistos são todos amigaveis, falei la com uns deles, e um deles (não me recordo do nick...) enviou me um zipzinho com 4 tutoriais, e o ultimo deles escreve "Hello World" pro ecrã, copiei descaradamente do codigo desse.
Aqui segue o sores:
.include "nes.h"
POS1 = $0300 ; Endereço de memoria onde vai guardar o primeiro byte da posição de print inicial 0x21cd
POS2 = $0301 ; segundo byte
COUNTER1 = $0310 ; primeiro contador
COUNTER2 = $0311 ; segundo contador
TEMP1 = $0320 ; variavel temporaria usada para comparar com 5
CURBG = $0325 ; utilizado pra guardar o indice actual da palete para a cor do background
; The iNES header tells the emulator which circuit board to emulate.
; The emulator can see it, but the emulated NES cannot.
.segment "INESHDR"
.byt "NES", $1A ; these four bytes identify a file as an NES ROM
.byt 1 ; size of PRG, in 16384 byte units
.byt 1 ; size of CHR, in 8192 byte units
.byt 0, 0 ; mapper, mirroring, etc. You'll learn these later.
.segment "ZEROPAGE"
; Reserve 1 byte for the variable 'retraces', used to detect
; the vertical blanking interrupt
retraces: .res 1
; Now the NMI routine actually does something!
.segment "VECTORS"
; NMI vector is at $FFFA, reset at $FFFC, IRQ at $FFFE
.addr nmi, reset, irq
.segment "CODE"
.proc reset
; inicializar variaveis!!!
; variaveis pelos vistos começam em $c000
;posicao: .byte $21, $CD
lda #$21
sta POS1
lda #$cd
sta POS2
;counter1: .byte $00
lda #$00
sta COUNTER1
;counter2: .byte $00
lda #$00
sta COUNTER2
; define o currbg
lda #$05
STA CURBG
; Turn off PPU
lda #0
sta PPUCTRL ; turn off NMI
sta PPUMASK ; turn off display
; Set up stack pointer
ldx #$FF
txs
; Wait for PPU to stabilize
warmup1:
lda PPUSTATUS
bpl warmup1
warmup2:
lda PPUSTATUS
bpl warmup2
; define palete
lda #$3F
sta PPUADDR
lda #$00
sta PPUADDR
lda #$3A ; VERDE posicao 0
sta PPUDATA
lda #$3B ;
sta PPUDATA
lda #$3C;
sta PPUDATA
lda #$30
sta PPUDATA
lda #$16
sta PPUDATA
; If you're going to turn on rendering, you don't need to reset the
; VRAM address. Setting the scroll position does this for you.
; Turn on vblank notification
lda #VBLANK_NMI
sta PPUCTRL
; Wait for a vblank before turning the screen on
jsr wait_vblank
; PPUMASK controls whether sprites are displayed and whether the
; background is displayed. Here, display only the background.
lda #BG_ON
sta PPUMASK
nop
nop
lda #CH_ALL ; ligar o chip de som
sta SND_CHN
lda #15 | SQ_1_2
sta SQ1_VOL ; for pulse 1
lda #SWEEP_OFF
sta SQ1_SWEEP ; for pulse 1
_ciclo1:
ldx #$05 ; le o valor 5 para X - $c063
stx TEMP1 ; grava X em TEMP1
lda COUNTER1 ; le o contador 1 para A
cmp TEMP1 ; compara A com TEMP1 (5)
beq _fim ; se for igual -> _fim
lda #$00 ; le 0 para A
sta COUNTER2 ; define counter2 a 0
_ciclo2:
lda COUNTER1 ; le coutner1 para A
cmp COUNTER2 ; compara c1 com c2
bcs _estrela ; se for menor -> estrela
jsr proxima_linha ; manda mudar de linha
lda COUNTER1 ; le o c1 para A
clc ; limpa a carry
adc #$01 ; adiciona 1 a A
sta COUNTER1 ; guarda A em c1
jmp _ciclo1 ; salta para o _ciclo1
_estrela:
jsr imprime ; imprime *
jsr proximo_char ; move a posição para o prox char
lda COUNTER2 ; le o c2 para A
clc ; limpa a carry
adc #$01 ; adiciona 1 a A
STA COUNTER2 ; guarda A em c2
jmp _ciclo2 ; salta para _ciclo2
_fim:
jsr barulhinho1
; Wait for a few vblanks.
jsr wait_vblank
jsr wait_vblank
jsr wait_vblank
jsr wait_vblank
jsr wait_vblank
; Under most circumstances, we can only update the VRAM through
; PPUADDR and PPUDATA at one of two times: when rendering is turned
; off, and in the first 2200 or so cycles after vblank starts.
; The PPU is continuously accessing VRAM at all other times,
; so the CPU has to keep its hands off.
; But a vblank has just started, so we're safe.
jsr barulhinho2
; Spin until power off
forever:
jsr mudabackground
jsr wait_vblank ; aguarda um pouco senao nem se nota
jsr wait_vblank
jsr wait_vblank
jsr wait_vblank
jmp forever
.endproc
.proc mudabackground
; define o endereço da cor do bg
lda #$3F
sta PPUADDR
lda #$00
sta PPUADDR
lda CURBG ; le o curbg inicial 0x03
sta PPUDATA
EOR #$01
STA CURBG
jsr actualiza_ppu
rts
.endproc
.proc imprime
lda POS1
sta PPUADDR
lda POS2
sta PPUADDR
lda #'*'
sta PPUDATA
jsr mudabackground
jsr actualiza_ppu
jsr wait_vblank ; aguarda um pouco
jsr wait_vblank
jsr wait_vblank
rts
.endproc
.proc proxima_linha
; MUDAR DE LINHA
clc ; limpa o carry
lda POS2 ; le o segundo byte da posição2
adc #$20 ; incrementa 0x20 (32) , se der a volta mete o carry a 1
sta POS2 ; guarda
lda POS1 ; le o primeiro byte da posicao
adc #$0 ; incrementa 0 + carry
sta POS1 ; guarda
; REMOVER O NUMERO DE ESTRELAS IMPRESSAS, PARA IR PRO X INICIAL
sed
lda POS2 ; le o segundo byte da posição
sec ; define o carry
sbc COUNTER2 ; remove-lhe o numero de estrelas printadas
sta POS2 ; guarda
lda POS1 ; le o primeiro byte da posicao
sbc #$0 ; remove 0 - carry
sta POS1 ; guarda
jsr barulhinho2
rts
.endproc
.proc proximo_char
clc ; limpa a carry
lda POS2 ; le o segundo byte da posicao para A
adc #$01 ; incrementa 1 a A, se der a volta mete o carry a 1
sta POS2 ; guarda em POS2 o valor de A
lda POS1 ; le a primeira posição para A
adc #$0 ; incrementa 0 + carry (se houver)
STA POS1 ; guarda POS1
jsr barulhinho1
rts ; volta a onde foi chamado
.endproc
.proc actualiza_ppu
; Set the scroll position of the background by writing (x, y)
; pixel coordinates to PPUSCROLL. This should always be done
; as the last thing before turning on rendering.
; The earliest programs will always scroll to (0, 0), so write
; 0 twice.
; Again, reset the VRAM address by writing to PPUCTRL and
; PPUSCROLL. We need to do this every time we use PPUADDR and
; PPUDATA to load something into VRAM.
lda #VBLANK_NMI
sta PPUCTRL
lda #0
sta PPUSCROLL
sta PPUSCROLL
rts
.endproc
.proc barulhinho1
; The E above that B is 1318.5 Hz.
lda #84 ; (111860.8 / 1318.5) - 1
sta SQ1_LO
lda #%00001000
sta SQ1_HI
rts
.endproc
.proc barulhinho2
; The B nearly two octaves above middle C is 987.77 Hz.
;lda #112 ; (111860.8 / 987.77) - 1
lda #50 ; (111860.8 / 987.77) - 1
sta SQ1_LO
lda #%00001000
sta SQ1_HI
rts
.endproc
; When the PPU is told to generate interrupts on vertical blank,
; it sends a signal to the CPU that sends it here.
.proc nmi
; Set the 'retraces' variable up by 1 so that the main program
; can see that a vblank has happened.
inc retraces
rti
.endproc
; We don't use IRQ so don't do anything
.proc irq
rti
.endproc
; This subroutine waits for the NMI handler to change retraces.
.proc wait_vblank
lda retraces
; The 'cmp' (compare) instruction reads a value from a memory
; location and then performs a subtraction: register A minus the
; value from memory. Then it discards the value, but it sets the
; minus flag based on bit 7 of the result, and it sets the equal
; flag if the result was zero. So this loop will spin until
; the new value of retraces has become different from the old
; value, which indicates that the NMI handler has run.
loop:
cmp retraces
beq loop
; To jump to the instruction after the 'jsr' that called a
; subroutine, use the 'rts' (return from subroutine, or return
; to saved) instruction.
rts
.endproc
; Include a font. Without a font, the NES isn't going to display
; anything.
.segment "CHR"
.incbin "ascii.chr"
Podem fazer download do sores completo, licença WTFPL. É preciso ter instalado o cc65 para assemblar.
Download da rom em ficheiro .nes para correr nos emuladores arvorezinha.nes. Utilizei os emuladores fceux e o nintendulator porque têm debuggers fixes.
Segue aqui um videozinho do que esta arvorezinha é capaz!
[hana-flv-player video='/wp-content/uploads/2009/04/arvorezinha.flv'
width="256"
height="240"
autorewind="true" /]
E já que pus suporte para videos no blol, aqui vai mais um da arvorezinha a correr num emulador de NES da Nintendo DS.
[hana-flv-player video='/wp-content/uploads/2009/04/arvorezinha_nds.flv'
width="176"
height="144" /]
Se alguém se julgar com suficientes pelos no peito, capaz de queimar isto num cartucho e correr numa nes de verdade e filmar só pelo lulz, ofereco-lhe duas internets.
Bem hajas, e até à proxima, talvez agora para a Super Nintendo!
why don't you fuck more and and leave the trees grow
BEST ARVOREZINHA EVAH!
Amei a melhor de sempre mesmo...
tenho a NES e o cartuxo... que se pode matar... se tiveres o know how para tal podemos fazelo numa tarde de caverna...