game program™
PROGRAMAÇÃO PARA
    ATARI 2600
 Use with Joystick Controllers
Proposta
    Entender o que torna o Atari
  tã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
Palestrante




                   @chesterbr
              http://chester.me
Atari 2600
(Video Computer System)
imagem: mitchelaneous.com
                            Mais de 600 jogos...
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 2
     650




Fotos: Larry Ziegler (2600 CE)
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


    4 KBytes!



F000-FFFF – Cartucho (ROM)

esse nem é o maior problema...
Mapa da Memória
                   128 BYTES!!!!!
                    (1/8 de KB)

  0080-00FF – RIOT (RAM)



e esse ainda não é o maior problema...
VRAM

  Um chip de vídeo típico
transforma padrões de bits
armazenados em memória
(VRAM) em pixels e cores
VRAM
       VRAM
       VRAM
        38
        44
        44
        7C
        44
        44
        EE
        00
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

             60 quadros
              (frames)
            por segundo




     Fonte: How Stuff Works
TIA opera em scanlines
Para cada scanline, você escreve em
 posiçõ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           ←← leitura
   PF1 = 00000000       leitura →→
   PF2 = 00000000       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 0001           ←← leitura
   PF1 = 00000000       leitura →→
   PF2 = 00000000       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 0011           ←← leitura
   PF1 = 00000000       leitura →→
   PF2 = 00000000       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 0111           ←← leitura
   PF1 = 00000000       leitura →→
   PF2 = 00000000       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 1111           ←← leitura
   PF1 = 11110000       leitura →→
   PF2 = 00000000       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 1111           ←← leitura
   PF1 = 11111110       leitura →→
   PF2 = 00010101       ←← leitura
   REFLECT = 0

          scanline resultante
████████████████████████████████████████
Configurando o playfield
   PF0 = 1111           ←← leitura
   PF1 = 11111110       leitura →→
   PF2 = 00010101       ←← leitura
   REFLECT = 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)
         000
         001
         010
NUSIZn




         011
         100
         101
         110
         111
Ligando o REFPn
         000
         001
         010
NUSIZn




         011
         100
         101
         110
         111
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 tela
  NTSC: 262 scanlines por frame

19.912 / 262 = 76 ciclos por scanline
Contas de padaria:

   CPU: 19.912 ciclos por tela
  NTSC: 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 6502
 AD   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 6502
 AD   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 6502
AD   LDA $0200
00
02
69   ADC #01
01
8D   STA $0201
01
02
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             foo.bin
LDA $0200 ASSEMBLER AD000269
ADC #01             018D0102
STA $0201           ...
...
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 = Load
  STA, STX, STY = Store
  TAX, TAY, TXA,
  TYA, TSX, TXS = Transfer
LDA #$10     0x10→A
STY $0200    Y→m(0x0200)
TXA          X→A
Aritmética
 ADC, 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ógico
ROL, ROR = Shift “rotacional”

AND #$11 A&0x11→A
LSR      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ão
BEQ, BNE = desvia se Equal/Não
BVS, BVC = desvia se Overflow/Não
BMI, BPL = desvia se Minus/Plus
CPY $1234    se y=m(0x1234),
BEQ $0200      0x0200→PC
Pilha e Subrotinas
JSR, RTS = chama subrotina/retorna
PHA, PLA = push/pop(pull) do A
PHP, PLP = push/pop do status (P)

JMP $1234     0x1234→PC
JSR $1234     PC(+3)→pilha,
              0x1234→PC
RTS           pilha→PC
O Resto...
NOP        = No Operation (nada!)
JMP        = Desvio direto (GOTO)
SEC, CLC   = Set/Clear Carry
SEV, CLV   = Set/Clear oVerflow
SEI, CLI   = Set/Clear Interrupt-off
SED, CLD   = Set/Clear Decimal
RTI        = Return from Interrupt
BRK        = Break
© 1999 Warner Bros




Neo: “I know kung fu.”
Morpheus: “Show me.”
Hello, World!
Hello, World!

 Escrever na horizontal é complicado
(muitos pixels/elementos por scanline)
Hello, World!
 É mais fácil escrever
    na 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)
(3+37+30).76 = 5320 ciclos
LÓGICA DO JOGO




                                                                                           KERNEL
                                          Fonte: Stella Programmers Guide, Steve Wright, 1979
Estrutura do programa
           VSYNC


         VBLANK


         KERNEL
      (desenha a tela)


       OVERSCAN
Estrutura do programa
              VSYNC


Playfield    VBLANK




            OVERSCAN
Estrutura do programa
              VSYNC


             VBLANK

 Loop         11 chars x
Principal      8 linhas x
 (eterno)     2 linhas por       Kernel
               scanline =     loop X: 0 a 191
                  176        (192 scanlines)
              scanlines
            OVERSCAN
Começando o programa
PROCESSOR 6502
INCLUDE "vcs.h"

ORG $F000   ; Início do cartucho




                                   VSYNC
                                VBLANK

                                KERNEL
                               OVERSCAN
Início do frame (loop principal)
 InicioFrame:
   lda #%00000010   ;   VSYNC inicia
   sta VSYNC        ;     setando o bit 1
   REPEAT 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 shift
  lsr            ;     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
                 ;     playfield        VSYNC
                                        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 de
  REPEAT 30         ;   overscan...
    sta WSYNC
  REPEND
  jmp 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 Executando
dasm 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
canhão

quando o canhão estiver no meio,
  configure o playfield do “1”...
o
                            canhã

...e você terá um desenho diferente
            do 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 para
determine a posição horizontal de
     players, missiles ou ball
  Você tem que contar o tempo
até que o canhão esteja na posição
e acionar o strobe correspondente
PONTOS DE STROBE
    (na teoria)
Dá pra calcular...
      1 ciclo de CPU = 3 pixels
         WSYNC = 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ções
  Você 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 ↔
 registradores HMP0/1 e HMM0/1




     MOVIMENTO ↕
 basta desenhar o player/missle em
uma scanline diferente a cada frame
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ígitos
 Comece 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 P1 8 pixels à direita do
      P0, encavalando as cópias:
                          Player 1

            454545

       Player 0
Placar com múltiplos dígitos

   Troque o desenho da scanline
 (GRP0/GRP1) sincronizando com o
          canhão, assim:

          454545

             CANHÃO
Placar com múltiplos dígitos

Quando o canhão estiver terminando
 a 1ª cópia do P2, altere o P1 para
        6 e o P2 para 7:

          454545

              CANHÃO
Placar com múltiplos dígitos

Repita o truque ao final da 2ª cópia
do P2, dessa vezalterando o P1 para
         8 e o P2 para 9

          456767

              CANHÃO
Placar com múltiplos dígitos

     Faça a mesma coisa para
     cada scanline do placar!


         456789

             CANHÃO
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 mais
O nosso Hello, World:              http://pastebin.com/abBRfUjd
Sorteio 2600                       http://github.com/chesterbr/sorteio2600
Racing 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%202600
Có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.com
Exemplos de som no TIA:            http://bit.ly/tnbPrp
Bankswitching (mais ROM/RAM):      http://bit.ly/tqhLZk
Dúvidas?
Obrigado!

                      @chesterbr
                 http://chester.me
   http://slideshare.net/chesterbr
Créditos e Licenciamento
          Esta 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

Programação para Atari 2600

  • 1.
    game program™ PROGRAMAÇÃO PARA ATARI 2600 Use with Joystick Controllers
  • 2.
    Proposta Entender o que torna o Atari tã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
  • 3.
    Palestrante @chesterbr http://chester.me
  • 4.
  • 5.
    imagem: mitchelaneous.com Mais de 600 jogos...
  • 6.
    mas por queeram tão... “Atari”?
  • 7.
    Por dentro doAtari (Jr.) Fotos: Larry Ziegler (2600 CE)
  • 8.
    CPU: 6507 Fotos: LarryZiegler (2600 CE)
  • 9.
    CPU: 6507 2 650 Fotos: Larry Ziegler (2600 CE)
  • 10.
    Video: TIA Fotos: LarryZiegler (2600 CE)
  • 11.
    Todo o resto:RIOT (6532) Fotos: Larry Ziegler (2600 CE)
  • 12.
    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)
  • 13.
    Mapa da Memória 4 KBytes! F000-FFFF – Cartucho (ROM) esse nem é o maior problema...
  • 14.
    Mapa da Memória 128 BYTES!!!!! (1/8 de KB) 0080-00FF – RIOT (RAM) e esse ainda não é o maior problema...
  • 15.
    VRAM Umchip de vídeo típico transforma padrões de bits armazenados em memória (VRAM) em pixels e cores
  • 16.
    VRAM VRAM VRAM 38 44 44 7C 44 44 EE 00
  • 17.
    VRAM Quanto maismemó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?
  • 18.
    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)
  • 19.
  • 20.
    Mapa da Memória 0 bytes !!!! ????-???? – VRAM #comofas?
  • 21.
  • 22.
    Funcionamento da TV Fonte: How Stuff Works
  • 23.
    Funcionamento da TV Fonte: How Stuff Works
  • 24.
    Scanlines 60 quadros (frames) por segundo Fonte: How Stuff Works
  • 25.
    TIA opera emscanlines Para cada scanline, você escreve em posições de memória do TIA que configuram “objetos desenháveis” É difícil mudar a cor/forma de um objeto numa mesma scanline
  • 26.
  • 27.
    E que objetossão esses? ● Playfield (PF) ● Players (P0, P1) ● Missiles/Ball (M0, M1, BL)
  • 28.
    Playfield Um padrão de20 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
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
    Configurando o playfield PF0 = 0000 ←← leitura PF1 = 00000000 leitura →→ PF2 = 00000000 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 34.
    Configurando o playfield PF0 = 0001 ←← leitura PF1 = 00000000 leitura →→ PF2 = 00000000 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 35.
    Configurando o playfield PF0 = 0011 ←← leitura PF1 = 00000000 leitura →→ PF2 = 00000000 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 36.
    Configurando o playfield PF0 = 0111 ←← leitura PF1 = 00000000 leitura →→ PF2 = 00000000 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 37.
    Configurando o playfield PF0 = 1111 ←← leitura PF1 = 11110000 leitura →→ PF2 = 00000000 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 38.
    Configurando o playfield PF0 = 1111 ←← leitura PF1 = 11111110 leitura →→ PF2 = 00010101 ←← leitura REFLECT = 0 scanline resultante ████████████████████████████████████████
  • 39.
    Configurando o playfield PF0 = 1111 ←← leitura PF1 = 11111110 leitura →→ PF2 = 00010101 ←← leitura REFLECT = 1 scanline resultante ████████████████████████████████████████
  • 41.
    Players Cada um dosplayers é um padrão de 8 bits com sua própria cor Ex.: 10100001 → ████████ Os dois padrões (GRP0/GRP1) podem aparecer na mesma scanline
  • 42.
  • 43.
  • 44.
    Players Épossível esticar/multiplicar e inverter o desenho de cada player usando os registradores NUSIZn e REFPn (n=0 ou 1)
  • 45.
    NUSIZn (em 5scanlines) 000 001 010 NUSIZn 011 100 101 110 111
  • 46.
    Ligando o REFPn 000 001 010 NUSIZn 011 100 101 110 111
  • 47.
  • 49.
  • 50.
  • 51.
  • 52.
    8 bits exigemcriatividade vs.
  • 53.
    Missiles/Ball Cadaum 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.
  • 54.
  • 55.
  • 56.
    BALL MISSILE
  • 57.
    BALL MISSILE
  • 58.
    Idéia geral Paracada 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
  • 59.
    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
  • 60.
    Contas de padaria: CPU: 19.912 ciclos por tela NTSC: 262 scanlines por frame 19.912 / 262 = 76 ciclos por scanline
  • 61.
    Contas de padaria: CPU: 19.912 ciclos por tela NTSC: 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”?)
  • 62.
  • 64.
  • 65.
    6502 (no Atari) Executainstruçõ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.
  • 66.
    Instruções Cadainstruçã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
  • 67.
    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)
  • 68.
    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
  • 69.
    Código de Máquina6502 AD 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”
  • 71.
    Linguagem Assembly Atribui acada opcode uma sigla (“mnemônico”) e define uma notação para os parâmetros
  • 72.
    Código de Máquina6502 AD 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”
  • 73.
    Assembly 6502 AD LDA $0200 00 02 69 ADC #01 01 8D STA $0201 01 02
  • 74.
    Assembler (Montador) Programa quelê um arquivo-texto escrito em linguagem Assembly e monta o arquivo binário (código de máquina) correspondente foo.asm foo.bin LDA $0200 ASSEMBLER AD000269 ADC #01 018D0102 STA $0201 ... ...
  • 75.
    DASM ● Macro Assembler6502 ● Inclui headers para Atari ● Multiplataforma ● Livre (GPLv2) http://dasm-dillon.sourceforge.net/
  • 76.
    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
  • 77.
    Instruções do 6502 = mais relevantes para o Atari
  • 78.
    Transferindo Dados LDA, LDX, LDY = Load STA, STX, STY = Store TAX, TAY, TXA, TYA, TSX, TXS = Transfer LDA #$10 0x10→A STY $0200 Y→m(0x0200) TXA X→A
  • 79.
    Aritmética ADC, 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
  • 80.
    Operações em Bits AND,ORA, EOR = and, or, xor (A) ASL, LSR = Shift aritmético/lógico ROL, ROR = Shift “rotacional” AND #$11 A&0x11→A LSR A>>1→A (A/2→A) ROR A>>1 (bit 7=carry)
  • 81.
    Comparações e Desvios CMP,CPX, CPY = compara A/X/Y (-) BCS, BCC = desvia se Carry/Não BEQ, BNE = desvia se Equal/Não BVS, BVC = desvia se Overflow/Não BMI, BPL = desvia se Minus/Plus CPY $1234 se y=m(0x1234), BEQ $0200 0x0200→PC
  • 82.
    Pilha e Subrotinas JSR,RTS = chama subrotina/retorna PHA, PLA = push/pop(pull) do A PHP, PLP = push/pop do status (P) JMP $1234 0x1234→PC JSR $1234 PC(+3)→pilha, 0x1234→PC RTS pilha→PC
  • 83.
    O Resto... NOP = No Operation (nada!) JMP = Desvio direto (GOTO) SEC, CLC = Set/Clear Carry SEV, CLV = Set/Clear oVerflow SEI, CLI = Set/Clear Interrupt-off SED, CLD = Set/Clear Decimal RTI = Return from Interrupt BRK = Break
  • 84.
    © 1999 WarnerBros Neo: “I know kung fu.” Morpheus: “Show me.”
  • 85.
  • 86.
    Hello, World! Escreverna horizontal é complicado (muitos pixels/elementos por scanline)
  • 87.
    Hello, World! Émais fácil escrever na vertical → (menos pixels/scanline) Podemos usar um player ou o playfield
  • 88.
    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)
  • 89.
    (3+37+30).76 = 5320ciclos LÓGICA DO JOGO KERNEL Fonte: Stella Programmers Guide, Steve Wright, 1979
  • 90.
    Estrutura do programa VSYNC VBLANK KERNEL (desenha a tela) OVERSCAN
  • 91.
    Estrutura do programa VSYNC Playfield VBLANK OVERSCAN
  • 92.
    Estrutura do programa VSYNC VBLANK Loop 11 chars x Principal 8 linhas x (eterno) 2 linhas por Kernel scanline = loop X: 0 a 191 176 (192 scanlines) scanlines OVERSCAN
  • 93.
    Começando o programa PROCESSOR6502 INCLUDE "vcs.h" ORG $F000 ; Início do cartucho VSYNC VBLANK KERNEL OVERSCAN
  • 94.
    Início do frame(loop principal) InicioFrame: lda #%00000010 ; VSYNC inicia sta VSYNC ; setando o bit 1 REPEAT 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
  • 95.
    Desligando elementos lda #$00 sta ENABL ; Desliga ball sta ENAM0 ; Desliga missiles sta ENAM1 sta GRP0 ; Desliga players sta GRP1 VSYNC VBLANK KERNEL OVERSCAN
  • 96.
    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
  • 97.
    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
  • 98.
    Kernel Scanline: cpx#174 ; Se acabou a frase, pula bcs FimScanline; o desenho txa ; Y=X/2 (usando o shift lsr ; 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 ; playfield VSYNC VBLANK KERNEL OVERSCAN
  • 99.
    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
  • 100.
    Fechando o loopprincipal Overscan: lda #%01000010 ; "Desliga o canhão": sta VBLANK ; 30 scanlines de REPEAT 30 ; overscan... sta WSYNC REPEND jmp InicioFrame ; ...e começa tudo de ; novo! VSYNC VBLANK KERNEL OVERSCAN
  • 101.
    A frase, bita bit Frase: .BYTE %00000000 ; H .BYTE %01000010 .BYTE %01111110 .BYTE %01000010 .BYTE %01000010 .BYTE %01000010 .BYTE %00000000 .BYTE %00000000 ; E .BYTE %01111110 ...
  • 102.
    A frase, bita bit ... .BYTE %00000000 ; D .BYTE %01111000 .BYTE %01000100 .BYTE %01000010 .BYTE %01000010 .BYTE %01000100 .BYTE %01111000 .BYTE %00000000 ; Valor final do PF1
  • 103.
    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
  • 104.
    Montando e Executando dasmfonte.asm -oromcartucho.bin -f3 http://stella.sourceforge.net/
  • 105.
  • 106.
  • 107.
    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
  • 108.
  • 109.
    (isso dá idéiaspara melhorar nosso Hello World?)
  • 110.
    (isso dá idéiaspara melhorar nosso Hello World?)
  • 111.
    Placar com playfield Problema:como mostrar coisas DIFERENTES em cada lado? Solução: mudar o playfield enquanto o canhão passa!
  • 112.
    canhã o configure o playfield para “3” no início da scanline
  • 113.
    canhão quando o canhãoestiver no meio, configure o playfield do “1”...
  • 114.
    o canhã ...e você terá um desenho diferente do outro lado!
  • 115.
  • 116.
    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
  • 117.
    Pitfall! Solução: geradorde 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
  • 118.
    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
  • 119.
  • 120.
    Posição horizontal Nãoexiste um registrador para determine a posição horizontal de players, missiles ou ball Você tem que contar o tempo até que o canhão esteja na posição e acionar o strobe correspondente
  • 121.
    PONTOS DE STROBE (na teoria)
  • 122.
    Dá pra calcular... 1 ciclo de CPU = 3 pixels WSYNC = 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 ↔
  • 123.
    Soluções Você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)
  • 124.
  • 125.
    MOVIMENTO ↔ registradoresHMP0/1 e HMM0/1 MOVIMENTO ↕ basta desenhar o player/missle em uma scanline diferente a cada frame
  • 126.
  • 127.
    Placar com múltiplosdí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...
  • 128.
    Placar com múltiplosdígitos Comece 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
  • 129.
    Placar com múltiplosdígitos Posicione o P1 8 pixels à direita do P0, encavalando as cópias: Player 1 454545 Player 0
  • 130.
    Placar com múltiplosdígitos Troque o desenho da scanline (GRP0/GRP1) sincronizando com o canhão, assim: 454545 CANHÃO
  • 131.
    Placar com múltiplosdígitos Quando o canhão estiver terminando a 1ª cópia do P2, altere o P1 para 6 e o P2 para 7: 454545 CANHÃO
  • 132.
    Placar com múltiplosdígitos Repita o truque ao final da 2ª cópia do P2, dessa vezalterando o P1 para 8 e o P2 para 9 456767 CANHÃO
  • 133.
    Placar com múltiplosdígitos Faça a mesma coisa para cada scanline do placar! 456789 CANHÃO
  • 134.
    Placar com múltiplosdí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!
  • 135.
  • 136.
    Tirando leite depedra 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...
  • 137.
    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á!
  • 138.
    Para aprender mais Onosso Hello, World: http://pastebin.com/abBRfUjd Sorteio 2600 http://github.com/chesterbr/sorteio2600 Racing 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%202600 Có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.com Exemplos de som no TIA: http://bit.ly/tnbPrp Bankswitching (mais ROM/RAM): http://bit.ly/tqhLZk
  • 139.
    Dúvidas? Obrigado! @chesterbr http://chester.me http://slideshare.net/chesterbr
  • 140.
    Créditos e Licenciamento Esta 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