# Desenvolvimento de jogos/Programação Megadrive
## Introdução

Este wikibook destina-se àqueles que estejam interessados no desenvolvimento de jogos para o videogame conhecido como Mega Drive.
(Em construção)

## Ferramentas
### Montadores

Também conhecidos pelo termo inglês assemblers, geram o código de máquina referente ao código fonte escrito na linguagem de montagem (assembly). No caso do Mega Drive, o código é escrito para os processadores Motorola 68000 (também conhecido como M68k ou M68000) e Z80, da Zilog, sendo o Z80 um coprocessador de áudio e para "compatibilidade" com jogos de Master System.

#### GAS

O GNU Assembler (GAS) . Ele é bem completo, pleno de recursos e genérico quanto a processador. Sua sintaxe segue o padrão AT&T em vez do da Intel. Caso seja necessário compilar o GAS para o M68k, utilize os seguintes comandos:
O primeiro comando configura o alvo de montagem ser o processador família M68k, sem sistema operacional. O segundo efetivamente compila os programas componentes do GAS, que são:
* addr2line
* ar
* as
* cxxfilt
* ld
* nm
* objcopy
* objdump
* ranlib
* readelf
* size
* strings
* strip
Os mais usados, para fins de programas para Mega Drive, são o as (o montador/assembler), o ld (o ligador/linker) e o objcopy (para gerar o arquivo no formato binário correto).

##### Usando o as

É importante o uso do parâmetro -m68000, porque o padrão é 68020. O parâmetro -o serve para definir o nome do arquivo objeto de saída (o padrão é "a.out"). Por fim, o não opcional: o código-fonte.

##### Usando o ld

Após feita as compilações de todas os códigos do programa, é preciso ligá-los.
* --oformat=binary e -Ttext=0 são necessários para o correto formato do arquivo binário.
* -o, como já mencionado para o as, define o nome do arquivo binário de saída, no caso "rom.bin"
* O parâmetro obrigatório para o ld é, obviamente, o(s) arquivo(s) objeto(s) que compõe o programa.

### Compiladores
#### BasiEgaXorz

Compilador de BASIC criado por Devster para Mega Drive, muito fácil de usar, e vem com muitos exemplos.
Pode ser baixado neste site

#### SGCC - Sega Genesis C Compiler

Compilador de C para Mega Drive. Embora tenha alguns problemas, como o fato de usar um dialeto arcaico da linguagem C, (Sintaxe K&R, para ser mais exato), ele é extremamente simples de usar, bastando descompactar e usar, além de já vir com alguns exemplos simples.
Pode ser baixado deste site, ou deste

#### GCC68k - GNU C Compiler for 68k

Versão do GNU C adaptado para o processador MC68000. Suporta tanto C como C++, podendo também suportar outras linguagens, como Fortran, Pascal e outros, dependendo da configuração. Tem a desvantagem de ser ligeiramente mais difícil de usar do que o SGCC.
Neste site, você pode baixar uma versão deste compilador já preparada para o Mega Drive.

#### SGDK - Kit de Desenvolvimento para o Sega Genesis / Sega Genesis Development Kit

Kit de bibliotecas e toolchains portados por Stephane Dalongeville para Mega Drive. Seu uso é bastante simples e também vem com alguns exemplos. O diretório contém alguns tutoriais de funções básicas e intermediárias.
Pode ser baixado por aqui.

### Conversores de imagens
#### ImaGenesis

Também criado por Devster (o mesmo criador de BasiEgaXorz), esse programa converte imagens para o formato de 16 cores do Mega Drive.
Pode ser baixado por aqui.

## Básico

Para os exemplos desta seção, será utilizado o BasiEgaXorz, por ser mais simples de usar.

### Escrevendo texto na tela

Usando o BasiEgaXorz, esta tarefa se torna bastante simples:

### Definindo a palheta de cores

O Megadrive possui quatro palhetas de cores, cada uma com 16 cores, selecionáveis dentre 512.
O BasiEgaXorz possui dois comandos para setar as palhetas do Megadrive:
PALLETTE seta uma única cor de uma única palheta. A sua sintaxe é:
* <código RGB> é um número hexadecimal que repesenta os tons de vermelho, verde e azul que compõem a cor desejada. Para calcular o código equivalente à cor, utilize a fórmula (R*2)+(G*32)+(B*512), onde:
** R representa o tom de vermelho, entre 0 e 7
** G representa o tom de verde, entre 0 e 7
** B representa o tom de azul, entre 0 e 7
* <n° da palheta> indica o número da palheta que se deseja modificar
* <n° da cor> indica qual das cores desta palheta será alterada
Exemplo:
O exemplo acima imprime uma linha de texto com cada palheta, espera o usuário pressionar qualquer botão do joystick, e então altera a cor 1 de cada palheta, a qual corresponde à cor utilizada pelo texto; como consequência, o texto muda de cor.
PALLETTES pode setar várias cores de uma determinada palheta de uma só vez. A sua sintaxe é:
* <rótulo dos dados> indica qual rótulo de dados será lido para definir as cores da palheta indicada (veja o exemplo abaixo para entender melhor)
* <n° da palheta> número da palheta que será alterada
* <cor inicial> cor inicial a ser alterada. Por exemplo, se a cor inicial for 4 e o número de cores for 3, serão alteradas as cores 4, 5 e 6
* <n° de cores> o número de cores que serão alteradas
* [Deslocamento] parâmetro opcional que indica a posição dentro dos dados indicados pelo rótulo a partir da qual os dados serão lidos.
Exemplo:
No exemplo acima, pallette1, palette2, palette3 e palette4 são rótulos que apontam para dados, os quais, neste caso, são definidos pelo comando DATAINT. O cifrão na frente de cada número serve para indicar que o número em questão está em notação hexadecimal. Isso torna mais fácil visualizar a cor que está sendo definida, visto que os três últimos dígitos do código da cor em hexa correspondem justamente aos valores de azul, verde e vermelho, respectivamente. Observe, também, que a cor do fundo também foi alterada, porque a cor zero da palheta zero também mudou.
Importante: Se for copiar e colar este exemplo para o BasiEgaXorz, lembre-se de tirar o espaço à esquerda dos rótulos, mas apenas dos rótulos. Isto é necessário, porque o BasiEgaXorz pressupõe que qualquer linha que comece com um ou mais espaços à esquerda não contenha um rótulo.

### Lendo o joystick

Existem algumas funções para o BasiEgaXorz que fazem o software interagir com o joystick, vou lista-las primeiramente, e a seguir dou um exemplo mais prático.

#### WaitPadUp <# do controle>

Espera que o device do numero especificado seja ligado ao console. Você pode fazer o jogo pausar se o controle for removido, ou caso o jogador selecione a opção de 2 players, verificar a existencia de um segundo controle para prosseguir.
Importante: os valores para o primeiro e o segundo controle são 0 e 1 respectivamente.

#### Joypad(<# do controle>)

Essa função de longe é a mais importante para interação com o controle. Essa função retorna o bit do evento correspondente. A tabela a baixo mostra a relação de Bit-Evento.
Não vou entrar muito em detalhes, vocês podem procurar no site da BasiEgaXorz por uma documentação mais detalhada, mas quando se utiliza joystick de 3 botoes os bits referentes aos botões ZYX não estarão 'limpos', e sim tera um valor randomico qualquer, I.E, lixo. Tomar cuidado com isso.
Vamos ao exemplo:
Cole o exemplo abaixo no BasiEgaXorz, lembrando que deve haver um espaço em branco antes de cada comando. Reparem que foi utilizado joypad sem nenhum parâmetro, caso este esteja omitido o padrão é o primeiro controle.
O ideal é utilizar select-case, ao invés de estrutura if-else por questões de desempenho, além de produzir uma melhor legibilidade ao código. No caso do select utilize os valores decimais da tabela. Caso queiram capturar dois botoes por exemplo, some os valores. Um exemplo com as 4 diagonais.

### Movendo objetos pela tela

Utilizando o loop do exemplo acima que lê os comandos do controle. As unicas adições des comandos foram o addsprite, propsprite, movesprite, e loadtiles. Irei comentar no codigo mesmo a função de cada coisa, caso alguem queria criar um tópico explicando cada comando, fique a vontade. Explicando superficialmente sprites são aquelas sequencias de imagens que permite fazer a ilusao de um personagem em movimento. É possivel, e comumente utilizado mas no exemplo ficou faltando, de trocar as imagens do sprite dado o movimento.

### Exibindo uma imagem no fundo da tela

Sei que a explicação não está muito didática mas estou tentando postar aqui conforme tenho tempo. Pela falta de documentação e exemplo em portugues, vamos a mais um exemplo pratico. Vou usar praticamente tudo do exemplo anterior, espero que tenha ficado claro.
Um pouco de teoria antes do exemplo. No mega drive existe 3 planos SCROLL_A, SCROLL_B, E WINDOW. O plano SCROLL_B, possui a menor prioridade de todas, seguido pelo SCROLL_A, e por final WINDOW. Manipulando esses 3 planos é que damos a noção de 'profundidade' ao jogo, um elemento mais 'proximo', I.E, em um plano de maior prioridade, irá tapar o outro criando essa ilusao. O mais importante é os spirtes terao uma prioridade mais alta, por isso nem me dei ao trabalho de atribuir os planos no exemplo a seguir. Existem mais comandos do que os que vou listar a baixo, recomendo ir buscar a documentação do BasiEgaXorz, vou explicar o basico só.
Você pode usar os comandos:

#### SETTEXTPLANE <SCROLL_A/SCROLL_B/WINDOW>

Esse comando torna padrão para o ASCII, comandos tipo print e etc.

#### SETGFXPLANE <SCROLL_A/SCROLL_B/WINDOW>

Esse comando permite escolher qual será o plano padrão dos comandos de desenho, drawtile, drawtiles.

#### SETSCROLLPLANE <SCROLL_A/SCROLL_B>

Escolhe qual plano será 'rolado'.
Esse não é um topico sobre scroll, estou explicando por que utilizei no codigo fonte e creio que ficará mais claro agora. Para adicionar as as imagens ao 'fundo', isto é imagens de um determinado plano. Existe varios comandos comando drawtile, drawtiles, drawtilesovr, drawtilesinc, DrawTilesOvr, drawtilesinc2. Por hora eu não vou entrar em detalhes nesses varios comandos, vou explicar somente o drawtile (depois eu edito o topico com o resto dos comandos).

#### DRAWTILE <POSICAO DA MEMORIA>, <X DA TELA>, <Y DA TELA>

Desenha o determinado tile na tela, na posicão x,y. Com um for você pode preencher toda tela com a mesma imagem, ou armazenar varios tiles que montem uma imagem maior e monta-la também com um for, sem maiores grandes problemas.
Dito isso, vamos ao exemplo prático. Boa parte do código foi retirado do exemplo acima então não terão dificuldade de entender a maioria das coisas, se entenderam o anterior.
OBS: Qualquer editor, revisor, fique a vontade para modificar ou deixar mais claro o conteudo.

### Seu primeiro jogo: Como fazer um clone do "Pong"

Pong sem dúvida é um grande exemplo de jogo, devido sua simplicidade é um ótimo exercício para quem está começando a programar. A título de informação foi o primeiro video game lucrativo da historia. Se não sabe do que se trata, esperto que essa imagem possa esclarecer.
Para começar vamos montar a estrutura do arquivo em BEX e ir progressivamente completando a cada tópico. Para começar a explicação gostaria de falar que os você deve escolher uma posição de memoria em que os tiles serão armazenados. Contudo, da posição 0 até 255, já estão preenchidos com a tabela ascii . Eu irei fazer uso desses caracteres para desenhar a tela com o comando drawtile e passando o endereço deles ao comando.
Nosso ponto de "partida" será o exemplo abaixo, é importante que entenda o que está acontecendo no código para prosseguir. Por isso está com muitos comentários. Além disso há várias substituições que podem ser feitas para deixar o código mais otimizado, mas acredito que prejudicaria o entendimento, por isso vai assim mesmo. Quando esse exemplo foi feito, o BasiEgaXorz não dava suporte número negativo, as últimas versões já o fazem.

#### Fazendo a bola ricochetear pela tela

Basicamente não há nenhuma dificuldade com a linguagem aqui, mero algoritmico, qualquer programador com os conhecimentos de ifs-else consegue montar uma solução para esse problema. A minha não foi uma das melhores, nem a mais otimizada. Só um detalhe, o basiegaxorz não trabalha com numero negativo. Por isso a função não ficou muito elegante. De todo modo, segue o fonte abaixo da função. Essa função está super simplória, vocês perceberão, adaptem ela para criar um movimento mais "melhor" na bolinha.
Reparem que eu executei uma função PLACAR ela ainda "não existe" no seu código fonte então comente ela ou dará erro. E o nosso loop principal ficará:

#### Checagem de colisão: Rebatendo a bola

Colisão é um assunto bem extenso. Tratar a colisão de qualquer modo é algo simples, contudo, de método eficiente pode ser tornar uma tarefa bem complicada, principalmente levando em consideração as limitações do processador do genesis.
Mas o que posso falar rapidamente é que eu vou usar um método muito especifico, que não poderá ser aplicado a praticamente nenhum game. Na realidade é um metodo que envolve o hardware do megadrive, o registrador de status (SR), algums emuladores (como o kega) nem implementao essa funcionalidade (passei algum tempo quebrando a cabeça, até descobri que o problema é no emulador). Caso queira testar seus codigos sugiro o emulador gens, é muito mais fiel ao funcionamento do video-game.
A função COLLISION utilizada no exemplo a baixo retorna o valor do SR, quando QUALQUER sprite colidir o seu valor será igual a 1, caso contrário igual a 0. Como nesse caso totalmente especifico não existe outra colisão possível sem ser a bola com a raquete do pong. E em ambos os casos eu dei o mesmo tratamento, ou seja, se houver colisão inverta o sentido no eixo X. Normalmente você nunca poderá usar esse método sozinho. Você jamais saberia usando ele se, um tiro acertou você ou se um tiro seu acertou um inimigo, ou se você bateu em um inimigo, se um inimigo atravessou o outro na tela, se um aliado passou por você, etc. A única vantagem desse método é: não envolve nenhum processamento, o proprio hardware do mega drive faz isso.
Outro detalhe, essa função normalmente não é usada por quem programa mega drive, procure ler mais sobre sprites e gerar seus métodos ou usar algum conhecido. No proprio site do basiegaxorz nem existe a documentação dessa função (também demorei um tempo para descobrir sua existência). Eu utilizo normalmente ela como gatilho para um método mais interessante. Por exemplo, se houve alguma colisão "detectar quais, onde, etc.", assim evita chamadas de um método que consome mais recurso atoa.
A unica alteração feita na função de mover a bolinha foi a inserção do trecho de codigo a seguir:
Ficando asssim:

#### Marcando a pontuação

Duas funções aqui, a função "placar" que o exemplo acima está usando. E uma função para reiniciar o jogo, apos o placar atingir 10.
Função para reiniciar o jogo, ela usa um Label, chamado "inicio".
Eu inseri o label logo antes da função que desenha a tela, assim:

#### Inteligência artificial básica: Jogador 1 versus CPU

IA horrivel, praticamente impossivel passar do CPU. Fiz mais para vocês terem uma noção do jogo funcionando mesmo. Com o movimento desse jeito da bolinha creio que é simplesmente impossivel passar algo pelo CPU. Mas ai está.
Nosso loop principal ficou assim:

#### Pong completo

Só para postar como ficou o jogo completo, com ia horrivel, e movimento da bola ruim. Modifique e faça um pong melhor.

### Múltiplos inimigos na tela: Fazendo seu próprio Galaxian
## Intermediário
### Movimentando os planos de fundo

Abaixo um exemplo de código que demonstra como é executada a rolagem de um plano, permitindo que você possa movimentar-se em todas as direções.
Trata-se de uma versão melhorada desta postada no fórum oficial do Basiegaxorz, e para que este código funcione corretamente, você vai precisar de uma imagem com 1040x1040 pixels, renderizada no programa Imagenesis com a segunda opção de mode marcada (15 color, 4bpp, 1 plane, 8x8 tiles, Optimized). Por ser grande a imagem, a renderização pode demorar um pouco.
Salve o arquivo de tiles e seu mapa na mesma pasta do código e nele insira, nos rótulos indicados, os nomes dos seus arquivos; cole a paleta da sua imagem e não esqueça de alterar a quantidade de tiles no segundo parâmetro do Loadtiles que irá carregar o cenário.
(no ato da renderização, verifique se sua imagem não gera uma quantidade de tiles acima da capacidade da memória).

### Som básico: Usando o PSG
## Avançado
### Descrição das portas de I/O do Mega Drive
#### VDP

$C00000 (Porta de dados)
$C00004 (Porta de controle)
Leitura
| * | * | * | * | * | * | VAZIO | CHEIO |
| F | SOVR | C | IMPAR | | VB | HB | PAL |
Escrita 1: Enviar dados ao registrador
| 1 | 0 | 0 | RS4 | RS3 | RS2 | RS1 | RS0 |
| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
* As portas do VDP devem ser acessadas via "word" ou "long word", para correto funcionamento.
Escrita 2: Seleção de endereço
1ª Escrita
| CD1 | CD0 | A13 | A12 | A11 | A10 | A9 | A8 |
| A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
2ª Escrita
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| CD5 | CD4 | CD3 | CD2 | 0 | 0 | A15 | A14 |
| Modo de Acesso | CD5 | CD4 | CD3 | CD2 | CD1 | CD0 |
| Escrita em VRAM | 0 | 0 | 0 | 0 | 0 | 1 |
| Escrita em CRAM | 0 | 0 | 0 | 0 | 1 | 1 |
| Escrita em VSRAM | 0 | 0 | 0 | 1 | 0 | 1 |
| Leitura da VRAM | 0 | 0 | 0 | 0 | 0 | 0 |
| Leitura da CRAM | 0 | 0 | 1 | 0 | 0 | 0 |
| Leitura da VSRAM | 0 | 0 | 0 | 1 | 0 | 0 |
$C00008 (Contador horizontal/vertical)
Modo não entrelaçado
| VC7 | VC6 | VC5 | VC4 | VC3 | VC2 | VC1 | VC0 |
| HC8 | HC7 | HC6 | HC5 | HC4 | HC3 | HC2 | HC1 |
Modo entrelaçado
| VC7 | VC6 | VC5 | VC4 | VC3 | VC2 | VC1 | VC8 |
| HC8 | HC7 | HC6 | HC5 | HC4 | HC3 | HC2 | HC1 |
Registradores
REG 0: Registrador de modo número 1
| 0 | 0 | 0 | IE1 | 0 | 1 | M3 | 0 |
REG 1: Registrador de modo número 2
| 0 | DISP | IE0 | M1 | M2 | 1 | 0 | 0 |
REG 2: Endereço base do mapa do fundo A
| 0 | 0 | SA15 | SA14 | SA13 | 0 | 0 | 0 |
REG 3: Endereço base do mapa da janela
| 0 | 0 | WD15 | WD14 | WD13 | WD12 | WD11 | 0 |
REG 4: Endereço base do mapa do fundo B
| 0 | 0 | 0 | 0 | 0 | SB15 | SB14 | SB13 |
REG 5: Endereço base da tabela de atributos dos sprites
| 0 | AT15 | AT14 | AT13 | AT12 | AT11 | AT10 | AT9 |
REG 7: Cor do fundo
| 0 | 0 | CPT1 | CPT0 | COL3 | COL2 | COL1 | COL0 |
REG 10: Interrupção horizontal
| BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 |
REG 11: Registrador de modo número 3
| 0 | 0 | 0 | 0 | IE2 | VSCR | HSCR | LSCR |
Modo de deslocamento horizontal
| VSCR | Função |
| 0 | Scroll completo |
| 1 | Scroll a cada duas células |
Modo de deslocamento vertical
| HSCR | LSCR | Função |
| 0 | 0 | Scroll completo |
| 0 | 1 | Inválido |
| 1 | 0 | Scroll célula a célula |
| 1 | 1 | Scroll linha a linha |
REG 12: Registrador de modo número 4
| RS0 | 0 | 0 | 0 | S/TE | LSM1 | LSM0 | RS1 |
| LSM1 | LSM2 | Função |
| 0 | 0 | Sem entrelaçamento |
| 0 | 1 | Entrelaçado |
| 1 | 0 | Inválido |
| 1 | 1 | Entrelaçado (Dupla resolução) |
REG 13: Endereço da tabela de deslocamento horizontal
| 0 | 0 | HS15 | HS14 | HS13 | HS12 | HS11 | HS10 |
REG 15: Dados de auto incremento
A cada vez que a CPU acessa a memória da VDP do Mega Drive, a posição atual desta memória é incrementada. Este registrador define o tamanho do incremento.
| 0 | 0 | HS15 | HS14 | HS13 | HS12 | HS11 | HS10 |
REG 16: Tamanho do plano de fundo
| 0 | 0 | VSZ1 | VSZ0 | 0 | 0 | HSZ1 | HSZ0 |
| VSZ1 | VSZ0 | Função |
| 0 | 0 | 32 células na vertical |
| 0 | 1 | 64 células na vertical |
| 1 | 0 | Inválido |
| 1 | 1 | 128 células na vertical |
| HSZ1 | HSZ0 | Função |
| 0 | 0 | 32 células na horizontal |
| 0 | 1 | 64 células na horizontal |
| 1 | 0 | Inválido |
| 1 | 1 | 128 células na horizontal |
REG 17: Posição horizontal da janela
| RIGT | 0 | 0 | WHP5 | WHP4 | WHP3 | WHP2 | WHP1 |
REG 18: Posição vertical da janela
| DOWN | 0 | 0 | WVP4 | WVP3 | WVP2 | WVP1 | WVP0 |
REG 19: Byte menos significativo do contador de DMA
| LG7 | LG6 | LG5 | LG4 | LG3 | LG2 | LG1 | LG0 |
REG 20: Byte mais significativo do contador de DMA
| LG15 | LG14 | LG13 | LG12 | LG11 | LG10 | LG9 | LG8 |
REG 21: Porção inferior do endereço de origem de DMA
| SA8 | SA7 | SA6 | SA5 | SA4 | SA3 | SA2 | SA1 |
REG 22: Porção central do endereço de origem de DMA
| SA16 | SA15 | SA14 | SA13 | SA12 | SA11 | SA10 | SA9 |
REG 23: Porção superior do endereço de origem de DMA
| DMD1 | DMD0 | SA22 | SA21 | SA20 | SA19 | SA18 | SA17 |
| DMD1 | DMD0 | Função |
| 0 | SA23 | Memória para vídeo |
| 1 | 0 | Preenche VRAM |
| 1 | 1 | Vídeo para vídeo |
Exemplo de utilização das portas

### Programação assembly 68k
#### Documentação Assembly

A extensa documentação do assembly do 68k pode ser encontrada no Devega.

#### Cabeçalho da ROM

O cabeçalho da ROM para Mega Drive consiste de duas seções, cada uma de 256 bytes. A primeira é "exigida" pelo processador, e indicam o início da pilha, o endereço da rotina principal e os endereços das rotinas de exceções (traps) e interrupções. Já a segunda seção é uma padronização realizada pela SEGA para identificação do cartucho (direitos autorais, nome do produto, etc.) e informações sobre utilização de recursos do hardware (mapeamento de memória, periféricos, etc.).
Os elementos mais usados do Vetor de Exceções (primeiros 256 bytes) :
| Item | Endereço | Descrição |
| 1 | 0x000000 | Endereço do início da pilha (geralmente próximo ao fim da memória RAM) |
| 2 | 0x000004 | Endereço da rotina principal (valor inicial do PC, também para quando resetado) |
| 26 | 0x000068 | Interrupção de nível 2: no Mega Drive, Externa. |
| 28 | 0x000070 | Interrupção de nível 4: no Mega Drive, Retraço Horizontal. |
| 30 | 0x000078 | Interrupção de nível 6: no Mega Drive, Retraço Vertical. |
O conteúdo de cada elemento do Vetor de Exceções é o endereço absoluto (em relação ao primeiro byte do arquivo binário) da respectiva rotina de tratamento da exceção. Cada elemento consiste de 4 bytes.
O costume é, quando for desejado não tratar certas exceções, elas reiniciem o software, ou seja, apontem para o começo do programa. Vale destacar que as interrupções de retraço devem ser tratadas, nem que seja não fazer nada, já que são efetivamente geradas a tempo constante.
As demais exceções/interrupções não citadas podem ser encontradas no Apêndice B do MOTOROLA M68000 FAMILY Programmer's Reference Manual disponível no Devega.