segunda-feira, julho 21, 2014

Apenas com o DevKitPro e a sua biblioteca LibNDS

Bom, PALib morreu. E antes de morrer já era incompatível com as versões mais novas (da época) do DevKitPro.

Resolvi então tentar aprender a mexer só com o DevKitPro, que vai sendo atualizado e a biblioteca NDS não se torna incompatível.

Vamos então ao primeiro programa:
#include <nds.h>
#include <stdio.h>

void clearScreen(r, g, b) {
    int i;
    for (i = 0; i < 256*192; i++) VRAM_A[i] = RGB15(r, g, b);
}

int main(void)
{
    int r = 0;
    int g = 0;
    int b = 0;
    int down;

    consoleDemoInit();
    videoSetMode(MODE_FB0);
    vramSetBankA(VRAM_A_LCD);

    while(1)
    {
        scanKeys();
        down = keysDown();
        if (down & KEY_DOWN) {
            r--;
            if (r < 0) r = 0;
        }
        if (down & KEY_UP) {
            r++;
            if (r > 31) r = 31;
        }
        if (down & KEY_B) {
            g--;
            if (g < 0) g = 0;
        }
        if (down & KEY_X) {
            g++;
            if (g > 31) g = 31;
        }
        if (down & KEY_L) {
            b--;
            if (b < 0) b = 0;
        }
        if (down & KEY_R) {
            b++;
            if (b > 31) b = 31;
        }
        if (down & KEY_START) {
            r = 0;
            g = 0;
            b = 0;
        }
        if (down & KEY_SELECT) {
            r = 31;
            g = 31;
            b = 31;
        }
       
        clearScreen(r, g, b);   
        iprintf("\x1b[10;0H    R = %02i\n", r);
        iprintf("    G = %02i\n", g);
        iprintf("    B = %02i\n", b);
           
   
        swiWaitForVBlank();
    }

    return 0;
}

 O programa acima mostra os dados RGB na tela de baixo e o resultado das cores na tela de cima. O comando consoleDemoInit diz que a tela de baixo vai ser um terminal texto. O comando videoSetMode(MODE_FB0)diz que a tela de cima vai ser frame buffer (são 256x192 pixels de 16 bits cada). O comando vramSetBankA(VRAM_A_LCD)aloca a memória usada pelo frame buffer.
O comando scanKeys deve ser rodado a cada loop para verificar o estado dos botões e da touch. A função keysDown é usada para ler o estado de cada botão somente se ele recém foi pressionado. Se eu quisesse pegar os botões que estão sendo mantidos apertados, o comando seria keysHeld e se quisesse os botões que foram recém soltos, o comando seria keysUp. O resultado da leitura dos botões deve ser "shifted" para ler os dados de cada botão individual. As informações de shift estão indexadas pelas seguntes constantes:
  KEY_A = BIT(0),
  KEY_B = BIT(1),
  KEY_SELECT = BIT(2),
  KEY_START = BIT(3),
  KEY_RIGHT = BIT(4),
  KEY_LEFT = BIT(5),
  KEY_UP = BIT(6),
  KEY_DOWN = BIT(7),
  KEY_R = BIT(8),
  KEY_L = BIT(9),
  KEY_X = BIT(10),
  KEY_Y = BIT(11),
  KEY_TOUCH = BIT(12),
  KEY_LID = BIT(13) 

 Por isso, para verificar se a seta para cima recém foi apertada, usei if (down & KEY_UP)

terça-feira, dezembro 26, 2006

Tentando explorar limites

Eu fiz um fundo tamanho 12800x192 para tentar fazer uma fase completa para um side shooter em um único fundo e funcionou.

O arquivo convertido para tiles ficou com 752 tiles e 97 cores

Programa e código-fonte

segunda-feira, dezembro 25, 2006

Video Poker

Concluí o meu primeiro homebrew "jogável"!!!!!!!

Está pronto o video poker. Fiz ele considerando qualquer "um par" ao invés de "valetes ou maior" (mais comum no video poker). Se eu achar que ele ficou muito repetitivo, vou repensar. Mas acho que não, a minha idéia é agora partir para outro, não ficar alterando este aqui, que não tem muito para tirar daqui. Valeu por ser o primeiro, mas é só.

Programa e fonte no rapidshare

sábado, dezembro 23, 2006

Voltou!!!!

Meu carregador voltou!!!!!

Agora fui testar o programa das cartas e adivinhem: eram os emuladores (testei no no$gba, no EmuDS e no DualiS) que estavam limitando meus gráficos. Com o DS gerou todas as cartas inclusive as viradas. Segundo o tutorial, o DS gerencia até 128 sprites. O emulador estava limitando em 52 de 32x64 e 26 de 64x64. Vou fazer mais uns testes de gráficos antes de dar andamento no programa.

Ps.: desisti de tentar cartas mais desenhadas, estão dando muito trabalho e não estão legíveis. Por enquanto ficam assim. Se alguma alma caridosa quiser me passar imagens de cartas em no máximo 32x64 e mínimo de 32x48 e que fiquem legíveis, publica em algum lugar e posta o link nos comentários. Se alguém fizer, eu agradeço.

sexta-feira, dezembro 22, 2006

Quem agüenta?

Não agüentei ficar sem programar. Decidi fazer um programa que pudesse rodar em um emulador. Estou usando o no$gba e rodou perfeito este programa (só tem que usar o arquivo gerado .ds.gba).

Pensei num video poker, algo que nunca programei.
Quem olhar o fonte vai notar algo sem otimização nenhuma, pois aqui o que eu queria era desenvolver a lógica. Não consultei nada sobre algoritmos de jogos de cartas, queria desenvolver eu mesmo. É claro, que em 1985, quando eu estava me aprofundando em programação Basic, eu li um algoritmo de como embaralhar cartas e nunca mais esqueci qual era a lógica (na época eu não usei o algoritmo então não sabia se a lógica realmente funcionava).

Embaralhando: a lógica é fazer um vetor com 52 posições, depois ir sorteando valores (rand) e preenchendo o vetor. A cada valor sorteado, percorrer toda a parte já preenchida do vetor para ver se esse valor já não foi sorteado, se foi sorteia outro valor e repete a verificação. Senão, insere esse valor e avança.

O jogo de video poker não é uma pessoa jogando contra a outra, é só um jogador e não existe inteligência artificial envolvida (pelo menos não nos jogos isentos). A lógica é receber a aposta do jogador e pagar um multiplicador pela aposta. Eu fiz a tabela de pontuação assim:
1 par = x1 (recebe de volta o que apostou);
2 pares = x2
trinca = x4
seqüência = x6
flush = x8
full house = x12
quadra = x16
straight flash = x20
royal straight flash = x32

O programa que eu fiz ainda não é o jogo. Ele embaralha e coloca as primeiras 5 cartas na tela, e mostra na outra tela o valor obtido. Se apertar A, ele embaralha de novo e mostra as 5 primeiras cartas com o valor novamente. Se apertar B, ele faz um ciclo de jogos programados, que eu fiz para testar se a minha lógica de análise do jogo para contagem dos pontos estava funcionando.

As cartas, inicialmente tinha desenhado num tamanho 40x56 pixels (acomodado em um sprite de 64x64 pixels). Ficou um lixo, esgotou a memória de vídeo e não conseguiu criar todos os sprites. Aí eu reduzi as imagens para 32x48. Daí o tamanho dos sprites passaram a 32x64. Deu justo para as 52 cartas. Não consegui criar nem o sprite do verso das cartas. Ainda bem que poker não usa coringa.

Link para o programa e o fonte no rapidshare

segunda-feira, dezembro 18, 2006

Pausa

Vou ter que dar um tempo na programação. Eu fui no fim-de-semana na colônia, na granja de um amigo. Levei o DS e o carregador e só voltou o DS, não achei mais o carregador (e só lembrei que tinha levado o carregador quando voltei para casa). Quando fui carregar a bateria domingo de noite, cadê o maldito. O pior que meu amigo não mora lá. Vou ter que esperar ele ir para a colônia de novo para tentar procurar. Acho que ele só vai para lá no Natal (é pouco tempo, mas parece uma eternidade).

Moral da história: não vou programar porque não tenho onde testar.

sexta-feira, dezembro 15, 2006

Rodando um filme com o PAFS

A idéia que eu havia tido de rotacionar o fundo 256 a 256 pixels ficou horrível. A gente podia ver os tiles sendo redesenhados. Ficava um pisca-pisca desgraçado.

Eu tentei então fazer dois níveis de fundos, um estático e outro com um pouco de animação. Só que ficou um com 506 tiles e o outro com mais de 700 tiles. Aí faltava memória de vídeo para carregar os dois.

Minha terceira tentativa de fazer um fundo animado foi ir carregando imagens JPG do PAFS. E o mais incrível é que funcionou! Só que 6/8 da memória de vídeo estão comprometidos, pois o JPG é considerado um fundo 16 bits (gif também é considerado 16 bits apesar de ter no máximo 256 cores - eu tentei colocar um gif num fundo 8 bits e o programa não rodou).

Na verdade o programa ficou com um fundo que é um filme. Aí o problema foi outro: como eu peguei um vídeo da internet e cortei 10 frames dele para fazer a animação de fundo, rodando ele em velocidade normal ficava nítido onde era o corte. Aí eu coloquei um contador e fiz o fundo ser atualizado a cada 8 frames. Ainda se percebe que há um corte, mas parece que os outros quadros também têm cortes, tornando a animação mais parelha (emparelhei pra pior, mas emparelhei). Quando eu conseguir uma animação mais estável, eu tiro o contador.

Link para o programa e o fonte no rapidshare

Ps.: para extrair as imagens frame a frame do vídeo, eu usei um freeware chamado tmpgenc.

terça-feira, dezembro 12, 2006

Aprofundando no sistema de arquivos

Agora chogou um ponto que o que o tutorial ensina eu não quero aprender (agora). Mais adiante vou continuar seguindo o tutorial. Por enquanto estou estudando os exemplos que vêm com a PAlib, mais o manual dela, e chegando às minhas próprias conclusões.

Eu fiz um programa que lê backgrounds do File System. Cada vez que aperta R, muda o background. O importante é que isso não fica limitado aos 4Mbytes de RAM do DS. Posso ir até 1Gbit de imagens.

As imagens que eu usei são paisagens paradas que tirei da internet, com o photoshop redimensionei para 256x192 e converti para 255 cores. Depois com o PAGfx converti para tilebackground. O PAGfx não gera só os .c dos arquivos, ele gera os .bin também. São esses .bin que vou carregando do FS (cada imagem tem 4 arquivos: o Info, o Map, o Pal e o Tile, que devem ser copiados nessa ordem).

Próximo passo, quero fazer fundos animados. A idéia é a seguinte: para um fundo com 4 frames, criar uma imagem de 1024x192, A cada 256 pixels de largura, coloco um frame da animação. Depois é só ir fazendo scroll X de 256 pontos.

Link para o programa com fonte no Rapidshare

sexta-feira, dezembro 08, 2006

Uma pitadinha de 3d

O tutorial é muito pobre em se tratando de 3d. O único programa que veio explicado coloca uma caixa 3d parada na tela, sem sombra nem nada.

A única coisa que fiz em cima disso foi permitir o giro do objeto: esquerda e direita rotacionam o eixo X do objeto, cima e baixo rotacionam o eixo Y e L e R o eixo Z.

Já vi que esse tutorial só ensina a fazer jogos tipo sonic e super mario bros (donkey kong de nes também hehehe).

Link com os programas e o fonte no rapidshare

Rompendo a barreira dos 4 MB

Pelo que consegui ler na internet, é 4 MBytes mesmo (32Mbits). Essa é a memória RAM do DS. Teoricamente, nenhum jogo poderia ter mais que essa memória; mas, na verdade, eles podem ter até 1GBit (128MB). A solução é criar um sistema de arquivos (file system). Isso a gente consegue usando as rotinas PAFS da PAlib.

Como funciona: Programa normal, usando as rotinas (depois eu falo um pouco delas); depois compila o programa normalmente; copia o PAFS.exe para a mesma pasta do arquivo compilado; cria uma pasta Files e copia os arquivos que vão ser usados no programa ali; abre uma janela dos e vai para a mesma pasta do arquivo compilado; roda:
PAFS nome_do_arquivo_compilado_com_extensao
Ele vai gerar um novo arquivo contendo o arquivo compilado somado aos arquivos da pasta Files.

Vamos ao fonte:
narq = PA_FSInit(); Inicia o File System, dizendo quantos arquivos existem (neste caso eu armazeno na variável narq). Além disso, esse comando cria um array PA_FSFile indexado pelo número do arquivo e contendo diversos dados, tipo nome, extensão, tamanho.

Para carregar um arquivo para a memória, depende das funções de aplicação, assim podemos carregar um arquivo mod, um arquivo raw e uma imagem e termos os três simultaneamente na memória. Quando carrega o segundo arquivo para a mesma aplicação, o primeiro é removido da memória.

Assim, podemos quebrar a barreira dos 4MB. Mas temos que tomar cuidado, pois nesses 4 MB tem que ter espaço para o programa e para os arquivos que forem carregados (e isso é muito fácil de ultrapassar quando a gente começa a gostar de colocar imagens e sons no programa.

O programa que eu criei para testar é um jukebox de músicas MOD. Eu coloquei um fundo parallax para não ficar tudo parado. O programa não começa a música seguinte após terminar a primeira, pois não pretendo gastar tempo de aprendizado otimizando um programa simplório. As setas para cima e para baixo movem a seleção, e o botão A inicia a reprodução do mod escolhido. Eu coloquei 8 mods para não ficar muito grande o arquivo para download (os mods não foram junto com o fonte, quem quiser, peguei esses mods no site http://www.modarchive.com

Link para o programa e os fontes no rapidshare

quinta-feira, dezembro 07, 2006

Acessando o hardware

Para pausar o programa quando é fechado o ds, basta colocar PA_CheckLid(); antes do PA_WaitForVBL();

Pode-se ter acesso também ao acendimento das lâmpadas das telas. Um programa feito para uma só tela pode apagar a outra e poupar energia.

Tem uma série de definições, que são as variáveis do hardware: data, hora, aniversário do dono, nome do dono, etc.

Segue um programinha que apresenta uma mensagem para quem aniversaria e também para quem não aniversaria, piscando as telas, pausando quando o ds é fechado.

// Includes
#include // Include for PA_Lib

// Function: main()
int main(int argc, char ** argv)
{
  s16 contador=0;

  PA_Init(); // Initializes PA_Lib
  PA_InitVBL(); // Initializes a standard VBL

  PA_InitText(0,0);

  PA_OutputSimpleText(0,1,1,"Parabens!!!");
  PA_OutputText(0,1,3,"%s",PA_UserInfo.Name);
  if (PA_RTC.Month==PA_UserInfo.BdayMonth
        &&PA_RTC.Day==PA_UserInfo.BdayDay) {
    PA_OutputSimpleText(0,1,5,"Hoje voce completa mais um ano");
  } else {
    PA_OutputSimpleText(0,1,5,"Voce nao envelheceu um ano hoje");
  }
  PA_SetScreenLight(0, 1);
  PA_SetScreenLight(1, 1);

  // Infinite loop to keep the program running
  while (1)
  {
    if (contador==7) PA_SetScreenLight(1, 0);
    else if (contador==15) PA_SetScreenLight(1, 1);
    else if (contador==21) PA_SetScreenLight(0, 0);
    else if (contador>=30) {
      PA_SetScreenLight(0, 1);
      contador=0;
    }
    contador++;
    PA_CheckLid(); //para verificar se foi fechado o ds e pausar
    PA_WaitForVBL();
  }

  return 0;
} // End of main()

Fazendo barulho

O DS pode reproduzir sons raw (tipo o wave mas sem cabeçalho) ou música mod. A PAlib ainda tem bastante bugs na parte de som e os dois formatos ainda não se misturam, a gente tem que escolher um ou o outro. Para produzir ruídos e música simultaneamente a solucão é usar uma música curta e reproduzir como som mesmo.

Como fazer: coloque o arquivo de som na pasta data (o arquivo mod é similar ao midi, mas com instrumentos digitalizados, o arquivo raw é obtido convertendo um wav; pode usar o switch gratuito ( http://www.nch.com.au/switch/ ) que ele desempenha a função.

PA_InitSound() prepara o ds para produzir sons
PA_PlaySimpleSound(canal,nome_do_arquivo_raw) reproduz o arquivo raw no canal selecionado
PA_PlayMod(nome_do_arquivo_mod) reproduz o arquivo mod infinitamente

Tem mais uma pilha de funções que não estão no tutorial e o manual mal fala delas. Isso tem que esperar um pouco ainda.

Como não consegui fazer funcionar direito, não vou colocar o arquivo aqui, pelo menos por enquanto.

quarta-feira, dezembro 06, 2006

Dicas para o tratamento numérico

O DS é extremamente lento em operações de ponto flutuante. O autor do PAlib recomenda que, quando precisarmos de valores entre 0 e 1, se utilize um inteiro (s32, por exemplo) com bit shift. Ele utiliza 8 bits, dessa forma, 256>>8 é 1. Dessa forma eu poderia ter feito o seguinte no controle dos 3 fundos (sem usar o parallax):
while (1) {
  x0+=512;
  x1+=256;
  x2+=128;
  PA_EasyBgScrollXY(1, 0, x0>>8, 0);
  PA_EasyBgScrollXY(1, 1, x1>>8, 0);
  PA_EasyBgScrollXY(1, 2, x2>>8, 0);
}
Seria algo como dizer: x0+2; x1+=1; x2+=0.5; sem usar ponto flutuante.

Outra coisa: como eu havia notado, na rotação dos sprites, o giro completo é 512. A relação é a seguinte: 0 = 0°; 128 = 90°; 256 = 180°; 384 = 270°; 512 = 360°. A PAlib tem rotina para calcular seno e cosseno desse angulo do DS (PA_Sin e PA_Cos), só que os limites são -256 a 256, ao invés de -1 a 1 (justamente pelo bit shift). Isso é útil em cálculo de tragetória (esquerda e direita rotaciona o sprite, cima move para a frente, baixo move para trás). Daí a solução pode ser assim:
if (Pad.Held.Up){ // Move para a frente
  x += PA_Cos(angulo);
  y -= PA_Sin(angulo);
}
if (Pad.Held.Down){ // Move para trás
  x += -PA_Cos(angulo);
  y -= -PA_Sin(angulo);
}

Números aleatórios são gerados pela função PA_Rand, só que essa função é meio inútil pois a gente não tem controle sobre os limites dos números gerados. Para gerar um n° aleatorio entre 0 e x, usar PA_RandMax(x); para gerar um n° aleatorio entre x e y, usar PA_RandMinMax(x,y); PA_SRand(semente) serve para definir uma semente específica para os nros aleatórios; PA_InitRand() inicializa a semente de números aleatórios baseando no relógio do DS (fica muito mais aleatório).

O resto, em C é %. Assim 3%2 retorna 1. Quando o segundo valor for potência de dois, pode-se usar o resto binário (&), ganhando performance (só o segundo valor tem que ser diminuido 1). por exemplo x%4 retorna o mesmo valor que x&3.

True vale 1 e false vale 0. Com isso pode-se evitar o comando if, que é bastante lento por consumir muitos ciclos de clock para ser executado.
if (soma==true) {
  x++
}
pode ser substituido por
x+=(soma==true);

Com essa lógica matemática fica fácil verificar colisões. Considerando que os desenhos nos sprites tendem a ter uma forma redonda, ou uma área de contato redonda, verificar a colisão significa ver se a distância dos centros dos dois sprites é menor ou igual à soma do raio das duas áreas redondas. Para ver a distância de dois pontos, tem a função PA_Distance(x1,y1,x2,y2).
Se os sprites tenderem à forma quadrada, basta verificar o contato das bordas.

terça-feira, dezembro 05, 2006

Scroll no fundo com parallax

Quem quiser ver como fazer com parallax, segue aqui o fonte. É só substituir o main.c do post anterior por este aqui. O resultado visual ficou exatamente o mesmo. Uma diferença é que com parallax, eu posso colocar velocidades de backgrounds mais próximas do que no scroll ponto a ponto.

// Includes
#include // Include for PA_Lib

#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"

// Function: main()
int main(int argc, char ** argv)
{
  //variaveis
  s32 x=95;
  s32 y=79;
  s32 scrl=0;

  //programa
  PA_Init(); // Initializes PA_Lib
  PA_InitVBL(); // Initializes a standard VBL

  PA_EasyBgLoad(1, 0, fundo3); // carrega, na tela 1, no nivel de fundo 0, o fundo3
  PA_EasyBgLoad(1, 1, fundo4); // carrega, na tela 1, no nivel de fundo 1, o fundo4
  PA_EasyBgLoad(1, 2, fundo5); // carrega, na tela 1, no nivel de fundo 2, o fundo5

  PA_InitParallaxX(1, 512, 256, 128, 0); // define o parallax horizontal com velocidade dobrada, normal, metade e sem parallax (0)

  PA_LoadSpritePal(1, 0, (void*)aviao_Pal);
  PA_CreateSprite(1, 0, (void*)aviao_Sprite, OBJ_SIZE_64X32, 1, 0, x, y);

  // Infinite loop to keep the program running
  while (1)
  {
    scrl ++;
    if (scrl>1024) scrl=0;
    PA_ParallaxScrollX(1, scrl); //parallax na tela 1, conforme o valor de scrl;

    x += (Pad.Held.Right - Pad.Held.Left) * 2;
    y += (Pad.Held.Down - Pad.Held.Up) * 2;
    if (y<0) y=0;
    if (y>159) y=159;
    if (x<0) x=0;
    if (x>191) x=191;

    PA_SetSpriteXY(1,0,x,y);

    PA_WaitForVBL();
  }

  return 0;
} // End of main()

Mais fácil que coçar um pé com o outro

Durante a semana, meu tempo para estudar é bem menor. Ontem não pude nem olhar o tutorial que estou seguindo, por pura falta de tempo. Hoje o meu filho pegou no sono um pouco mais cedo e resolvi arriscar o capítulo 5: fundo (background).

Fazer fundo no PAlib é a coisa mais fácil do mundo: carrega a imagem 256x256 ou 512x512 no conversor, gera os .c das imagens, dá o include no main.c e com dois comandos insere a imagem e movimenta a imagem. 10 vezes mais fácil que sprites.

Neste exemplo, eu peguei um landscape da internet (pelo google images), cortei ele em 128x256, fiz um flip horizontal e colei do lado (aí ficou 256x256 sem marcas de emendas, apesar da gente perceber obviamente que foi espelhado no photoshop), recortei em 3 pedaços (céu, montanhas e planície), converti os 3, coloquei cada um em um nível de background (o ds tem 4), fiz o controle manual da ação, com cada nível numa velocidade, side scroller tipo os cenário do Fatal Fury (tem os comandos parallax, que deixam isso automatizado, mas eu queria pegar o touro à unha mesmo). Ficou muito rápido (era de deixar o cara tonto). Tive que colocar um contador para diminuir um pouco (não queria ninguém chamando o hugo em cima do ds). Foi tão fácil que fui atrás da imagem de um aviazinho para colocar em um sprite voando sobre o cenário. Só o aviãozinho ficou com um contorno magenta por causa que o photoshop fez um anti-alias na cor que seria transparente (uso magenta normalmente pois é uma cor muito difícil de se usar - a não ser que eu estivesse fazendo um jogo da barbie), aí um tom ficou transparente e o outro apareceu.

Link para o programa com o fonte no rapidshare

domingo, dezembro 03, 2006

Sprites animados pesseando pelas duas telas

Para fazer um sprite animado, contendo diversos frames de animação, todos os frames devem estar numa única imagem, daí essa imagem é convertida.
Na hora de criar o sprite, a gente tem que informar qual o tamanho, daí a biblioteca corta os pedaços da imagem conforme o tamanho informado e define os frames.
A animação pode ser feita dos frames x a y, com isso podemos ter diversas animações num sprite. Por exemplo uma pessoa caminhando para cima, caminhando para a direita, etc.

Normalmente se define em qual tela vai ficar cada sprite, mas pode-se definir, através dos comandos "Dual" que as duas telas vão ser tratadas como uma só, virando um telão vertical.

Link para o programa e ´código-fonte no RapidShare

Continuando com sprites

Agora já consigo rotacionar e controlar o zoom dos sprites. A rotação/zoom não é feita sprite por sprite. Existem 32 rotações/zoom por tela. O que se faz é associar o sprite à rotação/zoom. Tem 3 funções: uma só rotaciona, outra só dá zoom e a terceira faz os dois (fazer só uma operação consome menos processador).

Link para o programa com código-fonte

Começanco a usar imagens (sprites 1)

A partir de agora, não dá mais para publicar o código-fonte aqui, pois as imagens também são programadas e fica muito longo. Estou incluindo o fonte junto com o arquivo do programa.

Para gerar o programa das imagens, eu usei o PAGfx, que veio junto com a PALib, dentro de tools. O frontend dele para windows precisa do dotNET Framework.

O programa é muito simples: é a Millennium Falcon correndo na tela de baixo controlada pelas setas. O botão A acelera e o B freia (diminui a velocidade).

Link para o programa e o fonte no repidshare

Entrada de dados

Este foi mais fácil ainda: a biblioteca PALib tem um teclado pronto e um interpretador Graffiti. Este programa usa o botão select para escolher qual método de entrada o usuário prefere.

Link para download no rapidshare

Código-fonte:
// Includes
#include // Include for PA_Lib

// Function: main()
int main(int argc, char ** argv)
{
  //variaveis
  s32 nletra = 0;
  char letra = 0;
  char texto[600];
  int grafitti = 0;

  //programa
  PA_Init(); // Initializes PA_Lib
  PA_InitVBL(); // Initializes a standard VBL

  PA_InitText(0, 0);
  PA_InitText(1, 0);
  PA_InitKeyboard(2);
  PA_KeyboardIn(20, 100);
  PA_OutputSimpleText(1, 0, 0, "Texto: ");
  PA_OutputSimpleText(0,0,0,"O que for digitado no teclado, ira' ser digitado
no texto. O enter é inserido no texto como Enter, mas apresentado na tela como um
espaço. As teclas A,B,X,Y trocam a cor do teclado. Select altera entre o modo teclado
e o modo graffiti.");

  // Infinite loop to keep the program running
  while (1)
  {
    if (Pad.Newpress.A) PA_SetKeyboardColor(0, 1); // Blue and Red
    if (Pad.Newpress.B) PA_SetKeyboardColor(1, 0); // Red and Blue
    if (Pad.Newpress.X) PA_SetKeyboardColor(2, 1); // Green and Red
    if (Pad.Newpress.Y) PA_SetKeyboardColor(0, 2); // Blue and Green
    if (Pad.Newpress.Select) {
      if (grafitti) {
        PA_KeyboardIn(20,100);
        grafitti = 0;
      } else {
        PA_KeyboardIn(20,199);
        grafitti = 1;
      }
    }
    if (grafitti) {
      letra = PA_CheckLetter();
    } else {
      letra = PA_CheckKeyboard();
    }

    if (letra > 31 || letra == '\n') { // nova letra
      texto[nletra] = letra;
      nletra++;
    } else if ((letra == PA_BACKSPACE)&&nletra) { // Retrocesso
      nletra--;
      texto[nletra] = ' '; // Apaga a ultima letra
    }

    PA_OutputSimpleText(1, 7, 0, texto); // escreve o texto na tela de cima
    PA_WaitForVBL();
  }

  return 0;
} // End of main()

Brincando com texto

Primeiro programa. Ele lota a tela de cima, escreve informaçoes com textos coloridos na tela de baixo e entra em loop lendo a stylus. Conforme a stylus é movimentada, altera as cores da tela de cima, conforme o rgb: R: estático no máximo, G: varia conforme a posição horizontal da stylus, B: varia conforme a posição vertical da stylus

Programa para download no Rapidshare

Código-Fonte (enquanto for pequeno vou postando aqui).

main.cpp:

// Includes
#include // Include for PA_Lib

// Function: main()
int main(int argc, char ** argv)
{
  // variaveis
  int x=0;

  //programa
  PA_Init(); // Initializes PA_Lib
  PA_InitVBL(); // Initializes a standard VBL

  PA_InitText(1,2); //Tell it to use text on screen 1, background number 2
  PA_InitText(0,2); //o mesmo para a screen 0

  for (x=0; x<24; x++) {
    PA_OutputSimpleText(1,0,x,"01234567890123456789012345678901");
  }
  PA_OutputSimpleText(0,1,1,"Hello World !");
  PA_OutputSimpleText(0,1,2,"Desenvolvido em C++");
  PA_OutputSimpleText(0,1,3,"por Guilherme Breda de Castro");
  PA_OutputSimpleText(0,1,4,"para aprender a programar no DS");
  PA_OutputSimpleText(0,1,5,"A tela de baixo eh a 0");
  PA_OutputSimpleText(0,1,6,"A tela de cima eh a 1");
  PA_OutputSimpleText(0,1,7,"Cada tela tem 32x24 carac.");
  PA_OutputSimpleText(0,1,8,"A tela de cima estah cheia");

  PA_BoxText(0,1,9,6,13,"Texto encaixotado",30);
  PA_OutputText(0,0,15,"%c00=branco;%c11=vermelho;%c22=verde;%c33=azul;%c44=magenta;%c55=ciano;%c66=amarelo;%c77=cinza1;%c88=cinza2;%c09=preto");

  // Infinite loop to keep the program running
  while (1)
  {
    PA_OutputText(0,0,14,"Stylus X : %d Stylus Y : %d ", Stylus.X, Stylus.Y);
    PA_SetTextCol(1,31,(Stylus.X/8),(Stylus.Y/6));
    PA_WaitForVBL();
  }

  return 0;
} // End of main()