※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。


kihon.hの作成

メモ帳に以下の内容を書き、kihon.hという名前でcc65/includeに保存します。
/////////////////////////////////////////////////////////////////////////////////
// 2016.10.19 ver 0.15
// 煮るなり焼くなりお好きにどうぞ。
// -----------------------------------------------------------------------------
// cc65@wiki
// http://www34.atwiki.jp/cc65/
/////////////////////////////////////////////////////////////////////////////////
#define REGIST_PPU_CTRL1 (char*)0x2000
#define REGIST_PPU_CTRL2 (char*)0x2001
#define REGIST_PPU_STS   (char*)0x2002
#define REGIST_SPR_ADR   (char*)0x2003
#define REGIST_SPR_GRA   (char*)0x2004
#define REGIST_SCROLL    (char*)0x2005
#define REGIST_ADR       (char*)0x2006
#define REGIST_GRA       (char*)0x2007
#define REGIST_DMA       (char*)0x4014
#define JOYPAD           (char*)0x4016
#define BTN_A            0x80
#define BTN_B            0x40
#define BTN_SELECT       0x20
#define BTN_START        0x10
#define BTN_UP           0x08
#define BTN_DOWN         0x04
#define BTN_LEFT         0x02
#define BTN_RIGHT        0x01
#define GUN_TRIGGER      0x10
#define GUN_HIT          0x08
#define MIC_USE          0x04
 
unsigned char padinfo[2][2];
unsigned char padother;
 
//コントローラ確認
void CheckPad()
{
    unsigned char i,j,ret;
    *JOYPAD = 1;
    *JOYPAD = 0;
    padother= 0;
    for(i=0; i < 2; i++) {
        padinfo[i][1] = padinfo[i][0];
        padinfo[i][0]= 0;
        for(j=0; j < 8; j++) {
            ret = *(JOYPAD + i);
            padinfo[i][0] <<= 1;
            padinfo[i][0] += (ret & 0x01);
            padother |= (ret & 0x0F);
        }
    }
}
 
//ボタン押しっぱなし player:0=1P , 1=2P
char ButtonDown(unsigned char player,unsigned char btn)
{
    if(padinfo[player][0] & btn) {
        return 1;
    } else {
        return 0;
    }
}
 
//ボタン押す
char ButtonPush(unsigned char player,unsigned char btn)
{
    if((padinfo[player][0] & btn) && ! (padinfo[player][1] & btn)) {
        return 1;
    } else {
        return 0;
    }
}
 
//マイク・光線銃など
char ControlOther(unsigned char btn)
{
    if(padother & btn) {
        return 1;
    } else {
        return 0;
    }
}
 
// PPUコントロールレジスタの初期化
void InitPPU()
{
    *REGIST_PPU_CTRL1 = 0x00;
    *REGIST_PPU_CTRL2 = 0x00;
}
 
// PPUコントロールレジスタの設定
void SetPPU(unsigned char flag1,unsigned char flag2)
{
    *REGIST_PPU_CTRL1 = flag1;
    *REGIST_PPU_CTRL2 = flag2;
}
 
// スクロール設定
void SetScroll(unsigned char x, unsigned char y)
{
    *REGIST_SCROLL = x;
    *REGIST_SCROLL = y;
}
 
//背景設定(繰り返し)
void FillBackground(unsigned char address1, unsigned char address2, unsigned char val, unsigned char cnt)
{
    char i;
 
    *REGIST_ADR = address1;
    *REGIST_ADR = address2;
    for (i = 0; i < cnt; i++)
    {
        *REGIST_GRA = val;
    }
}
 
//背景設定
void SetBackground(unsigned char address1, unsigned char address2, unsigned char *bg, unsigned char cnt)
{
    char i;
 
    *REGIST_ADR = address1;
    *REGIST_ADR = address2;
    for (i = 0; i < cnt; i++)
    {
        *REGIST_GRA = *(bg + i);
    }
}
 
//座標から背景のアドレス計算
void GetBackgroundAddress(unsigned char screen, unsigned char x, unsigned char y, char *ret)
{
    unsigned int pos;
 
    switch (screen) {
    case 0:        //左下
        pos = 0x2000;
        break;
    case 1:        //右下
        pos = 0x2800;
        break;
    case 2:        //左上
        pos = 0x2400;
        break;
    case 3:        //右上
        pos = 0x2c00;
        break;
    }
 
    pos += y * 0x20 + x;
    *(ret + 0) = (pos & 0xff00) >> 8;
    *(ret + 1) =  pos & 0x00ff;
}
 
//属性設定(繰り返し)
void FillAttribute(unsigned char address1, unsigned char address2, unsigned char val, unsigned char cnt)
{
    FillBackground(address1,address2,val,cnt);
}
 
//属性設定
void SetAttribute(unsigned char address1, unsigned char address2, unsigned char *atr, unsigned char cnt)
{
    SetBackground(address1,address2,atr,cnt);
}
 
//パレット設定
void SetPalette(unsigned char *palette,unsigned char sprite)
{
    char i;
 
    *REGIST_ADR = 0x3f;
    if (sprite) {
        *REGIST_ADR = 0x10;
    } else {
        *REGIST_ADR = 0x00;
    }
 
    for (i = 0; i < 16; i++)
    {
        *REGIST_GRA = *(palette + i);
    }
}
 
//スプライト設定
void SetSprite(unsigned char first,unsigned char x,unsigned char y,unsigned char index,unsigned char flag)
{
    if (first) {
        *REGIST_SPR_ADR=0x00;
    }
    *REGIST_SPR_GRA=y;
    *REGIST_SPR_GRA=index;
    *REGIST_SPR_GRA=flag;
    *REGIST_SPR_GRA=x;
}
 
//DMA転送領域初期化
void InitDMA(unsigned char *address)
{
    int i;
    for(i=0; i < 256; i++) {
        *(address + i)=0;
    }
}
 
//DMA転送準備
void SetDMA(unsigned char *address,unsigned char first,unsigned char x,unsigned char y,unsigned char index,unsigned char flag)
{
    static unsigned char cnt;
    if (first) { cnt = 0; }
    *(address + cnt + 0)=y;
    *(address + cnt + 1)=index;
    *(address + cnt + 2)=flag;
    *(address + cnt + 3)=x;
    cnt+=4;
}
 
//DMA転送
void SendDMA(unsigned char address)
{
    *REGIST_DMA = address;
}
 
//VBlank待ち
void VBlank()
{
    while (! (*REGIST_PPU_STS & 0x80) );
}
 
//0爆弾待ち
void ZeroSprite()
{
    while (  (*REGIST_PPU_STS & 0x40) );
    while (! (*REGIST_PPU_STS & 0x40) );
}
 

kihon.hの解説

  • CheckPad:コントローラの確認。基本的に毎回呼び出す。
  • ButtonDown:押されたボタンの確認。(押し続ける)
  • ButtonPush:押されたボタンの確認。(押す→離す)
  • ControlOther:マイク・光線銃の確認。
  • InitPPU:PPUコントロールレジスタの初期化。
  • SetPPU:PPUコントロールレジスタの設定。
    • flag1:PPUコントロールレジスタ1の設定。
      • bit7:VBlank時にNMIを実行
      • bit6:PPUマスター/スレーブセレクト
      • bit5:スプライトサイズ(0:8x8,1:8x16)
      • bit4:BGパターンテーブルアドレス(0:$0000,1:$1000)
      • bit3:スプライトパターンテーブルアドレス(0:$0000,1:$1000)
      • bit2:PPUアドレスインクリメント(0:+1,1:+32)
      • bit0,1:表示するネームテーブルアドレス番号(00:$2000・左下、01:$2400・左上、10:$2800・右下、11:$2C00・右上)
    • flag2:PPUコントロールレジスタ2の設定。
      • bit5-7:bit0=1のとき背景色/bit0=1のとき色強調
        000:なし・001:緑・010:青・100:赤(それ以外の数字は不可)
      • bit4:スプライト表示(0:非表示,1:表示)
      • bit3:BG表示(0:非表示,1:表示)
      • bit2:スプライトクリップ(0:画面の左8ドットを表示しない,1:クリップなし)
      • bit1:BGクリップ(0:画面の左8ドットを表示しない,1:クリップなし)
      • bit0:ディスプレイタイプ(0:カラー,1:モノクロ)
  • SetScroll:背景のスクロール。
    • x:横位置。
    • y:縦位置。
  • FillBackground:背景の設定(繰り返し)。
    • address1:出力開始位置(上位2バイト)
    • address2:出力開始位置(下位2バイト)
    • val:出力内容(マスの番号)
    • cnt:出力を繰り返す数
  • SetBackground:背景の設定。
    • address1:出力開始位置(上位2バイト)
    • address2:出力開始位置(下位2バイト)
    • *bg:出力内容のポインタ
    • cnt:出力内容のバイト数
  • GetBackgroundAddress:座標から背景のアドレスを算出。
    • screen:画面(0:左下・初期画面,1:右下,2:左上,3:右上)
    • x:横位置。
    • y:縦位置。
    • *ret:アドレスを格納する1バイト×2の配列
  • SetPalette:パレットの設定。
    • *palette:パレット内容のポインタ
    • sprite:0:背景用パレット 1:スプライト用パレット
  • FillAttribute:属性(背景4マス×4マス毎にどのパレットを使うか)の設定。(繰り返し)
    • address1:出力先(上位2バイト)
    • address2:出力先(下位2バイト)
    • val:出力内容
    • cnt:出力を繰り返す数
  • SetAttribute:属性(背景4マス×4マス毎にどのパレットを使うか)の設定。
    • address1:出力先(上位2バイト)
    • address2:出力先(下位2バイト)
    • *atr:出力内容のポインタ
    • cnt:出力内容のバイト数
  • SetSprite:スプライトの設定。
    • first:1=1つ目のスプライト 0=2つ目以降
    • x:横位置
    • y:縦位置
    • index:何コマ目の画像を表示するか
    • flag:表示条件 
      • 7bit:上下反転
      • 6bit:左右反転
      • 5bit:背景よりも後ろに表示
      • 0,1bit:パレット番号
  • SetDMA:DMA転送準備。(スプライト用領域の設定)
    • *address:スプライト用領域の開始アドレス
    • first:1=1つ目のスプライト 0=2つ目以降
    • x,y,index,flag:SetSpriteと同内容。
  • SendDMA:DMA転送
    • address:スプライト用領域の開始アドレスの三桁目 0x1=0x0100
  • VBlank:VBlank待ち処理。
  • ZeroSprite:0爆弾待ち処理。
※SetRegister・SetBackground・SetAttribute・SetPaletteは便宜上分けているだけで、やっている処理は同じです。
※FillBackgroundとFillAttributeも同様。

留意事項

  • SetBackgroundによる描画が間に合わない場合、背景がまだらに表示されます。
 VBlank()
 SetBackground(0x20,0x00,(char *)bg,32);
 SetScroll( 0, 0 );
 
  • 一時的に背景の描画を停止し、その間に描画することで回避できます。
 SetPPU(0x08,0x06);
 SetBackground(0x20,0x00,(char *)bg,32);
 SetScroll( 0, 0 );
 SetPPU(0x08,0x1e);
 
  • 描画する範囲を狭めることでも回避できます。
 VBlank();
 SetBackground(0x20,0x00,(char *)bg,10)
 SetScroll( 0, 0 );
 VBlank();
 SetBackground(0x20,0x0a,(char *)bg,10)
 SetScroll( 0, 0 );
 VBlank();
 SetBackground(0x20,0x14,(char *)bg,12)
 SetScroll( 0, 0 );
 
  • SetBackgroundを呼び出さずにベタに処理を書くとより効果的です。
    (#defineの使用は問題ありません。)
 *REGIST_ADR = 0x20;
 *REGIST_ADR = 0x00;
 *REGIST_GRA = 0;
 *REGIST_GRA = 1;
      :
      :
 *REGIST_GRA = 31;