programação para atari 2600
Post on 01-Dec-2014
68.738 Views
Preview:
DESCRIPTION
TRANSCRIPT
game program™PROGRAMAÇÃO PARA
ATARI 2600Use with Joystick Controllers
Proposta
Entender o que torna o Ataritão diferente de outros sistemas,
aprendendo o básico para escrever um “Hello, World” e poder apreciar clássicos como Enduro ou Pitfall! pela
habilidade de seus criadores
http://slideshare.net/chesterbr
Atari 2600(Video Computer System)
Mais de 600 jogos...imagem: mitchelaneous.com
mas por que eram tão... “Atari”?
Por dentro do Atari (Jr.)
Fotos: Larry Ziegler (2600 CE)
CPU: 6507
Fotos: Larry Ziegler (2600 CE)
CPU: 6507
Fotos: Larry Ziegler (2600 CE)
6502
Video: TIA
Fotos: Larry Ziegler (2600 CE)
Todo o resto: RIOT (6532)
Fotos: Larry Ziegler (2600 CE)
Mapa da Memória
0000-002C – TIA (Escrita)0030-003D – TIA (Leitura)0080-00FF – RIOT (RAM)0280-0297 – RIOT (I/O, Timer)F000-FFFF – Cartucho (ROM)
Mapa da Memória
F000-FFFF – Cartucho (ROM)
esse nem é o maior problema...
4 KBytes!
Mapa da Memória
0080-00FF – RIOT (RAM)
e esse ainda não é o maior problema...
128 BYTES!!!!!(1/8 de KB)
VRAM
Um chip de vídeo típicotransforma padrões de bitsarmazenados em memória(VRAM) em pixels e cores
VRAMVRAM
VRAM
3844447C4444EE00
VRAM
Quanto mais memória (VRAM), maior a resolução, e variedade de cores. Memória era cara nos anos
70/80, levando a um tradeoff.
Quanta VRAM o Atari tem?
Mapa da Memória
0000-002C – TIA (Escrita)0030-003D – TIA (Leitura)0080-00FF – RIOT (RAM)0280-0297 – RIOT (I/O, Timer)F000-FFFF – Cartucho (ROM)
Mapa da Memória
????-???? – VRAM
Mapa da Memória
0 bytes !!!!
????-???? – VRAM
#comofas?
TIA(Television Interface Adaptor)
Funcionamento da TV
Fonte: How Stuff Works
Funcionamento da TV
Fonte: How Stuff Works
Scanlines
Fonte: How Stuff Works
60 quadros(frames)
por segundo
TIA opera em scanlines
Para cada scanline, você escreve emposições de memória do TIA que configuram “objetos desenháveis”
É difícil mudar a cor/forma de um objeto numa mesma scanline
Isso explica
vs.
E que objetos são esses?
● Playfield (PF)● Players (P0, P1)● Missiles/Ball (M0, M1, BL)
Playfield
Um padrão de 20 bits (representando cor de frente e cor de fundo) que ocupa o lado esquerdo da scanline.
O lado direito repete o mesmo padrão, ou, opcionalmente, uma
versão “espelhada” dele
PLAYFIELD
PLAYFIELD
PLAYFIELD
PLAYFIELD
Configurando o playfield
PF0 = 0000 ←← leituraPF1 = 00000000 leitura →→PF2 = 00000000 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 0001 ←← leituraPF1 = 00000000 leitura →→PF2 = 00000000 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 0011 ←← leituraPF1 = 00000000 leitura →→PF2 = 00000000 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 0111 ←← leituraPF1 = 00000000 leitura →→PF2 = 00000000 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 1111 ←← leituraPF1 = 11110000 leitura →→PF2 = 00000000 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 1111 ←← leituraPF1 = 11111110 leitura →→PF2 = 00010101 ←← leituraREFLECT = 0
scanline resultante████████████████████████████████████████
Configurando o playfield
PF0 = 1111 ←← leituraPF1 = 11111110 leitura →→PF2 = 00010101 ←← leituraREFLECT = 1
scanline resultante████████████████████████████████████████
Players
Cada um dos players é um padrão de 8 bits com sua própria cor
Ex.: 10100001 → ████████
Os dois padrões (GRP0/GRP1) podem aparecer na mesma scanline
PLAYERS
PLAYERS
Players
É possível esticar/multiplicar e inverter o desenho de cada player usando os registradores NUSIZn e
REFPn (n=0 ou 1)
NUSIZn (em 5 scanlines)000001010011100101110111
NU
SIZ
n
Ligando o REFPn000001010011100101110111
NU
SIZ
n
NUSIZn
NUSIZn
NUSIZn
NUSIZn
8 bits exigem criatividade
vs.
Missiles/Ball
Cada um representa um pixel na scanline, mas pode ter sua largura
ampliada em 2, 4 ou 8 vezes.
Os missiles têm as cores dos players, enquanto ball tem a cor do playfield.
MISSILES
BALL
BALL
MISSILE
BALL
MISSILE
Idéia geral
Para cada scanline, você configura o formato dos objetos (playfield, players, missiles/ball) e as cores/efeitos deles.
O que você configura em uma scanline vale para as seguintes, mas ainda assim
o tempo é um problema
Contas de padaria:
6502 ≈ 1,19Mhz (1.194.720 ciclos/seg)NTSC: 60 frames (telas) por seg
1.194.720/60 ≅ 19.912 ciclos por tela
Contas de padaria:
CPU: 19.912 ciclos por telaNTSC: 262 scanlines por frame
19.912 / 262 = 76 ciclos por scanline
Contas de padaria:
CPU: 19.912 ciclos por telaNTSC: 262 scanlines por frame
19.912 / 262 = 76 ciclos por scanline
e o que se faz com “76 ciclos”?(aliás, o que exatamente é um “ciclo”?)
Assembly 6502
6502
6502 (no Atari)
Executa instruções armazenadas na ROM que manipulam e transferem bytes entre o RIOT (RAM + I/O + timers) e o TIA, com o apoio de
registradores internos.
Instruções
Cada instrução é composta por um opcode (1 byte) seguido por um
parâmetro (0 a 2 bytes)
Dependendo do opcode, a instrução leva de 2 a 6 ciclos para ser executada
Registradores do 6502
A = Acumulador (8 bits)X, Y= Índices (8 bits)
S = Stack Pointer (8 bits)P = Status (flags, 8 bits)PC = Program Counter (16 bits)
Exemplo de Programa
● Ler o byte da posição de memória 0x0200 para o acumulador (A)
● Somar 1 (um) no A● Guardar o resultado (A) na posição
de memória 0x0201
Código de Máquina 6502AD Opcode (Memória→A)00 2a. Parte de “0200”02 1a. Parte de “0200”69 Opcode (valor+A→A)01 valor “01”8D Opcode (A→Memória)01 2a. Parte de “0201”02 1a. Parte de “0201”
Linguagem Assembly
Atribui a cada opcode uma sigla(“mnemônico”) e define uma notação para os parâmetros
Código de Máquina 6502AD Opcode (Memória→A)00 2a. Parte de “0200”02 1a. Parte de “0200”69 Opcode (valor+A→A)01 valor “01”8D Opcode (A→Memória)01 2a. Parte de “0201”02 1a. Parte de “0201”
Assembly 6502AD LDA $0200000269 ADC #01018D STA $02010102
Assembler (Montador)Programa que lê um arquivo-texto escrito em linguagem Assembly e
monta o arquivo binário (código de máquina) correspondente
foo.asm
LDA $0200ADC #01STA $0201...
foo.bin
AD000269018D0102...
ASSEMBLER
DASM
● Macro Assembler 6502● Inclui headers para Atari● Multiplataforma● Livre (GPLv2)
http://dasm-dillon.sourceforge.net/
Notação (para hoje)
#... = valor absoluto $... = endereço, em hexa $..., X = endereço + X, em hexa #$... = valor absoluto em hexa
http://www.obelisk.demon.co.uk/6502/addressing.html
Instruções do 6502 = mais relevantes para o Atari
Transferindo Dados
LDA, LDX, LDY = LoadSTA, STX, STY = StoreTAX, TAY, TXA,TYA, TSX, TXS = Transfer
LDA #$10 0x10→A STY $0200 Y→m(0x0200)TXA X→A
AritméticaADC, SBC = +,- (C=“vai um”)INC, DEC = ++,-- (memória)INX, INY, DEX, DEY = ++,--
ADC $0100 m(0x100)+A→A INC $0200 m(0x200)+1→
m(0x200)DEX X-1→X
Operações em Bits
AND, ORA, EOR = and, or, xor (A) ASL, LSR = Shift aritmético/lógicoROL, ROR = Shift “rotacional”
AND #$11 A&0x11→ALSR A>>1→A (A/2→A)ROR A>>1 (bit 7=carry)
Comparações e Desvios
CMP, CPX, CPY = compara A/X/Y (-)BCS, BCC = desvia se Carry/NãoBEQ, BNE = desvia se Equal/NãoBVS, BVC = desvia se Overflow/NãoBMI, BPL = desvia se Minus/Plus
CPY $1234 se y=m(0x1234),BEQ $0200 0x0200→PC
Pilha e Subrotinas
JSR, RTS = chama subrotina/retornaPHA, PLA = push/pop(pull) do APHP, PLP = push/pop do status (P)
JMP $1234 0x1234→PCJSR $1234 PC(+3)→pilha,
0x1234→PCRTS pilha→PC
O Resto...
NOP = No Operation (nada!)JMP = Desvio direto (GOTO)SEC, CLC = Set/Clear CarrySEV, CLV = Set/Clear oVerflowSEI, CLI = Set/Clear Interrupt-offSED, CLD = Set/Clear DecimalRTI = Return from InterruptBRK = Break
Neo: “I know kung fu.”Morpheus: “Show me.”
© 1999 Warner Bros
Hello, World!
Hello, World!
Escrever na horizontal é complicado (muitos pixels/elementos por scanline)
Hello, World!
É mais fácil escreverna vertical →
(menos pixels/scanline)
Podemos usar um player ou o playfield
Display kernel
É a parte do programa que roda quando o canhão está desenhando a tela propriamente dita (através do
playfield, players e missiles/ball)
Fonte: Stella Programmers Guide, Steve Wright, 1979
LÓG
ICA
DO
JOG
O(3
+37
+30
).76
= 5
320
cicl
osK
ERN
E L
Estrutura do programaVSYNC
VBLANK
KERNEL(desenha a tela)
OVERSCAN
Estrutura do programaVSYNC
VBLANK
OVERSCAN
Playfield
LoopPrincipal
(eterno)Kernel
loop X: 0 a 191(192 scanlines)
Estrutura do programaVSYNC
VBLANK
OVERSCAN
11 chars x8 linhas x
2 linhas por scanline =
176 scanlines
Começando o programa
PROCESSOR 6502INCLUDE "vcs.h"
ORG $F000 ; Início do cartucho
VSYNC
VBLANK
KERNEL
OVERSCAN
Início do frame (loop principal)
InicioFrame:lda #%00000010 ; VSYNC iniciasta VSYNC ; setando o bit 1REPEAT 3 ; e dura 3 scanlines
sta WSYNC ; (WSYNC = aguarda fim REPEND ; da scanline)lda #0 ; VSYNC finaliza sta VSYNC ; limpando o bit 1
VSYNC
VBLANK
KERNEL
OVERSCAN
Desligando elementos
lda #$00 sta ENABL ; Desliga ball sta ENAM0 ; Desliga missiles sta ENAM1 sta GRP0 ; Desliga players sta GRP1
VSYNC
VBLANK
KERNEL
OVERSCAN
Configurando o Playfield
sta COLUBK ; Cor de fundo (0=preto) sta PF0 ; PF0 e PF2 ficam apagados sta PF2 lda #$FF ; Cor do playfield sta COLUPF ; (possivelmente amarelo) lda #$00 ; Reset no bit 0 do CTRLPF sta CTRLPF ; para duplicar o PF ldx #0 ; X=contador de scanlines
VSYNC
VBLANK
KERNEL
OVERSCAN
VBLANK propriamente dito
REPEAT 37 ; VBLANK dura 37 scanlines,sta WSYNC ; (poderíamos ter lógica
REPEND ; do jogo aqui)lda #0 ; Finaliza o VBLANK, sta VBLANK ; "ligando o canhão"
VSYNC
VBLANK
KERNEL
OVERSCAN
Kernel
Scanline:cpx #174 ; Se acabou a frase, pula bcs FimScanline; o desenho
txa ; Y=X/2 (usando o shiftlsr ; lógico para dividir,tay ; que só opera no A)lda Frase,y ; Frase,Y = mem(Frase+Y) sta PF1 ; PF1 = bits 5 a 11 do
; playfieldVSYNC
VBLANK
KERNEL
OVERSCAN
Kernel (continuação)
FimScanline:sta WSYNC ; Aguarda fim da scanline inx ; Incrementa contador e cpx #191 ; repete até até a bne Scanline ; completar a tela
VSYNC
VBLANK
KERNEL
OVERSCAN
Fechando o loop principal
Overscan:lda #%01000010 ; "Desliga o canhão":sta VBLANK ; 30 scanlines deREPEAT 30 ; overscan...
sta WSYNCREPENDjmp InicioFrame ; ...e começa tudo de
; novo!VSYNC
VBLANK
KERNEL
OVERSCAN
A frase, bit a bit
Frase:.BYTE %00000000 ; H.BYTE %01000010.BYTE %01111110.BYTE %01000010.BYTE %01000010.BYTE %01000010.BYTE %00000000.BYTE %00000000 ; E.BYTE %01111110
...
A frase, bit a bit
....BYTE %00000000 ; D.BYTE %01111000.BYTE %01000100.BYTE %01000010.BYTE %01000010.BYTE %01000100.BYTE %01111000.BYTE %00000000 ; Valor final do PF1
Configurações finais
ORG $FFFA ; Ficam no final da; ROM (cartucho)
.WORD InicioFrame ; Endereço NMI
.WORD InicioFrame ; Endereço BOOT
.WORD InicioFrame ; Endereço BRK
END
Montando e Executandodasm fonte.asm -oromcartucho.bin -f3
http://stella.sourceforge.net/
Técnicas Avançadas
Placar com playfield
Placar com playfield
Para identificar os placares, é possível usar as cores dos players no playfield, setando o bit 1 do
registrador CTRLPF (score mode)
O lado esquerdo fica com a cor do P0, e o direito com a cor do P1
CORES DOS PLAYERS
(isso dá idéias para melhorar nosso Hello World?)
(isso dá idéias para melhorar nosso Hello World?)
Placar com playfield
Problema: como mostrar coisas DIFERENTES em cada lado?
Solução: mudar o playfield enquanto o canhão passa!
canhão
configure o playfield para “3”no início da scanline
quando o canhão estiver no meio,configure o playfield do “1”...
canhão
canhão
...e você terá um desenho diferentedo outro lado!
Mundos gigantes
Pitfall!
Cada uma das 256 telas é definida (objetos, árvores, paredes...) por 1 byte,
que deveriam ser armazenados no cartucho (ROM)
Tamanho da tabela: 256 bytes
Pitfall!
Solução: gerador de sequência com aleatoriedade aceitável e que também gera o valor anterior a partir do atual,
para voltar telas (LFSR bidirecional)
Tamanho do código: 50 bytes
http://en.wikipedia.org/wiki/Linear_feedback_shift_register
River Raid
A mesma solução é aplicada com um gerador de 16 bits (que eventualmente se repete), com pequenos ajustes para
tornar os primeiros setores mais fáceis.
Ao passar a ponte, o jogo guarda o valor atual, recuperando em caso de morte para voltar no mesmo setor
Posição horizontal
Posição horizontal
Não existe um registrador paradetermine a posição horizontal de
players, missiles ou ball
Você tem que contar o tempoaté que o canhão esteja na posiçãoe acionar o strobe correspondente
PONTOS DE STROBE(na teoria)
Dá pra calcular...
1 ciclo de CPU = 3 pixelsWSYNC = 20 ciclos
posição x ≈ (ciclos – 20) * 3
...mas é aproximado, porque o TIA só lê os registros a cada 5 ciclos de CPU,
tornando inviável para movimento ↔
SoluçõesVocê pode mover player, missile ou
ball relativamente à posição anterior, usando um registrador de 4 bits
(isto é, movendo de -7 a +8 pixels)
E o missile pode ser posicionado no meio do player correspondente,
tornando fácil “atirar” ele (daí o nome)
PONTOS DE STROBE
MOVIMENTO ↕ basta desenhar o player/missle emuma scanline diferente a cada frame
MOVIMENTO ↔registradores HMP0/1 e HMM0/1
Placar com múltiplos dígitos
Placar com múltiplos dígitos
O truque é o mesmo do placar com playfield: mudar a imagem com o
canhão andando, mas o timing tem que ser muito mais preciso
Digamos que o placar seja 456789...
Placar com múltiplos dígitosComece cada scanline com a linha do 4 no GRP0 e do 5 no GRP1.
Configure NUSIZ0 e NUSIZ1 para repetir três vezes:
4 4 4 5 5 5
Player 0 Player 1
Placar com múltiplos dígitos
Posicione o player 1 à direita do player 0, encavalando as cópias:
454545
Player 0
Player 1
Placar com múltiplos dígitos
Troque o desenho dos players (GRP0/GRP1) sincronizando com o
canhão, assim:
CANHÃO
454545
Placar com múltiplos dígitos
Quando o canhão estiver terminando a 1ª cópia do player 1, altere o player
0 para 6 e o player 1 para 7:
CANHÃO
454545
Placar com múltiplos dígitos
Repita o truque ao final da 2ª cópia do player 2, dessa vez alternado o
player 0 para 8 e o player 1 para 9
CANHÃO
456767
Placar com múltiplos dígitos
Faça a mesma coisa paracada scanline do placar!
CANHÃO
456789
Placar com múltiplos dígitos
É mais difícil do que parece: não dá tempo de carregar bitmaps da memória quando o canhão passa, e só temos 3 registradores para guardar 4 dígitos...
...mas é isso que torna divertido!
Conclusões
Tirando leite de pedra
Quando observar um jogo de Atari, tente identificar os truques que o(a) programador(a) usou: como dividiu a
tela, o que tem em cada scanline, como gastou a RAM e a ROM...
Mãos à obra!
Você pode fazer seu jogo de Atari – é um desafio de programação divertido!
Será preciso estudar várias coisas que não detalhamos: contagem de ciclos, som, leitura de joysticks... mas dá!
Para aprender maisO nosso Hello, World: http://pastebin.com/abBRfUjdSorteio 2600 http://github.com/chesterbr/sorteio2600Racing The Beam (livro): http://bit.ly/dSqhjS Palestra David Crane (Pitfall): http://youtu.be/MBT1OK6VAIU Tutoriais do Crane para iOS: http://bit.ly/9pwYHs e http://bit.ly/qWBciZ Stella Programmer's Guide: http://emu-docs.org/?page=Atari%202600Código-fonte de jogos clássicos: http://classicdev.org/wiki/2600/Source_Code Especificações do Atari: http://nocash.emubase.de/2k6specs.htm Referência 6502: http://bit.ly/hxG5c6 Emulador no browser: http://jogosdeatari.com.br/ Tutorial Andrew Dave: http://bit.ly/ptQDdA (o site todo é bom)Cartucho com leitor de SD: http://harmony.atariage.com/BAtari (compilador BASIC): http://bataribasic.comExemplos de som no TIA: http://bit.ly/tnbPrp Bankswitching (mais ROM/RAM): http://bit.ly/tqhLZk
Dúvidas?Obrigado!
@chesterbr
http://slideshare.net/chesterbrhttp://chester.me
Créditos e LicenciamentoEsta apresentação está licenciada sob os termos da
licença Creative Commons “by-nc” 3.0,observadas as exceções abaixo
O slide de abertura é baseado em ilustração © 2011 Ila Fox,licenciada exclusivamente para o autor e não inclusa na licença acima
Fotos e ilustrações de terceiros usados sob premissa de “fair use” têm sua autoria mencionada e também excluídos da licença acima
Atari™, Adventure™, Donkey Kong™, Pitfall™, Super Mario™ e outros personagens/jogos citados para fins ilustrativos, bem como suas imagens
e logomarcas, são de propriedade de seus detentores, com todos os direitos reservados, não havendo qualquer relação deles com o autor
top related