見出し画像

SGDK学習メモ:No.6、画面のモード、状態、解像度などについて確認してみる

*以下SGDKは記述時点で最新版のSGDK 2.00 (january 2024)を使用しています


画面のモード等についての知識が曖昧なままなので、実際にコードを書きつつ確認してみます。WINDOW(後述)のコード記述方法も確認します。確認にはSGDK同梱のサンプルも使用します。

今回(も)かなり冗長ですが、比喩や卑下ではなく「私は不器用」ということでご了承ください(無類の不器用)


今回学習のベースにするのは(メガドライブ版ダライアスなどで有名な)Hidecadeさんの
メガドライブのゲームの作り方 その2 - 画面モードとVRAMのマッピング | Arcade Cabinet


GENESIS Technical Overview (*PDF)
です。
GENESIS Technical Overviewは表紙に"CONFIDENTIAL"、"PROPERTY OF SEGA"とありますが、セガオフィシャルでゲームをリリースしたHidecadeさんのサイトでもこの資料(または類似資料)からの引用が行われており、「怒られは発生しないだろう」と判断しました。引用時には可読性を上げるためBold強調などを行っている場合があります。


使用される用語について

Sega Technical OverviewにTERMINOLOGY(用語集)という項目があります。

_ TERMINOLOGY _

1.  A unit of Position on X Y coordinates is called a "DOT".
2.  A minimum unit of display is called a "PIXEL".
3.  "CELL" means an 8 (pixel) x 8 (pixel) pattern.
4.  SCROLL indicated a repositionable screen-spanning play field.
5.  CPU usually indicates the 68000.
6.  VDP stands for Video Display Processor.
7.  CTRL stands for Control.
8.  VRAM stands for VDP RAM, the 64K bytes area of RAM accessible only
through the VDP.
9 .  CRAM stands for Color RAM, 64 9 bit words inside the VDP chip.
10.  VSRAM stands for vertical Scroll RAM. 40 1Obit words inside the
VDP chip.
11.  DMA stands for Direct Memory Access, the process by which the
VDP performs high speed fills or memory copies.
12.  PSG stands for Programmable sound Generator. A class of
low-capability Sound chips. The Mega drive contains a
Texas Instruments 76489 PSG chip.
13.  FM stands for Frequency Modulation, a class of high-capability
sound chip. The Mega drive contains a Yamaha 2612 FM chip.

GENESIS Technical Overview ,P.12

今回に関連する項目としては
「3.  "CELL" は8 (pixel) x 8 (pixel)です」
「6 .  VDPはVideo Display Processorを表します(の省略です)」
「8.  VRAMはVDP RAMを表します、64K bytesのRAMはVDP経由でのみアクセスできます」(*SGDKではVDPアクセス用の関数が用意されている)
あたりです。


画面モード、WINDOW、背景のPLANEと優先度、最背面の背景色について

2. VDP 315 - 5313
(Video Display Processor)
The VDP controls screen display. VDP has graphic modes IV and V. Where Mode IV is for compatibility with the MASTER SYSTEM and V is for the new Mega drive functions. There are no advantages to using mode IV. so it is assumed that all Mega drive development will use mode V. In Mode V. the VDP display has 4 planes: SPRITE, SCROLL A/WINDOW, SCROLL B, and BACKGROUND.

VDP(Video Display Processor)は画面表示をコントロールします。VDPにはグラフィックモードとして「モードIV」「モードV」が存在します。
「モードIV」はマスターシステム互換用「モードV」はメガドライブ用で新しい機能が使用できます。「モードIV」を使用する優位性はありません。そのため全てのメガドライブのソフトウェアは「モードV」で開発されるでしょう。
「モードV」でのVDPディスプレイは4面(4 planes)を持ちます:SPRITESCROLL A/WINDOWSCROLL B、そしてBACKGROUND(単色の背景色)です。

GENESIS Technical Overview ,P.11
翻訳文と一部補足を追加
GENESIS Technical Overview , P.11
モードIV、1背景とスプライトの2画面、
これで作成されたメガドライブのゲームはあるのでしょうか?
(Homebrewとかの技術デモは存在しそう)
GENESIS Technical Overview  ,P.11
モードV、3背景とスプライトの4画面、
実際にはSCROLL AとWINDOWは共用される(後述)、
図では省略されていますが一番後ろに単色の背景色も存在します

上図モードVの説明だとトータル4画面(4 planes)存在するように見えますが

VIDEO:
(中略)
- 3 Planes
  ・2 scrolling playfields
  ・1 sprite plane
  ・definable priorities between planes

GENESIS Technical Overview ,P.2

とあり実際には3画面です。これは
WINDOWはSCROLL Aの一部で、WINDOWはスクロールしない(させない)ためのPLANEである
となっています(と私は認識しています)。
テスト用に作成したプログラムで確認します。

今回テスト用プログラムではリソースファイルを取り込まず、SGDKが用意している(取り込んでいる)デフォルトのフォント(res/image/font_default.png)を背景用データとして使用しています。

Gens KMod(Gens)のデバッガでVDPを表示、
フォントは先頭の空白文字を含めて96文字、
上図は2文字目の!(0x05A1)を選択している状態、
今回はこれらのフォントを背景のタイルデータとして使用(借用)している
自作のコードで背景を表示した状態、
画面下部の青文字3、4、5はSCROLL A、
/、A、B、C(5の下に存在)はSCROLL B、
この時点でWINDOWに青文字0~2のタイルを設定済みだが
設定が足りていないため表示されていない
ボタン押下により画面上部にWINDOWに設定済みの
青文字0、1、2が表示された状態、
具体的にはVDP_setWindowVPos(FALSE, 1)を実行している
画面全体を少し左下にスクロールさせた状態、
0~2はWINDOWに設定されているので
スクロールせずに画面上部に残り続けている
画面をスクロールさせてSCROLL AとWINDOWを一部重ね、
GensのLayers機能でSCROLL B(Layer B)を非表示にした状態、
WINDOW(0~2)のほうが優先順位が高いため、
WINDOWにラップされているSCROLL A(Layer A)(3~5)が見えなくなっている

SGDKでは
WINDOW(上図青文字0~1) → VDP_setTileMapXY(WINDOW,tile,x,y);
SCROLL A(上図青文字3~5) → VDP_setTileMapXY(BG_A,tile,x,y);
とWINDOWとSCROLL Aにはそれぞれに値を設定する必要があります(*VRAMも別に割り当てる必要がある)。
SCROLL Aに指定したタイルがVDP_setWindowVPos()(またはVDP_setWindowHPos())によってWINDOWになるわけではない(←当初私はこう思っていた)、ということになります。

WINDOWは画面の上下左右のいずれかに設定可能です。
画面上 → VDP_setWindowVPos(FALSE、固定するCELL数);
画面下 → VDP_setWindowVPos(TRUE、固定するCELL数);
画面左 → VDP_setWindowHPos(FALSE、固定するCELL数);
画面右 → VDP_setWindowHPos(TRUE、固定するCELL数);
となります。
画面分割表示は「0爆弾」「IRQ」や「MMC5でなければ縦分割スクロールできない」なファミコンと比べるとSGDKの手助けもあって劇的に楽です。

背景及びスプライトのPLANEはプライオリティを設定可能です。背景は通常
SCROLL B < SCROLL A/WINDOW
なのですが、SCROLL Bのプライオリティを上げることで
SCROLL B > SCROLL A/WINDOW
となります。

SCROLL Bのプライオリティを上げた状態、
画像が潰れて見えづらいですが
SCROLL A/WINDOWの青文字0~5が
SCROLL Bの下に表示されています

最背面の背景はパレットの番号を指定することで任意の色に変更可能です。SGDKでは VDP_setBackgroundColor(パレットの番号(0~63)) を実行します。初期値は0です。

VDP_setBackgroundColor(37)を設定した状態、
ここで設定しているパレットの具体的な色は
上図GensのVDPのスクショを参照ください

画面解像度について

*本項目は基本NTSCについての話となります、ちなみにフランスにはSECAMモデルのメガドライブが存在したそうです

メガドライブの解像度には大別して以下の2つ("THERE ARE TWO MOEDS")
32 cell wide mode(H32) → 32*28 CELL (256*224 PIXEL)
40 cell wide mode(H40) → 40*28 CELL (320*224 PIXEL)(SGDKのデフォルトはこれ)
が存在します。
横の解像度が8 CELL(64 PIXEL)増減し、縦の解像度は28 CELL(224 PIXEL)固定です。

GENESIS Technical Overview ,P.13

PALの場合は縦解像度30 CELL(240 PIXEL)も選択可能だそうですが("a vertical size of 30 cells (240 dots) is selectable")が、今回これは無視しておきます。
(PALにしたい場合は要レジスタへの設定?←未確認)

doc/html/index.html
NTSCの場合縦解像度は常に224が返却される
40 cell wide mode → 40*28 CELL (320*224 PIXEL)
の場合
32 cell wide mode → 32*28 CELL (256*224 PIXEL)
右の8CELL分が表示されなくなった

SGDKでは
VDP_setScreenWidth320() → 40 cell wide mode
VDP_setScreenWidth256() → 32 cell wide mode
で切り替え可能です。今回ボタン押下により解像度を切り替えできるようにしましたが、実機でどのような挙動になるかは未確認です。またエミュレータにより挙動も異なりました。

BlastEmの場合32 cell wide modeだと
Gensと異なり画面全体に拡大表示されます、
実機だと多分こっち?

解像度によって表示できる最大スプライト数、横にちらつかずに並べられるスプライト数も異なります。

There are 64 sprites available when the screen is in 32 cell wide mode. Or 80 when the screen is in 40 cell wide mode.

GENESIS Technical Overview ,P.13
GENESIS Technical Overview ,P.19
VDP PORTのSTATUS REGISTERに存在するSOVRで
スプライトのチラつき発生が検知できるようです

in 256 pixel (32 cell) wide mode, 64 ($40) sprites per frame, 16 ($10) sprites per scanline.
in 320 pixel (40 cell) wide mode, 80 ($50) sprites per frame, 20 ($14) sprites per scanline.

https://segaretro.org/Sega_Mega_Drive/Sprites

32 cell wide mode → 最大 64 sprites、16 sprites per scanline
40 cell wide mode → 最大 80 sprites、20 sprites per scanline
となっています。
ちなみにファミコンセガ・マークIII/マスターシステムだとちらつき無しで横に並べられるスプライト数は最大8です(=32 cell wide modeはマスターシステム互換ではない)。

ラエルさんのサイトによると

SGDKの小技 - nendo

横256ドットモードにするとクロックが落ちて全体的に性能が下がります。

https://nendo16.jimdofree.com/sgdk%E3%81%AE%E5%B0%8F%E6%8A%80/

だそうです。
32 cell wide modeで表示できるスプライト数が減るのは「解像度が低いときはクロックを下げているから」(意訳)ということになるのでしょうか?

ついでに32 cell wide modeについての考察動画と、動画中で言及されているリストも紹介しておきます。

How to Use 256 Low Resolution Mode on Sega Genesis & Mega Drive - Beginners Game Dev Tutorials - YouTube

Genesis USA games that use 256x224 mode during main gameplay. List created & tested by Firebrandx (wolff@firebrandx.com) Updated March 27, 2019.

https://www.firebrandx.com/downloads/Genesis-256-Mode-List.txt

基本的にはデフォルトの40 cell wide modeを使用する、ということで良さそうです。
上記動画を見ても32 cell wide modeのメリットがいまいち感じられませんでした(携帯機でもないのに解像度を「下げる」クロックが下がり表示可能スプライト数が減る/ちなみにメガジェットはACアダプタ駆動)が、動画のコメントにある

i think another reason to use H32 is because 256x224 mode is sharper than 320x224 mode with composite video or RF connections. So if you want your game or a particular sreen to look sharper thru composite video you use H32 mode

https://www.youtube.com/watch?v=0Vbt6vv7jWs

「32 cell wide modeだとコンポジット接続、RF端子接続で画像が鮮明になる」(意訳)
が個人的には「なるほど」と感じました。BlastEmのように拡大表示されるのであれば、特にブラウン管だと効果がありそうです。


スクロール面のサイズの設定とVRAMのマッピング(一部)について

メガドライブのVRAM容量は64K Bytes(0x0000-0xFFFF)です(*CRAM/パレットは別領域)。

VDP (Video Display Processor)
    • dedicated video display processor
       - controls playfield & sprites
       - capable of DMA
       - Horizontal & Vertical interrupts
    • 64 KBytes of dedicated VRAM (Video Ram)
    • 64 x 9-bits of CRAM (Color RAM)

GENESIS Technical Overview ,P.2

VRAM中のマッピングは自由に配置可能ですが、背景とスプライトに使用する画像データ(PATTERN GENERATOR TABLE and SPRITE GENERATOR TABLE)はVRAM中の先頭(0x0000)から固定で確保されます。

5.VRAM MAPPING
(中略)
the base address of PATTERN GENERATOR TABLE and SPRITE GENERATOR TABLE are 0000H and fixed. However, the other base addresses can be freely assigned in VRAM by setting VDP REGISTER.
(中略)
PATTERN GENERATOR TABLE
Base address is 0000H (fixed).
SPRITE GENERATOR TABLE
Base address is 0000H (fixed).

GENESIS Technical Overview ,P.79

スクロール面(SCROLL PLAYFIELD)の設定可能なサイズについては、先に引用済みのDISPLAY SPECIFICATION OUTLINEに記載があります。該当部分をもう一度引用します。

SCROLL PLAYFIELDS
Two scrolling play fields. whose size in cells is selectable from;
32 * 32, 32 * 64, 32 * 128, 64 * 32, 64 * 64, 128 * 32

GENESIS Technical Overview ,P.13

32 CELL、64 CELL、128 CELLの組み合わせでトータル6パターンあります。
また別のページに以下のような説明があります。

VIDEO:
  • NOTE: Playfield and Sprites are character-based
  • Display Area (visual)
    - 40 chars wide x 28 chars high
      • each char is 8 x 8 pixels
(中略)
-Playfields:
  • 6 different sizes
  • 1 playfield can have a "fixed" window
  • playfield map
     - each char position takes 2 Bytes, that includes:
       • char name (10 bits); points to char definition
       • horizontal flip
       • vertical flip
       • color palette (2 bits); index into CRAM
       • priority

GENESIS Technical Overview ,P.2

スクロール面は1 CELL("char is 8 x 8 pixels")毎に"each char position takes 2 Bytes"ということなので、SCROLL A、Bが(それぞれ)必要とするメモリは
(1) 幅32CELL * 高さ32CELL * 2Bytes = 2048Bytes(0x800) = 2KiB
(2) 幅32CELL * 高さ64CELL * 2Bytes = 4096Bytes(0x100) = 4KiB
(3) 幅32CELL * 高さ128CELL * 2Bytes = 8192Bytes(0x200) = 8KiB
(4) 幅64CELL * 高さ32CELL * 2Bytes = 4096Bytes(0x100) = 4KiB
(5) 幅64CELL * 高さ64CELL* 2Bytes = 8192Bytes(0x200) = 8KiB
(6) 幅128CELL * 高さ32CELL* 2Bytes = 8192Bytes(0x200) = 8KiB
となります。

スクロール面のサイズ変更には
VDP_setPlaneSize ()
を使用します。

doc/html/index.html

VDP_setPlaneSize()は引数setupVramにTRUEを渡すことで、SGDKがVRAMマッピング+タイルの再配置をしてくれます(*タイル再配置については今回未確認)。
今回VDP_setPlaneSize(幅,高さ,TRUE)を実行してどの様にVRAMがマッピングされるのか、Gensのデバッグ機能VDP Registersを使用して確認してみました。VDPの各レジスタの説明はGENESIS Technical Overview ,PP.22-26 に記載があります。以下の画像では今回の内容に関係ある部分を赤で囲みました。


パターン1) スクロール面ごとに必要なVRAMが2KiB
・(1) 幅32CELL * 高さ32CELL

「#数値」はレジスタの番号を意味します、
VDPには#0-#23の書き込み専用レジスタと
1つの読み込み専用レジスタ、トータル25個のレジスタが存在します

SCROLL AにはVRAM 2KiBが割り当てられていますが、WINDOWとSCROLL Bには同一のアドレス(0xC000-0xDFFF)がマッピングされ、更にVRAMの割り当てが8KiBとなっています。容量的に余裕があるはずなのになぜWINDOWとSCROLL Bで同じアドレスがマッピングされるのか、また2KiBより大きい容量(8KiB)が設定されている理由は現時点で理解できていません。
とりあえずSCROLL AとSCROLL Bには容量2KiB、またはそれ以上が割り当てられていることがわかりました。

今回WINDOWとSCROLL Bに同一のアドレスが設定されていますが、このような場合の挙動については次回以降の宿題としておきます。

Also, AREA can be overlapped. Therefore, TABLE can be commonly used by SCROLL screen and WINDOW for example.

GENESIS Technical Overview ,P.79

とあり、スクロール面とWINDOWに同一(重複)アドレスを割り当てることは仕様上問題ないとされています。

解像度 幅32CELL * 高さ32CELLをどのような状況で使用したらよいのか理解できていませんが、固定画面系のゲームで多めに画像を使いたい(PATTERN GENERATOR TABLE and SPRITE GENERATOR TABLEにデータを多く置きたい)場合とかでしょうか?

*2024/05/10追記
sgdk Documentation及びソースコード(vdp.c)を軽く追ってわかったのですが、VRAMのマッピング時に指定するアドレス(の開始位置)は特定の値の倍数を指定することというルールがありました。

SCROLL A、SCROLL Bの場合は
「0x2000(8192)の倍数を指定すること」とあります、
VRAMは64KiBなので0x2000、0x4000...0xC000、0xE000の
トータル8つの中から選択する必要があります

SCROLL Bに(必要な2KiBに対して過剰とも言える)8KiBが設定されているのはこれが理由でした(と今のところ理解しておきます)。
SCROLL A、SCROLL B以外でも倍数値は異なりますが同様のルール(縛り)が存在するため、それを踏まえてマッピングする必要があります。
SCROLL A、SCROLL Bのアドレスには0x2000(8192/8KiB)の倍数を指定するルールのため、実質的に0xC000または0xE000のいずれかを指定せざるを得ないでしょう。スクロールする背景が1面で足りる場合はSCROLL A、SCROLL Bに同一のアドレスを設定する、とか。


パターン2) スクロール面ごとに必要なVRAMが4KiB
・(2) 幅32CELL * 高さ64CELL
・(4) 幅64CELL * 高さ32CELL

先のパターン1)と異なり、今回はSCROLL A、SCROLL B、WINDOWのそれぞれに4KiBが割り当てられています。
SGDKのデフォルトでは (4) 幅64CELL * 高さ32CELL が設定されており、Hidecadeさんのサイトでも

通常は64x32 CELLの設定で問題ないかと思われます。

https://ameblo.jp/arcade-cabinet/entry-12269462843.html

とあります。


パターン3) スクロール面ごとに必要なVRAMが8KiB
・(3) 幅32CELL * 高さ128CELL
・(5) 幅64CELL * 高さ64CELL
・(6) 幅128CELL * 高さ32CELL

SCROLL A、SCROLL Bに8KiB、WINDOWに4KiBが割り当てられています。今回WINDOWには4KiBが割り当てられていますが、「とりあえずWINDOWには4KiBを用意した」「WINDOWにもっとメモリが必要なら自分で設定してくれ」ということでしょうか?

*2024/05/11追記
Sega Technical Overview P.55に"WINDOW PATTERN NAME TABLE MAX 4K BYTES"とありました。P.79には"There are 2K bytes for WINDOW PATTERNNAME TABLE in H32 cell mode, and 4K byte area in H 40 cell mode."とあり、PP.80-81のVRAMマッピングサンプルでもWINDOWにはH32で2KiB、H40で4KiBの割り当てです(=通常H40なので、WINDOWを使用するのであれば4KiBを割り当てることになる)。

<スクロール面のサイズの設定>
スクロールA面(SCROLL A)とスクロールB面(SCROLL B)はサイズ変更可能で以下のサイズから選択できます。この範囲のサイズなら一度に画像を読み込むことができます。ただファンタジーゾーンのように背景面が広い場合(横256 CELL)は一度に読み込むことができないため、スクロールに応じて再度画像を読み込む必要があります。

https://ameblo.jp/arcade-cabinet/entry-12269462843.html

とあるのですが、これはつまり「背景面の画像が背景面に指定したサイズ以下ならスクロール時に画像(再)読み込みが不要」となります。
SGDKのサンプルにconsole(sample\sys\console)が存在するのですが、これのmain.cで
VDP_setPlaneSize(128, 32, TRUE);
が行われており、つまり解像度1024PIXEL*256PIXEL(=(128*8)*(32*8))以下の画像であれば再読み込みが不要となります。

consoleで使用している背景用の画像ファイル
sample\sys\console\res\R-Type_FG.png
はPLANEに指定しているサイズと同値の1024 PIXEL * 256 PIXELで

GensのPlane Explorerで確認すると、スクロールしても再読み込みが行われていないことがわかります。

PLANE Aには
サイズ1024PIXEL*256PIXELのR-Type_FG.pngが
まるまる入っています
PLANE Bには
サイズ64PIXEL*64PIXELのR-Type_BG.pngが
1024PIXEL*256PIXEL分敷き詰められています、
こちらも読み込みは発生していません

今回かなり長くなってしまったので、VRAMのマッピングについての残りは次回にします。

今回作成したソースファイルも載せておきます。

#include <genesis.h>

//VRAMアドレス
#define HSCROLL_TABLE_ADDR 0xA800
#define SPRITE_LIST_ADDR   0xAC00
#define WINDOW_ADDR        0xB000
#define BGA_ADDR           0xC000
#define BGB_ADDR           0xE000


bool isBgBPriority=FALSE;
bool isScreen256=FALSE;
bool isSetAddress=false;

static const int sciSlash = 0x05AF; //文字"/"、1455
static const int sciA = 0x05C1;     //文字"A"、1473
static const int sciOne = 0x05B0;   //文字"1"、1456

s16 currentScreenX=0;
s16 currentScreenY=0;

u8 currentBackgrouondColor=0;

int currentPlaneSizesIndex=0;   //0-5が設定される想定
static const int PLANE_SIZES[2][6] = {{32,32,32,64,64,128}, {32,64, 128,32,64,32}};


//背景初期化
void bgInit(){
    //BG_B、A
    VDP_fillTileMapRect(BG_B,TILE_ATTR_FULL(PAL0,FALSE,FALSE,FALSE,sciSlash),0,0,39,27);

    //BG_B,AとB
    VDP_fillTileMapRect(BG_B,TILE_ATTR_FULL(PAL1,FALSE,FALSE,FALSE,sciA),39,0,1,27);
    VDP_fillTileMapRect(BG_B,TILE_ATTR_FULL(PAL1,FALSE,FALSE,FALSE,sciA+1),0,27,39,1);

    //BG_B,C
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,FALSE,FALSE,FALSE,sciA+2),39,27);


    //0~2はWINDOW(BG_A)
    VDP_setTileMapXY(WINDOW,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne),0,0);
    VDP_setTileMapXY(WINDOW,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne+1),31,0);
    VDP_setTileMapXY(WINDOW,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne+2),39,0);

    //3~5はBG_A
    VDP_setTileMapXY(BG_A,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne+3),0,27);
    VDP_setTileMapXY(BG_A,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne+4),31,27);
    VDP_setTileMapXY(BG_A,TILE_ATTR_FULL(PAL3,FALSE,FALSE,FALSE,sciOne+5),39,27);
}

//背景 BG_B のpriorityを設定
void setBGBPriority(){

    isBgBPriority = !isBgBPriority;
    //Slash
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL0,isBgBPriority,FALSE,FALSE,sciSlash),0,0);
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL0,isBgBPriority,FALSE,FALSE,sciSlash),31,0);
    //A
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,isBgBPriority,FALSE,FALSE,sciA),39,0);
    //B
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,isBgBPriority,FALSE,FALSE,sciA+1),0,27);
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,isBgBPriority,FALSE,FALSE,sciA+1),31,27);
    //C
    VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,isBgBPriority,FALSE,FALSE,sciA+2),39,27);

    //VDP_setTileMapXY(BG_B,TILE_ATTR_FULL(PAL1,isBgBPriority,FALSE,FALSE,sciA+2),39,27);

}

//VDP アドレスログ出力、未使用
void logVramAddresses(){
    kprintf("VDP_getBGAAddress(): %u",VDP_getBGAAddress());

    kprintf("VDP_getBGBAddress(): %u",VDP_getBGBAddress());
    kprintf("VDP_getWindowAddress(): %u",VDP_getWindowAddress());

    kprintf("VDP_getSpriteListAddress(): %u",VDP_getSpriteListAddress());
    kprintf("VDP_getHScrollTableAddress(): %u",VDP_getHScrollTableAddress());
}

//VDP アドレスセット、未使用
void setVramAddresses(){

    VDP_setBGBAddress         ( BGB_ADDR           );

    VDP_setBGAAddress         ( BGA_ADDR           );
    VDP_setWindowAddress      ( WINDOW_ADDR        );

    VDP_setSpriteListAddress  ( SPRITE_LIST_ADDR   );
    VDP_setHScrollTableAddress( HSCROLL_TABLE_ADDR );
}


//キーイベント(コールバック)
void myJoyHandler( u16 joy, u16 changed, u16 state)
{
    if (joy == JOY_1){

        //Aボタン、WINDOWの位置設定
        if (state & BUTTON_A){
            VDP_setWindowVPos(FALSE, 1);
        }

        //Bボタン、PLANE Bのプライオリティ変更
        if (state & BUTTON_B){
            setBGBPriority();
        }

        //Cボタン、解像度変更
        if (state & BUTTON_C){
            if(isScreen256){
                VDP_setScreenWidth320();
            }else{
                VDP_setScreenWidth256();
            }

            isScreen256 = !isScreen256;
        }

        //Xボタン、最背面の色
        if (state & BUTTON_X){
            if(currentBackgrouondColor < 63 ){
                currentBackgrouondColor++;
            }else{
                currentBackgrouondColor=0;
            }
            //パレットのindexを指定する
            kprintf("(Before)VDP_getBackgroundColor(),%u",VDP_getBackgroundColor());
            VDP_setBackgroundColor(currentBackgrouondColor);
            kprintf("(Current)VDP_setBackgroundColor(),%u",VDP_getBackgroundColor());
        }


        //Yボタン、PLANEサイズの変更
        if (state & BUTTON_Y){
            // int currentPlaneSizesIndex=0;   //0-5が設定される想定
            // static const int PLANE_SIZES[2][6] = {{32,32,32,64,64,128}, {32,64, 128,32,64,32}};

            VDP_setPlaneSize(PLANE_SIZES[0][currentPlaneSizesIndex],PLANE_SIZES[1][currentPlaneSizesIndex],TRUE);

            if(currentPlaneSizesIndex >= 5){
                currentPlaneSizesIndex=0;
            }else{
                currentPlaneSizesIndex++;
            }
        }

        //Z
        if (state & BUTTON_Z){
            PAL_setColor(0,0xF00);
        }





        //スタートボタン、リセットによる初期化処理(*PLANEサイズの初期化はしない)
        if (state & BUTTON_START){
            VDP_setWindowVPos(FALSE, 0);

            currentScreenX=0;
            currentScreenY=0;
            VDP_setVerticalScroll(BG_A,currentScreenY);
            VDP_setHorizontalScroll(BG_A,currentScreenX);
            VDP_setVerticalScroll(BG_B,currentScreenY);
            VDP_setHorizontalScroll(BG_B,currentScreenX);

            currentBackgrouondColor=0;
            VDP_setBackgroundColor(currentBackgrouondColor);
        }

    }
}

//キーイベント(VBlankごと)
static void handleInput()
{
    u16 value = JOY_readJoypad(JOY_1);

    if (value & BUTTON_UP){
        currentScreenX++;
    }
    if (value & BUTTON_DOWN){
        currentScreenX--;
    }
    if (value & BUTTON_RIGHT){
        currentScreenY++;
    }
    if (value & BUTTON_LEFT){
        currentScreenY--;
    }

    VDP_setVerticalScroll(BG_A,currentScreenX);
    VDP_setVerticalScroll(BG_B,currentScreenX);
    VDP_setHorizontalScroll(BG_A,currentScreenY);
    VDP_setHorizontalScroll(BG_B,currentScreenY);
}

// main
int main()
{

    JOY_init();
    JOY_setEventHandler( &myJoyHandler );

    bgInit();

    while(1)
    {
        handleInput();
        SYS_doVBlankProcess();
    }
    return (0);
}

【了】

この記事が気に入ったらサポートをしてみませんか?