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...