Desenvolvimento de jogos/Programação Megadrive
Introdução
editarEste wikibook destina-se àqueles que estejam interessados no desenvolvimento de jogos para o videogame conhecido como Mega Drive.
(Em construção)
Ferramentas
editarMontadores
editarTambé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
editarO GNU Assembler (GAS) [1]. 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:
./configure -target=m68k-coff make
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
editaras -m68000 -o arquivo.o arquivo.s
É 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
editarApós feita as compilações de todas os códigos do programa, é preciso ligá-los.
ld --oformat=binary -Ttext=0 -o rom.bin arquivo.o [outros .o]
- --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
editarBasiEgaXorz
editarCompilador 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
editarCompilador 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
editarVersã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
editarKit 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
editarImaGenesis
editarTambé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
editarPara os exemplos desta seção, será utilizado o BasiEgaXorz, por ser mais simples de usar.
Escrevendo texto na tela
editarUsando o BasiEgaXorz, esta tarefa se torna bastante simples:
print "Ola, mundo!!!"
Definindo a palheta de cores
editarO 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 é:
Pallette <código RGB>, <n° da palheta>, <n° da cor>
- <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:
ink 0 print "Texto usando a palheta0" ink 1 print "Texto usando a palheta1" ink 2 print "Texto usando a palheta2" ink 3 print "Texto usando a palheta3" ' ink 0 print print "Pressione qualquer botao..." while joypad()=0: wend waitpadup 0 ' pallette 14, 0, 1 pallette 224, 1, 1 pallette 3584, 2, 1 pallette 238, 3, 1 ' ink 1 print "Observe como a cor do texto mudou ao" print "alterar a palheta"
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 é:
Pallettes <rótulo dos dados>, <n° da palheta>, <cor inicial>, <n° de cores>, [Deslocamento]
- <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:
ink 0 print "Texto usando a palheta0" ink 1 print "Texto usando a palheta1" ink 2 print "Texto usando a palheta2" ink 3 print "Texto usando a palheta3" ' ink 0 print print "Pressione qualquer botao..." while joypad()=0: wend waitpadup 0 ' pallettes pallette1, 0, 0, 16 pallettes pallette2, 1, 0, 16 pallettes pallette3, 2, 0, 16 pallettes pallette4, 3, 0, 16 ' ink 1 print "Observe como a cor do texto mudou ao" print "alterar a palheta, assim como a cor" print "do fundo." ' pallette1: DATAINT $0ECA,$00EE,$0EA8,$0CA6,$0CAC,$08CA,$08AA,$0A68 DATAINT $0866,$0ACE,$0442,$0686,$066A,$0AAE,$06AA,$0000 pallette2: DATAINT $0002,$00AA,$002A,$004A,$006E,$02AE,$002C,$04AE DATAINT $02EE,$04EE,$0046,$0468,$04AA,$028A,$08CC,$0000 pallette3: DATAINT $0620,$0088,$0A62,$0CEC,$0644,$0CA4,$0684,$06A8 DATAINT $0868,$0424,$0444,$0222,$0648,$0428,$0000,$0000 pallette4: DATAINT $0422,$0044,$0426,$0446,$086A,$0CEE,$044A,$0866 DATAINT $06AE,$0642,$0202,$0244,$0662,$0000,$0000,$0000
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
editarExistem 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>
editarEspera 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>)
editarEssa 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.
Bit Hex Decimal Evento 0 &h001 1 Cima 1 &h002 2 Baixo 2 &h004 4 Esquerda 3 &h008 8 Direita 4 &h010 16 B 5 &h020 32 C 6 &h040 64 A 7 &h080 128 Start 8 &h100 256 Z (6-Botões) 9 &h200 512 Y (6-Botões) 10 &h400 1024 X (6-Botões) 11 &h800 2048 Mode (6-Botões)
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.
WaitPadUp 'se omitir o valor 0 ou 1, será o valor padrao é 0 Print "Controle 1 esta conectado" ' Player Joypad Inputs while 1 sleep 6 'so para nao ficar num loop frenetico e ocupar muito a cpu joy = joypad() 'mesma coisa que no waitpadup if joy.0 then ' cima Print "cima" endif if joy.1 then ' baixo Print "baixo" endif if joy.2 then ' esquerda Print "esquerda" endif if joy.3 then ' direita Print "direita" endif if joy.4 then ' b Print "b" endif if joy.5 then ' c Print "c" endif if joy.6 then ' a Print "a" endif if joy.7 then ' start Print "start" endif if joy.8 then ' z Print "z" endif if joy.9 then ' y Print "y" endif if joy.10 then ' x Print "x" endif if joy.11 then 'mode 6 button Print "mode 6 button" endif 'voce tambem pode usar capturar mais de um envento ao mesmo tempo, por exemplo. 'perceba que ele caira em todos os if's no do botao B, no do botao C, e no BC. 'se quizer aninhar os ifs - else, pode facilmente impedir isso. 'Nao fiz pois achei que ficaria mais claro os exemplos assim. if joy.4 and joy.5 then print "bc" endif wend
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.
WHILE 1 SLEEP 6 JOY = JOYPAD() SELECT CASE JOY CASE 1: PRINT "CIMA" EXIT CASE CASE 2: PRINT "BAIXO" EXIT CASE CASE 4: PRINT "ESQUERDA" EXIT CASE CASE 5: PRINT "DIAGONAL ESQUERDA PRA CIMA" EXIT CASE CASE 6: PRINT "DIAGONAL ESQUERDA PRA BAIXO" EXIT CASE CASE 8: PRINT "DIREITA" EXIT CASE CASE 9: PRINT "DIAGONAL DIREITA PRA CIMA" EXIT CASE CASE 10: PRINT "DIAGONAL DIREITA PRA BAIXO" EXIT CASE CASE ELSE: EXIT SELECT END SELECT WEND
Movendo objetos pela tela
editarUtilizando 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 ([2]) 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.
LOADTILES TILES,1,256 'CARREGA A 'IMAGEM' DO LABEL TILES QUE SERÁ O NOSSO CURSOR, É UMA IMAGEM 8X8 PIXIELS. 'OS PARAMETROS, 1 E 256 SÃO RESPECTIVAMENTE A QUANTIDADE DE TILE (CADA TILE É UM 'BLOCO' DE 8X8 PIXIELS) E 'O ENDEREÇO DE MEMORIA EM QUE O TILE SERÁ ALOCADO PALLETTES PALLETTEDATA,0,0,15 'MODIFICA A PALETA DE CORES NUMERO ZERO COM AS CORES DO LABEL PALLETTEDATA, QUE CONTEM AS CORES DO NOSSO CURSOR 'ESSE COMANDO JÁ FOI EXPLICADO ACIMA SPRITE=ADDSPRITE(1,1) 'CRIA UM SPRITE DE 1 TILE DE ALTURA E 1 TILE DE COMPRIMENTO (8X8 PIXIELS), E RETONA O ENDEREÇO DO SPRITE PROPSPRITE SPRITE,256,0 DESENHA O TILE' DE ENDEREÇO SPRITE, INICIANDO NO ENDEREÇO DE MEMORIA 256, UTILIZANDO A PALETA DE CORES NUMERO 0 X=280 Y=235 WHILE 1 SLEEP 2 MOVESPRITE SPRITE,X,Y 'MOVIMENTA O SPRITE PARA AS COORDENADAS X,Y JOY = JOYPAD() SELECT CASE JOY CASE 1: Y-- EXIT CASE CASE 2: Y++ EXIT CASE CASE 4: X-- EXIT CASE CASE 5: X-- Y-- EXIT CASE CASE 6: X-- Y++ EXIT CASE CASE 8: X++ EXIT CASE CASE 9: X++ Y-- EXIT CASE CASE 10: X++ Y++ EXIT CASE CASE ELSE: EXIT SELECT END SELECT WEND TILES: 'TILE CONVERTIDO, EU UTILIZEI UMA FERRAMENTA PARA FAZER ESSA CONVERSAO 'E EMBORA CHATO NÃO É IMPOSSIVEL DE SE FAZER NA MAO NAO 'É UMA ESPÉCIE DE MAPA 8X8 PIXIELS, ONDE OS NUMEROS REPRESENTA OS NUMEROS DA PALETA DE CORES '1 = PRETO, 2 = VERMELHOR MAIS CLARO, 3 = VERMELHO MAIS ESCURO, 4 = UM VERMELHO 'INTERMEDIARIO' 'SE REPARAREM BEM CONSEGUIRAO VER A NAVE DESENHADA NESSE 'MAPA' AO LADO 'ALTEREM ESSE MAPINHA E AS CORES DA PALETA VCS ENTENDERAO COMO FUNCIONA. DATALONG $11122111 ' Tile #0 DATALONG $11322311 DATALONG $11322311 DATALONG $13244211 DATALONG $13244231 DATALONG $32433423 DATALONG $32433423 DATALONG $22222222 PALLETTEDATA: 'PALLET COM AS CORES DO TILE, EU UTILIZEI UMA FERRAMENTA PARA DETECTAR AS CORES. 'MAS É POSSIVEL, E NEM TÃO DIFICIL, FAZER A PALETA DE CORES "NA MAO". DATAINT $0000,$0000,$000E,$0004,$0008,$0000,$0000,$0000 DATAINT $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
Exibindo uma imagem no fundo da tela
editarSei 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>
editarEsse comando torna padrão para o ASCII, comandos tipo print e etc.
SETGFXPLANE <SCROLL_A/SCROLL_B/WINDOW>
editarEsse comando permite escolher qual será o plano padrão dos comandos de desenho, drawtile, drawtiles.
SETSCROLLPLANE <SCROLL_A/SCROLL_B>
editarEscolhe 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>
editarDesenha 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.
LOADTILES TILES,4,256 'CARREGA A 'IMAGEM' DO LABEL TILES QUE SERÁ O NOSSO CURSOR, É UMA IMAGEM 8X8 PIXIELS. 'OS PARAMETROS, 4 E 256 SÃO RESPECTIVAMENTE A QUANTIDADE DE TILE (CADA TILE É UM 'BLOCO' DE 8X8 PIXIELS) E 'O ENDEREÇO DE MEMORIA EM QUE O TILE SERÁ ALOCADO 'REPAREM QUE ALTEREI A FUNCAO LOADTILES AGORA PARA CARREGAR 4 TILES A PARTIR DE 256 PALLETTES PALLETTEDATA,0,0,15 'MODIFICA A PALETA DE CORES NUMERO ZERO COM AS CORES DO LABEL PALLETTEDATA, QUE CONTEM AS CORES DO NOSSO CURSOR 'ESSE COMANDO JÁ FOI EXPLICADO ACIMA SPRITE=ADDSPRITE(1,1) 'CRIA UM SPRITE DE 1 TILE DE ALTURA E 1 TILE DE COMPRIMENTO (8X8 PIXIELS), E RETONA O ENDEREÇO DO SPRITE PROPSPRITE SPRITE,256,0 DESENHA O TILE' DE ENDEREÇO SPRITE, INICIANDO NO ENDEREÇO DE MEMORIA 256, UTILIZANDO A PALETA DE CORES NUMERO 0 X=280 Y=235 SETTEXTPLANE SCROLL_A SETGFXPLANE SCROLL_B SETSCROLLPLANE SCROLL_B 'GERA AS ESTRELAS NO FUNDO, ESCOLHE ENTRE 3 TIPOS DE ESTRELAS, A GRANDE, A PEQUENA CLARA, E A PEQUENA ESCURA RANDOMIZE 2 'E ALOCA 100 DELAS 'RANDOMICAMENTE' PELA TELA. COMO ESTOU UTILIZANDO A MESMA SEMENTE, SEMPRE SERÁ NOS MESMOS LUGARES FOR I=0 TO 200 'IMPORTANTE: ISSO É O TIPO DE COISA QUE NÃO SE DEVE FAZER, A FUNÇÃO RANDOM GERA UM PROCESSAMENTO DESNECESSÁRIO. DRAWTILE 257+RND(3),RND(40)+1,RND(63)+1 'E O PROCESSADOR DO MEGA DRIVE NÃO TEM TANTO PODER COMPUTACIONAL PARA FICAR DESPERDICANDO. NEXT 'É MUITO MELHOR ESCOLHER AS POSICOES E USAR O DRAWTILE: 'DRAWTILE <POSICAO DA MEMORIA>, COORDENADA X, COORDENADA Y 'VOU DECLARAR UMAS CONSTANTES PARA DEIXAR O CODIGO MAIS LEGIVEL CONST #MAX_X=438 'NADA A VER COM O ASSUNTO EM QUESTAO, MAS DECLAREI ESSAS CONTANTES PARA LIMITAR A NAVE DE SAIR DA TELA CONST #MAX_Y=343 CONST #MIN_X=130 CONST #MIN_Y=148 INK 1 WHILE 1 I++ 'ISSO DEVE DAR ALGUM ERRO, QUASE CERTEZA... UMA HORA A VARIAVEL I IRA ULTRAPASSAR O VALOR MAXIMO DO INTEGER, EU NAO VOU FICAR TRATANTO 'ESSAS EXEÇÕES NO EXEMPLO. MAS NINGUEM DEVE TER DIFICULDADE COM ISSO. SCROLL2 DOWN, I 'MOVE O PLANO DE SCROLL, SCROLL_B, PARA A POSICAO I... SLEEP 1 'OBSERVAÇÃO SOBRE O SLEEP, CADA 1 DE SLEEP EQUIVALE A 1HZ DA TV OU SEJA 1/60s SLEEP 60 É IGUAL A 1 SEGUNDO LOCATE 0,0 'MOVE O CURSOS DE TEXTO PARA A POSICAO 0,0 PRINT "COORD: ";X;" - ";Y; 'IMPRIMI A COORDENADA DO CURSOR... A CADA LOOP O VALOR SERA ATUALIZADO NA TELA JOY = JOYPAD() SELECT CASE JOY CASE 1: IF Y > #MIN_Y THEN Y-- ENDIF EXIT CASE 'AQUI ANTES DE MOVER A NAVE PARA QUALQUER DIREÇÃO EU FIZ UM VERIFICACAO SE ELA PODE AINDA MOVER CASE 2: 'DE RESTO É A MESMA INTERACAO COM O CONTROLE DE ANTES. IF Y < #MAX_Y THEN Y++ ENDIF EXIT CASE CASE 4: IF X > #MIN_X THEN X-- ENDIF EXIT CASE CASE 5: IF X > #MIN_X AND Y > #MIN_Y THEN X-- Y-- ENDIF EXIT CASE CASE 6: IF X > #MIN_X AND Y < #MAX_Y THEN X-- Y++ ENDIF EXIT CASE CASE 8: IF X < #MAX_X THEN X++ ENDIF EXIT CASE CASE 9: IF X < #MAX_X AND Y > #MIN_Y THEN X++ Y-- ENDIF EXIT CASE CASE 10: IF X < #MAX_X AND Y < #MAX_Y THEN X++ Y++ ENDIF EXIT CASE CASE ELSE: EXIT SELECT END SELECT MOVESPRITE SPRITE,X,Y 'MOVIMENTA O SPRITE PARA AS COORDENADAS X,Y WEND TILES: 'TILE CONVERTIDO, EU UTILIZEI UMA FERRAMENTA PARA FAZER ESSA CONVERSAO 'E EMBORA CHATO NÃO É IMPOSSIVEL DE SE FAZER NA MAO NAO 'É UMA ESPÉCIE DE MAPA 8X8 PIXIELS, ONDE OS NUMEROS REPRESENTA OS NUMEROS DA PALETA DE CORES '1 = PRETO, 2 = VERMELHOR MAIS CLARO, 3 = VERMELHO MAIS ESCURO, 4 = UM VERMELHO 'INTERMEDIARIO' 'SE REPARAREM BEM CONSEGUIRAO VER A NAVE DESENHADA NESSE 'MAPA' AO LADO 'ALTEREM ESSE MAPINHA E AS CORES DA PALETA VCS ENTENDERAO COMO FUNCIONA. 'EDIT: FICOU FEIO MAS EU FIZ AS ESTRELAS MANUALMENTE DATALONG $11122111 'NAVE, POSICAO 256 DATALONG $11322311 DATALONG $11322311 DATALONG $13244211 DATALONG $13244231 DATALONG $32433423 DATALONG $32433423 DATALONG $22222222 DATALONG $11111111 'ESTRELA GRANDE, POSICAO 257 DATALONG $11111111 DATALONG $11111111 DATALONG $11116111 DATALONG $11165611 DATALONG $11116111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 'ESTRELA PEQUENA CLARA, POSICAO 258 DATALONG $11111111 DATALONG $11511111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 'ESTRELA PEQUENA ESCURA, POSICAO 259 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 DATALONG $11111161 DATALONG $11111111 DATALONG $11111111 DATALONG $11111111 PALLETTEDATA: 'PALLET COM AS CORES DO TILE, EU UTILIZEI UMA FERRAMENTA PARA DETECTAR AS CORES. 'MAS É POSSIVEL, E NEM TÃO DIFICIL, FAZER A PALETA DE CORES "NA MAO". 'INSERI AS CORES 5 E 6 MANUALMENTE PARA FAZER AS ESTRELAS (DOIS TONS DE CINZA) 'PODERIA FAZE-LAS VERMELHAS TB APROVEITANDO AS CORES QUE A NAVE ESTA UTILIZANDO. DATAINT $0000,$0000,$000E,$0004,$0008,$0CCC,$0444,$0000 DATAINT $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000
OBS: Qualquer editor, revisor, fique a vontade para modificar ou deixar mais claro o conteudo.
Seu primeiro jogo: Como fazer um clone do "Pong"
editarPong 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. [3]
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 [4]. 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.
'IGNORA AS OPÇÕES DE COMPILAÇÃO DA GUI, E COMPILA O JOGO PARA MEGA DRIVE (CARTUCHO) OPTION CARTRIDGE 'OBRIGA EXPLICITAMENTE A DECLARAÇÃO DE VARIÁVEIS (BOAS PRÁTICA) OPTION EXPLICIT 'DESABILITA A SENSIBILIDADE A CAIXA ALTA PARA VARIAVEIS, LABELS, FUNCÕES, ETC. OPTION CASESENSE 'TÍTULO QUE SERÁ EXIBIDO NA JANELA DO SEU EMULADOR. (DEPOIS TITULO INSERIDO AUTOMATICAMENTE PELO BASIEGAXORZ) OPTION TITLE, "PONG!" 'ATRIBUI OS COMANDOS DE DRAW PARA O PLANO SCROLL_B POR PADRÃO, E OS DE TEXTO AO PLANO SCROLL_A SETGFXPLANE SCROLL_B SETTEXTPLANE SCROLL_B 'EXECUTA A SUB QUE APRESENTARÁ A TELA DE APRESENTAÇÃO DO JOGO APRESENTACAO SLEEP 10 'EXECUTA A SUB QUE IRÁ DESENHAR A TELA DO PONG DESENHA_TELA 'ALTERA O PADRÃO TO TEXTO PARA O SCROLL_A SETTEXTPLANE SCROLL_A 'ADICIONAR OS SPRITES DOS JOGADORES E A BOLA DIM JOGADOR AS INTEGER DIM CPU AS INTEGER DIM BOLA AS INTEGER JOGADOR=ADDSPRITE(4,1) CPU=ADDSPRITE(4,1) BOLA=ADDSPRITE(1,1) 'ALOCA OS SPRITES DOS JOGADORES LOADTILES TILEDATA, 4, 256 'INSERE DUAS CORES NA PALETA DE COR (DOIS TONS DE CINZA) PALLETTE 2184, 0, 2 PALLETTE 3276, 0, 3 'ATRIBUI EM QUAL REGIÃO DA MEMÓRIA É O INICIO DO SPRITE PROPSPRITE JOGADOR,256, 0 PROPSPRITE CPU,256, 0 PROPSPRITE BOLA,7,2 'POSICIONA OS SPRITES NA TELA MOVESPRITE JOGADOR, 136, 218 MOVESPRITE CPU, 432,218 MOVESPRITE BOLA, 142, 229 'LOOP DO JOGO 'VOU REMOVER TUDO DO LOOP WHILE 1 SLEEP 1 MOVE_SPRITE JOGADOR WEND 'FUNÇÃO QUE DESENHA A TELA (REDE, LATERAIS, JOGADORES) DECLARE SUB DESENHA_TELA() 'DECLARA A VARIÁVEL DENTRO DA FUNÇÃO COMO LOCAL, A MEMÓRIA SERÁ LIBERADA APÓS O TERMINO DA FUNÇÃO (BOA PRÁTICA) LOCAL I AS INTEGER 'ITERA UMA VEZ PARA CADA COLUNA FOR I=1 TO 38 'AS POSIÇÕES DE MEMÓRIA ANTES DE 256 SÃO RESERVADAS PARA OS CARATERES. 'IMPRIME A LINHA SUPERIOR DRAWTILE 205,I,1 'IMPRIME A LINHA INFERIOR DRAWTILE 205,I,26 NEXT FOR I=2 TO 25 'IMPRIME A LINHA VERTICAL DRAWTILE 186, 19, I NEXT 'MOVE O CURSOR DE TEXTO PARA A POSIÇÃO 0,1 (O COMANDO LOCATE USA AS COORDENADAS Y,X E NÃO X,Y) LOCATE 1,0 'MUDA A COR PARA VERDE INK 2 PRINT "JOGADOR" LOCATE 1,35 PRINT "CPU" 'VOLTA A COR ORIGINAL INK 0 END SUB 'FUNÇÃO QUE IMPRIME A TELA INICIADO DO JOGO E PRESSIONAR START PARA SEGUIR EM FRENTE DECLARE SUB APRESENTACAO() LOCATE 10,10 'MUDA A COR DO TEXTO PARA VERDE INK 2 PRINT "PONG - PRESS START" 'VOLTA A COR ORIGINAL INK 0 'EXECUTA UMA ESPERA ATÉ QUE O BOTÃO DE START SEJA PRESSIONADO. (USE O COMANDO SLEEP (BOA PRÁTICA)) WHILE JOYPAD() != 128 : SLEEP 2 : WEND CLS END SUB 'TESTA E MOVE O SPRITE DO JOGADOR, SE O MOVIMENTO FOR PERMITIDO DECLARE SUB MOVE_SPRITE(JOGADOR AS INTEGER) LOCAL JOY AS INTEGER JOY = JOYPAD() 'PARA CIMA IF JOY.0 THEN IF SPRITEPOSY(JOGADOR) > 142 THEN SHIFTSPRITE JOGADOR, 0, -1 ENDIF ELSE 'PARA BAIXO IF JOY.1 THEN IF SPRITEPOSY(JOGADOR) < 305 THEN SHIFTSPRITE JOGADOR, 0, 1 ENDIF ELSE 'START IF JOY.7 THEN LOCATE 12,12 INK 1 PRINT "=== PAUSE ===" INK 0 SLEEP 10 WHILE JOYPAD() != 128 : SLEEP 2 : WEND SLEEP 10 CLS ENDIF ENDIF ENDIF END SUB 'TILE DA RAQUETE DOS JOGADORES TILEDATA: DATALONG $00000000 ' Tile #0 DATALONG $00022000 DATALONG $00233200 DATALONG $02211220 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #0 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #2 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #1 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02211220 DATALONG $00233200 DATALONG $00022000 DATALONG $00000000
Fazendo a bola ricochetear pela tela
editarBasicamente 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.
'QUANDO A BOLINHA ESTIVER VINDO PRA VOCÊ O SENTIDO X É 1, OU 0 QUANDO ESTIVER INDO NA DIREÇÃO DO COMPUTADOR 'QUANDO A BOLINHA ESTIVER INDO PARA CIMA O SENTIDO Y É 1, OU 0 QUANDO ESTIVER INDO PARA BAIXO GLOBAL SENTIDOX AS INTEGER GLOBAL SENTIDOY AS INTEGER 'VOCÊ PROVAVELMENTE IRÁ SE PERGUNTAR, QUE PORRA É ESSA QUE EU FIZ... 'REALMENTE FIZ RÁPIDO, MAS NÃO TEM COMO FUGIR MUITO POIS NÃO TEMOS SUPORTE A VARIÁVEIS COM NÚMERO NEGATIVO. 'SE NÃO DAVA PARA FAZER ESSA FUNÇÃO EM BEM MENOS LINHAS, PARA SIMULAR ACABEI CRIANDO FLAGS DE SENTIDO. DECLARE SUB MOVIMENTAR_BOLA(BOLA AS INTEGER) LOCAL X AS INTEGER LOCAL Y AS INTEGER X=SPRITEPOSX(BOLA) Y=SPRITEPOSY(BOLA) 'TESTA SE NÃO FOI PONTO IF (X > 136) AND (X < 432) THEN 'NÃO FOI PONTO, VAMOS VERIFICAR SE CHEGOU EM ALGUMA PAREDE IF (Y = 139) THEN 'CHEGOU NA PAREDE DE CIMA, VAMOS TROCAR A DIREÇÃO DA BOLA NO EIXO Y SENTIDOY-- ELSE 'TESTANDO SE CHEGOU NA PAREDE DE BAIXO IF (Y = 332) THEN 'CHEGOU, VAMOS MUDAR DE DIREÇÃO DA BOLA NO EIXO Y SENTIDOY++ ENDIF ENDIF ELSE 'FOI PONTO 'COLOCAR A BOLA NA POSIÇÃO INICIAL MOVESPRITE BOLA,278,233 'VAMOS CHAMAR A SUB QUE ATUALIZA O PLACAR E PASSAR O SENTIDOX, 'CASO ESSE SEJA IGUAL A 0 O JOGADOR FEZ UM PONTO, E SE FOR IGUAL A 1 ELE SOFREU UM PONTO. PLACAR SENTIDOX 'MUDAR O SENTIDO DA BOLA, A BOLA IRÁ PRIMEIRO PARA O LADO DE QUEM FEZ GOL. IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF 'VAMOS MOVER A BOLA IF SENTIDOY THEN IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,-1 ELSE SHIFTSPRITE BOLA,1,-1 ENDIF ELSE IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,1 ELSE SHIFTSPRITE BOLA,1,1 ENDIF ENDIF END SUB
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á:
WHILE 1 SLEEP 1 MOVE_SPRITE JOGADOR MOVIMENTAR_BOLA BOLA WEND
Checagem de colisão: Rebatendo a bola
editarColisã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:
'VAMOS TESTAR A COLISAO DO MODO MAIS SIMPLORIO QUE EXISTE.. IF COLLISION() THEN IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF
Ficando asssim:
'QUANDO A BOLINHA ESTIVER VINDO PRA VOCÊ O SENTIDO X É 1, OU 0 QUANDO ESTIVER INDO NA DIREÇÃO DO COMPUTADOR 'QUANDO A BOLINHA ESTIVER INDO PARA CIMA O SENTIDO Y É 1, OU 0 QUANDO ESTIVER INDO PARA BAIXO GLOBAL SENTIDOX AS INTEGER GLOBAL SENTIDOY AS INTEGER 'VOCÊ PROVAVELMENTE IRÁ SE PERGUNTAR, QUE PORRA É ESSA QUE EU FIZ... 'REALMENTE FIZ RÁPIDO, MAS NÃO TEM COMO FUGIR MUITO POIS NÃO TEMOS SUPORTE A VARIÁVEIS COM NÚMERO NEGATIVO. 'SE NÃO DAVA PARA FAZER ESSA FUNÇÃO EM BEM MENOS LINHAS, PARA SIMULAR ACABEI CRIANDO FLAGS DE SENTIDO. DECLARE SUB MOVIMENTAR_BOLA(BOLA AS INTEGER) LOCAL X AS INTEGER LOCAL Y AS INTEGER X=SPRITEPOSX(BOLA) Y=SPRITEPOSY(BOLA) 'TESTA SE NÃO FOI PONTO IF (X > 136) AND (X < 432) THEN 'NÃO FOI PONTO, VAMOS VERIFICAR SE CHEGOU EM ALGUMA PAREDE IF (Y = 139) THEN 'CHEGOU NA PAREDE DE CIMA, VAMOS TROCAR A DIREÇÃO DA BOLA NO EIXO Y SENTIDOY-- ELSE 'TESTANDO SE CHEGOU NA PAREDE DE BAIXO IF (Y = 332) THEN 'CHEGOU, VAMOS MUDAR DE DIREÇÃO DA BOLA NO EIXO Y SENTIDOY++ ENDIF ENDIF ELSE 'FOI PONTO 'COLOCAR A BOLA NA POSIÇÃO INICIAL MOVESPRITE BOLA,278,233 'VAMOS CHAMAR A SUB QUE ATUALIZA O PLACAR E PASSAR O SENTIDOX, 'CASO ESSE SEJA IGUAL A 0 O JOGADOR FEZ UM PONTO, E SE FOR IGUAL A 1 ELE SOFREU UM PONTO. PLACAR SENTIDOX 'MUDAR O SENTIDO DA BOLA, A BOLA IRÁ PRIMEIRO PARA O LADO DE QUEM FEZ GOL. IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF 'VAMOS TESTAR A COLISAO DO MODO MAIS SIMPLORIO QUE EXISTE.. IF COLLISION() THEN IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF 'VAMOS MOVER A BOLA IF SENTIDOY THEN IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,-1 ELSE SHIFTSPRITE BOLA,1,-1 ENDIF ELSE IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,1 ELSE SHIFTSPRITE BOLA,1,1 ENDIF ENDIF END SUB
Marcando a pontuação
editarDuas 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.
'NADA DE MAIS AQUI, SÓ VARIAVEIS GLOBAIS PARA MARCAR A PONTUAÇÃO DE CADA JOGADOR GLOBAL PONTOS_JOGADOR AS INTEGER GLOBAL PONTOS_CPU AS INTEGER 'FUNÇÃO QUE ATUALIZA O PLACAR DECLARE SUB PLACAR(PONTO AS INTEGER) 'MUDA O PADRÃO DE ESCRITA PARA O PLANO SCROLL_B 'O PAUSE, ESTÁ NO PLANO SCROLL_A, E QUANDO EU REMOVO O PAUSE EU DEI UM CLS NO PLANO. 'ESTOU MUDANDO DE PLANO PARA ESCREVER O PLACAR SÓ PARA QUANDO O JOGO FOR DESPAUSADO NÃO LIMPAR O PLACAR JUNTO SETTEXTPLANE SCROLL_B 'CIANO INK 1 'TESTA DE QUEM FOI PONTO IF PONTO THEN 'CPU LOCATE 1,19 PONTOS_CPU++ PRINT PONTOS_CPU 'VOCÊ PERDE O JOGO SE O CPU TIVER 10 PONTOS IF PONTOS_CPU > 9 THEN LOCATE 12,8 PRINT "PERDEU, APERTE START" INK 0 WHILE JOYPAD() != 128 : SLEEP 2 : WEND RESET ENDIF ELSE 'JOGADOR LOCATE 1,17 PONTOS_JOGADOR++ PRINT PONTOS_JOGADOR 'VOCÊ GANHA O JOGO COM SE TIVER 10 PONTOS IF PONTOS_JOGADOR > 9 THEN LOCATE 12,8 PRINT "GANHOU, APERTE START" INK 0 WHILE JOYPAD() != 128 : SLEEP 2 : WEND RESET ENDIF ENDIF 'RETORNA A COR ORIGINAL INK 0 'VOLTA A ESCRITA PADRÃO NO PLANO DE CIMA SETTEXTPLANE SCROLL_A END SUB
Função para reiniciar o jogo, ela usa um Label, chamado "inicio".
DECLARE SUB RESET() PONTOS_JOGADOR=0 PONTOS_CPU=0 SENTIDOX=0 SENTIDOY=0 FREEALLSPRITES SETTEXTPLANE SCROLL_A CLS SETTEXTPLANE SCROLL_B CLS GOTO INICIO END
Eu inseri o label logo antes da função que desenha a tela, assim:
'EXECUTA A SUB QUE APRESENTARÁ A TELA DE APRESENTAÇÃO DO JOGO APRESENTACAO 'LABEL QUE INDICA INICIO DO JOGO INICIO SLEEP 10 'EXECUTA A SUB QUE IRÁ DESENHAR A TELA DO PONG DESENHA_TELA
Inteligência artificial básica: Jogador 1 versus CPU
editarIA 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á.
'OK, IA HORRIVEL :P 'FIZ SÓ PARA VEREM O JOGO FUNCIONANDO DECLARE SUB MOVE_CPU(CPU AS INTEGER, BOLA AS INTEGER) LOCAL BOLAY AS INTEGER LOCAL CPUY AS INTEGER BOLAY=SPRITEPOSY(BOLA) CPUY=SPRITEPOSY(CPU) IF BOLAY > CPUY THEN IF CPUY < 305 THEN SHIFTSPRITE CPU,0,1 ENDIF ELSE IF BOLAY < CPUY THEN IF CPUY > 142 THEN SHIFTSPRITE CPU,0,-1 ENDIF ENDIF ENDIF END SUB
Nosso loop principal ficou assim:
WHILE 1 SLEEP 1 MOVE_SPRITE JOGADOR MOVIMENTAR_BOLA BOLA MOVE_CPU CPU,BOLA WEND
Pong completo
editarSó para postar como ficou o jogo completo, com ia horrivel, e movimento da bola ruim. Modifique e faça um pong melhor.
'IGNORA AS OPÇÕES DE COMPILAÇÃO DA GUI, E COMPILA O JOGO PARA MEGA DRIVE (CARTUCHO) OPTION CARTRIDGE 'OBRIGA EXPLICITAMENTE A DECLARAÇÃO DE VARIÁVEIS (BOAS PRÁTICA) OPTION EXPLICIT 'DESABILITA A SENSIBILIDADE A CAIXA ALTA PARA VARIAVEIS, LABELS, FUNCÕES, ETC. OPTION CASESENSE 'TÍTULO QUE SERÁ EXIBIDO NA JANELA DO SEU EMULADOR. (DEPOIS TITULO INSERIDO AUTOMATICAMENTE PELO BASIEGAXORZ) OPTION TITLE, "PONG!" 'ATRIBUI OS COMANDOS DE DRAW PARA O PLANO SCROLL_B POR PADRÃO, E OS DE TEXTO AO PLANO SCROLL_A SETGFXPLANE SCROLL_B SETTEXTPLANE SCROLL_B 'EXECUTA A SUB QUE APRESENTARÁ A TELA DE APRESENTAÇÃO DO JOGO APRESENTACAO 'LABEL QUE INDICA INICIO DO JOGO INICIO SLEEP 10 'EXECUTA A SUB QUE IRÁ DESENHAR A TELA DO PONG DESENHA_TELA 'ALTERA O PADRÃO TO TEXTO PARA O SCROLL_A SETTEXTPLANE SCROLL_A 'ADICIONAR OS SPRITES DOS JOGADORES E A BOLA DIM JOGADOR AS INTEGER DIM CPU AS INTEGER DIM BOLA AS INTEGER JOGADOR=ADDSPRITE(4,1) CPU=ADDSPRITE(4,1) BOLA=ADDSPRITE(1,1) 'ALOCA OS SPRITES DOS JOGADORES LOADTILES TILEDATA, 4, 256 'INSERE DUAS CORES NA PALETA DE COR (DOIS TONS DE CINZA) PALLETTE 2184, 0, 2 PALLETTE 3276, 0, 3 'ATRIBUI EM QUAL REGIÃO DA MEMÓRIA É O INICIO DO SPRITE\ PROPSPRITE JOGADOR,256, 0 PROPSPRITE CPU,256, 0 PROPSPRITE BOLA,7,2 'POSICIONA OS SPRITES NA TELA MOVESPRITE JOGADOR, 136, 218 MOVESPRITE CPU, 432,218 MOVESPRITE BOLA, 278,233 'LOOP DO JOGO 'VOU REMOVER TUDO DO LOOP WHILE 1 SLEEP 1 MOVE_SPRITE JOGADOR MOVIMENTAR_BOLA BOLA MOVE_CPU CPU,BOLA WEND 'FUNÇÃO QUE DESENHA A TELA (REDE, LATERAIS, JOGADORES) DECLARE SUB DESENHA_TELA() 'DECLARA A VARIÁVEL DENTRO DA FUNÇÃO COMO LOCAL, A MEMÓRIA SERÁ LIBERADA APÓS O TERMINO DA FUNÇÃO (BOA PRÁTICA) LOCAL I AS INTEGER 'ITERA UMA VEZ PARA CADA COLUNA FOR I=1 TO 38 'AS POSIÇÕES DE MEMÓRIA ANTES DE 256 SÃO RESERVADAS PARA OS CARATERES. 'IMPRIME A LINHA SUPERIOR DRAWTILE 205,I,1 'IMPRIME A LINHA INFERIOR DRAWTILE 205,I,26 NEXT FOR I=2 TO 25 'IMPRIME A LINHA VERTICAL DRAWTILE 186, 19, I NEXT 'MOVE O CURSOR DE TEXTO PARA A POSIÇÃO 0,1 (O COMANDO LOCATE USA AS COORDENADAS Y,X E NÃO X,Y) LOCATE 1,0 'MUDA A COR PARA VERDE INK 2 PRINT "JOGADOR" LOCATE 1,35 PRINT "CPU" INK 1 'IMPRIME O PLACAR ZERADO LOCATE 1,17 PRINT 0 LOCATE 1, 19 PRINT 0 'VOLTA A COR ORIGINAL INK 0 END SUB 'FUNÇÃO QUE IMPRIME A TELA INICIADO DO JOGO E PRESSIONAR START PARA SEGUIR EM FRENTE DECLARE SUB APRESENTACAO() LOCATE 10,10 'MUDA A COR DO TEXTO PARA VERDE INK 2 PRINT "PONG - PRESS START" 'VOLTA A COR ORIGINAL INK 0 'EXECUTA UMA ESPERA ATÉ QUE O BOTÃO DE START SEJA PRESSIONADO. (USE O COMANDO SLEEP (BOA PRÁTICA)) WHILE JOYPAD() != 128 : SLEEP 2 : WEND CLS END SUB 'TESTA E MOVE O SPRITE DO JOGADOR, SE O MOVIMENTO FOR PERMITIDO DECLARE SUB MOVE_SPRITE(JOGADOR AS INTEGER) LOCAL JOY AS INTEGER JOY = JOYPAD() 'PARA CIMA IF JOY.0 THEN IF SPRITEPOSY(JOGADOR) > 142 THEN SHIFTSPRITE JOGADOR, 0, -1 ENDIF ELSE 'PARA BAIXO IF JOY.1 THEN IF SPRITEPOSY(JOGADOR) < 305 THEN SHIFTSPRITE JOGADOR, 0, 1 ENDIF ELSE 'START IF JOY.7 THEN LOCATE 12,12 INK 1 PRINT "=== PAUSE ===" INK 0 SLEEP 10 WHILE JOYPAD() != 128 : SLEEP 2 : WEND SLEEP 10 CLS ENDIF ENDIF ENDIF END SUB 'QUANDO A BOLINHA ESTIVER VINDO PRA VOCÊ O SENTIDO X É 1, OU 0 QUANDO ESTIVER INDO NA DIREÇÃO DO COMPUTADOR 'QUANDO A BOLINHA ESTIVER INDO PARA CIMA O SENTIDO Y É 1, OU 0 QUANDO ESTIVER INDO PARA BAIXO GLOBAL SENTIDOX AS INTEGER GLOBAL SENTIDOY AS INTEGER 'VOCÊ PROVAVELMENTE IRÁ SE PERGUNTAR, QUE PORRA É ESSA QUE EU FIZ... 'REALMENTE FIZ RÁPIDO, MAS NÃO TEM COMO FUGIR MUITO POIS NÃO TEMOS SUPORTE A VARIÁVEIS COM NÚMERO NEGATIVO. 'SE NÃO DAVA PARA FAZER ESSA FUNÇÃO EM BEM MENOS LINHAS, PARA SIMULAR ACABEI CRIANDO FLAGS DE SENTIDO. DECLARE SUB MOVIMENTAR_BOLA(BOLA AS INTEGER) LOCAL X AS INTEGER LOCAL Y AS INTEGER X=SPRITEPOSX(BOLA) Y=SPRITEPOSY(BOLA) 'TESTA SE NÃO FOI PONTO IF (X > 136) AND (X < 432) THEN 'NÃO FOI PONTO, VAMOS VERIFICAR SE CHEGOU EM ALGUMA PAREDE IF (Y = 139) THEN 'CHEGOU NA PAREDE DE CIMA, VAMOS TROCAR A DIREÇÃO DA BOLA NO EIXO Y SENTIDOY-- ELSE 'TESTANDO SE CHEGOU NA PAREDE DE BAIXO IF (Y = 332) THEN 'CHEGOU, VAMOS MUDAR DE DIREÇÃO DA BOLA NO EIXO Y SENTIDOY++ ENDIF ENDIF ELSE 'FOI PONTO 'COLOCAR A BOLA NA POSIÇÃO INICIAL MOVESPRITE BOLA,278,233 'VAMOS CHAMAR A SUB QUE ATUALIZA O PLACAR E PASSAR O SENTIDOX, 'CASO ESSE SEJA IGUAL A 0 O JOGADOR FEZ UM PONTO, E SE FOR IGUAL A 1 ELE SOFREU UM PONTO. PLACAR SENTIDOX 'MUDAR O SENTIDO DA BOLA, A BOLA IRÁ PRIMEIRO PARA O LADO DE QUEM FEZ GOL. IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF 'VAMOS TESTAR A COLISAO DO MODO MAIS SIMPLORIO QUE EXISTE.. IF COLLISION() THEN IF SENTIDOX THEN SENTIDOX-- ELSE SENTIDOX++ ENDIF ENDIF 'VAMOS MOVER A BOLA IF SENTIDOY THEN IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,-1 ELSE SHIFTSPRITE BOLA,1,-1 ENDIF ELSE IF SENTIDOX THEN SHIFTSPRITE BOLA,-1,1 ELSE SHIFTSPRITE BOLA,1,1 ENDIF ENDIF END SUB 'NADA DE MAIS AQUI, SÓ VARIAVEIS GLOBAIS PARA MARCAR A PONTUAÇÃO DE CADA JOGADOR GLOBAL PONTOS_JOGADOR AS INTEGER GLOBAL PONTOS_CPU AS INTEGER 'FUNÇÃO QUE ATUALIZA O PLACAR DECLARE SUB PLACAR(PONTO AS INTEGER) 'MUDA O PADRÃO DE ESCRITA PARA O PLANO SCROLL_B 'O PAUSE, ESTÁ NO PLANO SCROLL_A, E QUANDO EU REMOVO O PAUSE EU DEI UM CLS NO PLANO. 'ESTOU MUDANDO DE PLANO PARA ESCREVER O PLACAR SÓ PARA QUANDO O JOGO FOR DESPAUSADO NÃO LIMPAR O PLACAR JUNTO SETTEXTPLANE SCROLL_B 'CIANO INK 1 'TESTA DE QUEM FOI PONTO IF PONTO THEN 'CPU LOCATE 1,19 PONTOS_CPU++ PRINT PONTOS_CPU 'VOCÊ PERDE O JOGO SE O CPU TIVER 10 PONTOS IF PONTOS_CPU > 9 THEN LOCATE 12,8 PRINT "PERDEU, APERTE START" INK 0 WHILE JOYPAD() != 128 : SLEEP 2 : WEND RESET ENDIF ELSE 'JOGADOR LOCATE 1,17 PONTOS_JOGADOR++ PRINT PONTOS_JOGADOR 'VOCÊ GANHA O JOGO COM SE TIVER 10 PONTOS IF PONTOS_JOGADOR > 9 THEN LOCATE 12,8 PRINT "GANHOU, APERTE START" INK 0 WHILE JOYPAD() != 128 : SLEEP 2 : WEND RESET ENDIF ENDIF 'RETORNA A COR ORIGINAL INK 0 'VOLTA A ESCRITA PADRÃO NO PLANO DE CIMA SETTEXTPLANE SCROLL_A END SUB 'FUNÇÃO QUE REINICIA O JOGO, ZERA O PLACAR, LIMPA A TELA, LIBERA OS SPRITES DECLARE SUB RESET() PONTOS_JOGADOR=0 PONTOS_CPU=0 SENTIDOX=0 SENTIDOY=0 FREEALLSPRITES SETTEXTPLANE SCROLL_A CLS SETTEXTPLANE SCROLL_B CLS GOTO INICIO END 'OK, IA HORRIVEL :P 'FIZ SÓ PARA VEREM O JOGO FUNCIONANDO DECLARE SUB MOVE_CPU(CPU AS INTEGER, BOLA AS INTEGER) LOCAL BOLAY AS INTEGER LOCAL CPUY AS INTEGER BOLAY=SPRITEPOSY(BOLA) CPUY=SPRITEPOSY(CPU) IF BOLAY > CPUY THEN IF CPUY < 305 THEN SHIFTSPRITE CPU,0,1 ENDIF ELSE IF BOLAY < CPUY THEN IF CPUY > 142 THEN SHIFTSPRITE CPU,0,-1 ENDIF ENDIF ENDIF END SUB 'TILE DA RAQUETE DOS JOGADORES TILEDATA: DATALONG $00000000 ' Tile #0 DATALONG $00022000 DATALONG $00233200 DATALONG $02211220 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #0 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #2 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 ' Tile #1 DATALONG $02311320 DATALONG $02311320 DATALONG $02311320 DATALONG $02211220 DATALONG $00233200 DATALONG $00022000 DATALONG $00000000
Múltiplos inimigos na tela: Fazendo seu próprio Galaxian
editarIntermediário
editarVisão geral do hardware do Mega Drive
editarMovimentando os planos de fundo
editarAbaixo 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).
disable screen Dim Mapa(129,129) as Integer palette &h000E,0,1 pallettes paleta_aviao,1,0,15 loadtiles desenho_aviao,8,256 'desenho do aviao settextplane SCROLL_A setgfxplane SCROLL_B setscrollplane SCROLL_B loadtiles tiles_mapa,578,264 'carregar cenário pallettes paleta_mapa,2,0,16 reload mapa_dados For Y=0 to 129 For X=0 to 129 readint Mapa(X,Y) Next X Next Y For Y=0 to 29 For X=0 to 41 DrawTile Mapa(X+45,Y+51)+pallette(2)+264,X,Y Next X Next Y aviao=addsprite(2,2) propsprite aviao,256,1 enable screen ' liga a tela aviao_x=280 aviao_y=232 Y=308 X=332 mapa_X=45 mapa_Y=51 controleY=78 controleX=84 k=pallette(2)+264 do sleep 1 movesprite aviao,aviao_x,aviao_y J = JOYPAD() if J.0 then gosub cima if J.1 then gosub baixo if J.2 then gosub esquerda if J.3 then gosub direita locate 0,0 :PRINT "Y= ";Y;" " locate 1,0 :PRINT "controleY= ";controleY;" " locate 2,0 :PRINT "mapa_Y= ";mapa_Y;" " locate 3,0 :PRINT "bufferY= ";bufferY;" " locate 4,0 :PRINT "aviao_y ";aviao_y;" " locate 6,0 :PRINT "X= ";X;" " locate 7,0 :PRINT "controleX= ";controleX;" " locate 8,0 :PRINT "mapa_X= ";mapa_X;" " locate 9,0 :PRINT "bufferX= ";bufferX;" " locate 10,0 :PRINT "aviao_x "; aviao_x;" " loop cima: if Y !=0 then if Y < 513 [and] Y > 104 then desenha = Y >> 2 if desenha < controleY then controleY=desenha mapa_Y-- bufferY-- bufferY=bufferY [and] 63 Q=bufferX For cont=0 to 41 Q=Q [and] 63 DrawTile Mapa(CONT+mapa_X,mapa_Y)+k,Q,bufferY Q++ Next cont end if scroll DOWN,2 else aviao_y=aviao_y-1 end if Y-- end if return baixo: if Y !=618 then if Y < 512 [and] Y > 103 then desenha = Y >> 2 if desenha > controleY then controleY=desenha mapa_Y++ bufferY++ bufferY=bufferY [and] 63 Q=bufferX For cont=0 to 41 Q=Q [and] 63 DrawTile Mapa(CONT+mapa_X,mapa_Y+29)+k,Q,bufferY+29 Q++ Next cont end if scroll UP,2 else aviao_y=aviao_y+1 end if Y++ end if return esquerda: if X !=0 then if X < 513 [and] X > 152 then desenha = X >> 2 if desenha < controleX then controleX=desenha mapa_X-- bufferX-- bufferX=bufferX [and] 63 Q=bufferY For cont=0 to 29 Q=Q [and] 63 DrawTile Mapa(mapa_X,CONT+mapa_Y)+k,bufferX,Q Q++ Next cont end if scroll RIGHT,2 else aviao_x=aviao_x-1 end if X-- end if return direita: if X !=664 then if X < 512 [and] X > 151 then desenha = X >> 2 if desenha > controleX then controleX=desenha mapa_X++ bufferX++ bufferX=bufferX [and] 63 Q=bufferY For cont=0 to 29 Q=Q [and] 63 DrawTile Mapa(mapa_X+41,CONT+mapa_Y)+k,bufferX+41,Q Q++ Next cont end if scroll LEFT,2 else aviao_x=aviao_x+1 end if X++ end if return tiles_mapa: datafile ARQUIVO.bin,bin 'arquivo com os tiles paleta_mapa: DATAINT $000E,$000E,$0EEE,$0000,$0000,$0000,$0000,$0000 ' paleta 2 DATAINT $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000 mapa_dados: datafile MAPA_DADOS.bin,bin 'arquivo do mapa desenho_aviao: DATALONG $00000000 ' Tile: 0 DATALONG $00000000 DATALONG $FFFF0000 DATALONG $F33F0000 DATALONG $F33F0000 DATALONG $F33FFFFF DATALONG $F3333333 DATALONG $F3333333 DATALONG $0F333333 ' Tile: 1 DATALONG $00F33333 DATALONG $000F3333 DATALONG $0000FFFF DATALONG $00000000 DATALONG $00000000 DATALONG $00000000 DATALONG $00000000 DATALONG $00000000 ' Tile: 2 DATALONG $0BB00000 DATALONG $0B2F0000 DATALONG $9DDFF000 DATALONG $9DFFEF00 DATALONG $FFFFFFF1 DATALONG $333333F1 DATALONG $3F33F3F1 DATALONG $3F33F3FF ' Tile: 3 DATALONG $3F33F3F1 DATALONG $3F33F3F1 DATALONG $FF33FFF1 DATALONG $0F33F000 DATALONG $00FF0000 DATALONG $00000000 DATALONG $00000000 DATALONG $00000000 ' Tile: 0 DATALONG $0000BB00 DATALONG $00002B00 DATALONG $000EDD90 DATALONG $10EEDD90 DATALONG $10FFFFFF DATALONG $1F333333 DATALONG $1F3F3333 DATALONG $9F3F3333 ' Tile: 1 DATALONG $9F3F3333 DATALONG $1F3F3333 DATALONG $1F3F3333 DATALONG $10FF3333 DATALONG $100F3333 DATALONG $0000F33F DATALONG $00000FF0 DATALONG $00000000 ' Tile: 2 DATALONG $00000000 DATALONG $0000FFFF DATALONG $0000F33F DATALONG $0000F33F DATALONG $FFFFF33F DATALONG $3333333F DATALONG $F333333F DATALONG $F33333F0 ' Tile: 3 DATALONG $F3333F00 DATALONG $F333F000 DATALONG $F33F0000 DATALONG $FFF00000 DATALONG $F0000000 DATALONG $00000000 DATALONG $00000000 paleta_aviao: DATAINT $0626,$00EE,$0E0E,$000E,$0EE0,$00E0,$0E00,$0888 ' paleta 1 DATAINT $0CCC,$0088,$0808,$0008,$0880,$0080,$0800,$0000
Som básico: Usando o PSG
editarAvançado
editarDescrição das portas de I/O do Mega Drive
editarVDP
editar$C00000 (Porta de dados)
$C00004 (Porta de controle)
Leitura
* | * | * | * | * | * | VAZIO | CHEIO |
F | SOVR | C | IMPAR | VB | HB | PAL |
VAZIO 1: FIFO de escrita vazio 0: CHEIO 1: FIFO de escrita cheio 0: F 1: Ocorreu interrupção vertical SOVR 1: Ocorreu estouro de sprites. Muitos na mesma linha. Mais de 17 no modo de 32 células. Mais de 21 no modo de 40 células. C 1: Ocorreu colisão entre dois pixels não nulos entre dois sprites. 0: IMPAR 1: Quadro ímpar no modo entrelaçado. 0: Quadro par no modo entrelaçado. VB 1: Durante o "blanking" vertical 0: HB 1: Durante o "blanking" horizontal 0: DMA 1: Realizando DMA 0: PAL 1: Modo PAL 0: Modo NTSC
Escrita 1: Enviar dados ao registrador
1 | 0 | 0 | RS4 | RS3 | RS2 | RS1 | RS0 |
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
RS4 ~ RS0 : Número do registrador D7 ~ D0 : Dados a enviar
- 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 |
CD5 ~ CD0 : Código de indentificação A15 ~ A0 : Endereço de destino
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 |
HC8 ~ HC1 : Posição horizontal VC8 ~ VC0 : Posição vertical
Registradores
REG 0: Registrador de modo número 1
0 | 0 | 0 | IE1 | 0 | 1 | M3 | 0 |
IE1 1: Habilita interrupção horizontal (68000 nível 4) 0: Desabilita interrupção horizontal (REG #10) M3 1: Parar o contador HV 0: Habilita o contador HV para leitura
REG 1: Registrador de modo número 2
0 | DISP | IE0 | M1 | M2 | 1 | 0 | 0 |
DISP 1: Habilita display 0: Desabilita display IE0 1: Habilita interrupção vertical (68000 nível 6) 0: Desabilita interrupção vertical M1 1: Habilita DMA 0: Desabilita DMA M2 1: Modo 30 células na horizontal (modo PAL) 0: Modo 28 células na horizontal (Modo PAL, sempre 0 em modo NTSC)
REG 2: Endereço base do mapa do fundo A
0 | 0 | SA15 | SA14 | SA13 | 0 | 0 | 0 |
Endereço da VRAM: $XXX0_0000_0000_0000
REG 3: Endereço base do mapa da janela
0 | 0 | WD15 | WD14 | WD13 | WD12 | WD11 | 0 |
WD11 deve ser 0 no modo de 40 células horizontais Endereço da VRAM: $XXXX_X000_0000_0000 (32 células horizontais) Endereço da VRAM: $XXXX_0000_0000_0000 (40 células horizontais)
REG 4: Endereço base do mapa do fundo B
0 | 0 | 0 | 0 | 0 | SB15 | SB14 | SB13 |
Endereço da VRAM: $XXX0_0000_0000_0000
REG 5: Endereço base da tabela de atributos dos sprites
0 | AT15 | AT14 | AT13 | AT12 | AT11 | AT10 | AT9 |
WD9 deve ser 0 no modo de 40 células horizontais Endereço da VRAM: $XXXX_XXX0_0000_0000 (32 células horizontais) Endereço da VRAM: $XXXX_XX00_0000_0000 (40 células horizontais)
REG 7: Cor do fundo
0 | 0 | CPT1 | CPT0 | COL3 | COL2 | COL1 | COL0 |
CPT1 ~ CPT0 : Número da palheta COL3 ~ COL0 : Código da cor
REG 10: Interrupção horizontal
BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 |
Conta o número da linha horizontal da tela sendo atualmente renderizado
REG 11: Registrador de modo número 3
0 | 0 | 0 | 0 | IE2 | VSCR | HSCR | LSCR |
IE2 1: Habilita interrupção externa (68000 nível 2) 0: Desabilita interrupção externa
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 |
RS0 0: Modo de 32 células horizontais 1: Modo de 40 células horizontais RS1 0: Modo de 32 células horizontais 1: Modo de 40 células horizontais * É recomendável setar o mesmo valor para RS0 e RS1. S/TE 1: Habilita sombreamento e realce 0: Habilita sombreamento e realce LSM1, LSM0: Modo de entrelaçamento
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 |
Endereço da VRAM: $XXXX_XX00_0000_0000
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 |
INC7 ~ INC0: Tamanho do incremento ( 0 ~ $FF )
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 |
RIGT 0: Janela está à esquerda do ponto base 1: Janela está à direita do ponto base WHP5 ~ WHP1 Posição horizontal em células, isto é de 8 em 8 pixels
REG 18: Posição vertical da janela
DOWN | 0 | 0 | WVP4 | WVP3 | WVP2 | WVP1 | WVP0 |
DOWN 0 : Janela está acima do ponto base 1 : Janela está abaixo do ponto base WVP4 ~ WVP0 Posição vertical em células
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 |
SA22 ~ SA1 : Endereço de origem de DMA DMD1, DMD0 : Modo de DMA
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
void init_GFX() { register unsigned int *pw; pw = (uint *) 0xC00004; /* Aponta para a porta de controle */ *pw = 0x8016; /* reg. 0 - Habilita HBL */ *pw = 0x8174; /* reg. 1 - Habilita display, VBL, DMA e seta a largura */ *pw = 0x8230; /* reg. 2 - Plano A =$30*$400=$C000 */ *pw = 0x832C; /* reg. 3 - Janela =$2C*$400=$B000 */ *pw = 0x8407; /* reg. 4 - Plano B =$7*$2000=$E000 */ *pw = 0x855E; /* reg. 5 - Tabela de sprites em $BC00=$5E*$200 */ *pw = 0x8700; /* reg. 7 - Cor do fundo */ *pw = 0x8a01; /* reg 10 - HInterrupt timing */ *pw = 0x8b00; /* reg 11 - $0000abcd a=interrupção externa b=scroll horiz. cd=scroll vert. */ *pw = 0x8c81; /* reg 12 - células horiz + sombreamento/realce + modo entrelaçamento (40 células, sem sombreamento, sem entrelaçamento)*/ *pw = 0x8d2E; /* reg 13 - Tabela de deslocamento horizontal = $B800 */ *pw = 0x8f02; /* reg 15 - auto incremento */ *pw = 0x9011; /* reg 16 - tamanho dos planos de fundo (64x64) */ *pw = 0x9100; /* reg 17 - posição horizontal da janela */ *pw = 0x92ff; /* reg 18 - posição vertical da janela */ };
Programação assembly 68k
editarDocumentação Assembly
editarA extensa documentação do assembly do 68k pode ser encontrada no Devega.
Cabeçalho da ROM
editarO 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.).
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.