KITAQ SERIES • REFERENCE EDITION
KITAQGB LIBRARY
ゲームを組み立てる道具箱
ローカルソース採取:2026年9月14日
Rust製コンパイラでのライブラリ利用
lib/はゲーム機のROMに組み込むCヘッダーとCソースです。PC用コンパイラと補助ツールはRust製で、ゲームのソース、対象機のライブラリとAPIはCのまま使用します。収録するSOURCE_MANIFEST.jsonとLIBRARY_MERGE.jsonにはライブラリの選択元を記録しています。
kitaqgb · Rust製コンパイラと補助ツール · GitHub Actions
Windows・Linux・macOS ARM・macOS Intelでネイティブビルドと実行検証が成功しています。KITAQGBは各環境48件のテストと395件の補助ツール検証、KITAQFCは55件と401件が成功しました。Rust 1.85でも確認済みです。PUBLIC_DISTRIBUTION.jsonに配置済みバイナリのハッシュと検証の出典を記録しています。公開GitHub Actionsでもこのソースを独立してビルド・検証します。
保存済みの比較データでROMのバイト列、診断と補助ツールの形式を検証しています。以前のC#版によるエミュレータ検証は、そのソース指紋に対応する過去の記録として残しています。これだけでRust版の全API、実機動作、FDSのBIOS経由のゲーム起動を保証するものではありません。元のPNG変換スクリプトは入手できないため仕様から再実装し、元スクリプトとのバイト一致は確認できません。
2026-10-03 ローカル版ライブラリの差分と検証
C:/kitaqgb_project/lib のソースを、公開版 2de7f4b と比較しました。更新日時だけでは最新版を決められません。ローカル版には追加機能があり、公開版には別の追加機能と修正があります。以下は保存したローカルソースの契約です。従来の公開版APIの検証結果とは対象を区別します。
physics2d.c は既存の接触APIを保ち、KQ2D_CACHE_SURFACE による接触・速度制限のキャッシュ実装を追加しています。この設定はROMバンク13を使うため、そのバンクを確保してください。KQ2D_EXTERNAL_SCALE_Q8、KQ2D_EXTERNAL_REST_COMPONENT、KQ2D_EXTERNAL_SURFACE を定義した場合は、対応する実装を呼び出し側で提供します。今回の8,872ケースはこれらを定義しない標準経路の結果です。
KQ3D_BODY_SPHERE=0x04 は球と箱の接触を選びます。球の半径は half_x です。kq3d_overlap_sphere_aabb(sphere, box) は接する境界を含めて判定し、NULLまたは半径が0以下なら0を返します。球同士の専用ソルバはありません。座標差、積、二乗和を16ビットの範囲に収めてください。
KQ3D_BODY_KINEMATIC=0x02 を持つ有効なbodyは、kq3d_step で逆質量が0でもX/Y速度と位置を更新します。Z位置とZ速度は更新しません。逆質量0のbodyは接触で押し戻されません。kq3d_integrate_body は従来どおり静的bodyを積分しません。break_speed と KQ3D_BODY_BROKEN による自動破壊は行いません。
ローカルの bank_switch は kq_bank_current と0x2000へ書き込みます。公開版の __bankswitch 経由の処理とは、コンパイラのバンク追跡状態の更新が異なります。切替処理は固定バンクから実行し、コンパイラのバンク復帰状態も必要な場合は __bankswitch を使ってください。
ローカルの cgb_bg_color/cgb_obj_color(RGB単色ラッパーも同様)は、パレットのインデックスに自動インクリメントを設定しません。CGB実行では0x1234の下位バイトが0x12で上書きされることを確認しました。単色設定には公開版の修正、または colors 系の配列APIを使ってください。DMGでは書き込みを行いません。
ローカルの metasprite_draw は完了時に first_id+count へ使用数を伸ばします。上限40で途中終了した場合は使用数を更新しません。再現例では first_id=39、count=2 の戻り値は1、スロット39は有効、使用数は0でした。公開版は各スロットを処理する際に使用数を更新します。
ローカル版は vram_get_queue_used と vram_get_queue_free を提供しますが、公開版の vram_get_queue_capacity はありません。容量には VRAM_QUEUE_MAX(既定32)を使えます。1件の登録後は使用数1、空き31でした。
wire3d_dmg.h は DrawRect2D の宣言を96行プロファイルに限定します。DrawBoxEdges2D は両プロファイルで宣言され、DrawIndexedEdges は120行プロファイルだけです。プロファイルに対応するソースを1つ選んでください。今回これらの描画結果は再検証していません。 保存したlib内には DrawRect2D と DrawBoxEdges2D の実装が見つかりません。宣言だけの項目なので、呼び出すには別途実装が必要です。
ローカルの audio_vblank は公開版の AudioVBlank_QueueReset/QueueRefill/QueuePlay と audio_vblank_queue.inc を含みません。ZX0も公開版だけにあります。これらは公開版の追加機能として扱い、ローカル版の検証済み機能に含めません。
kq3d_overlap_sphere_aabb
u8 kq3d_overlap_sphere_aabb(const KQBody3D* sphere, const KQBody3D* box);KQ3D_BODY_SPHERE=0x04 は球と箱の接触を選びます。球の半径は half_x です。kq3d_overlap_sphere_aabb(sphere, box) は接する境界を含めて判定し、NULLまたは半径が0以下なら0を返します。球同士の専用ソルバはありません。座標差、積、二乗和を16ビットの範囲に収めてください。
今回実行した検証
2D標準経路:KOKURA C APIの自動ハードウェア選択で8,872ケース。全件成功。速度制限、接触、符号付きQ8乗算、接触時刻を照合しました。明示的なDMG/CGB別試験ではありません。
物理境界試験:19ケースをDMG・CGBで実行し、計38実行が全件成功しました。3D追加機能とスプライト/VRAM/パレットの再現試験も、それぞれ両モードで期待値と一致しました。パレットとスプライトは不具合の再現成功であり、正常動作の合格とは区別します。
これはC#版コンパイラが生成したROMのエミュレータ検証です。Rust版の同等性、実機、音声、描画、全APIの正常動作を証明する結果ではありません。既存の画像・検証結果は各記録のソース指紋に結び付いた過去の結果です。
ソース指紋・入力・期待値・実測値・再実行手順 · evidence.tar.gz · verify_rom_cases.py · verify_surface.py
1 ライブラリの使い方
ライブラリは、繰り返し使う処理をまとめたCソースです。KITAQGB本体の __ 組み込み関数を土台にしています。画面制御などの小さな機能から、物理、3D、通信まであります。
#include "input.h"
// 起動時に一度
input_init();
// ゲームの1フレームに一度
input_update();
if (input_pressed(BTN_A)) { /* 決定処理 */ }.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\gb_input.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input.gb --profile=dev各API辞典には宣言、所属ヘッダー、実装ソース、呼び出し例を掲載しています。引数を受け渡すコード断片では、使うバッファやオブジェクトを呼び出し側で準備してください。ROMとしてビルドできる教材は「完全なサンプルプログラム」欄にあります。
2 1フレームの組み立て
system_init はフレーム状態を初期化してNMIを有効にします。system_wait_vblank はNMIを待ち、ソフト側のフレーム数を増やしてから、登録したコールバックを同期的に1回呼びます。system_set_vblank_callback(0) で解除できます。コールバックはNMIハンドラーの中ではなく、待機関数を呼んだ側で実行します。
- 入力を1回更新する。
- 移動・当たり判定・ゲーム状態を計算する。
- 描画コマンドとOAMを準備する。
- VBlankに合わせて画面へ反映する。
- 採用した方式の音楽更新を行う。
vram_flush や sprite_flush_oam のような待機付き処理を重ねると、1回のゲーム更新で2フレーム待ってしまうことがあります。待機済みなら対応する _now 関数を使う設計もできますが、呼ぶタイミングを自分で保証してください。
3 入力・リピート
input_down(mask) は押している間、input_pressed(mask) は押した瞬間、input_released(mask) は離した瞬間、input_repeat(mask) はメニュー向けの連続入力です。状態は input_update() ごとに更新されます。1フレーム中に何度もupdateすると押下エッジが失われます。
| ボタン | マスク |
|---|---|
| 右・左・上・下 | 0x01 / 0x02 / 0x04 / 0x08 |
| A・B・SELECT・START | 0x10 / 0x20 / 0x40 / 0x80 |
gb_input.c はAを押した回数を表示します。押し続けても増え続けないことを確かめ、input_pressed を input_down に変えて違いを試してください。
4 背景・文字・VRAM
vram_queue_bg_tile は1枚、vram_queue_bg_rect は長方形、vram_queue_bg_block は配列を転送する予約です。戻り値や vram_get_overflowed() を調べ、キュー容量を超えていないか確認します。転送元がポインタで保持される予約では、flush完了までデータを変更せず、有効なバンクとメモリを保ちます。
text.c / menu.c は rpg.h の文字・選択肢・ウィンドウを提供します。文字列からどのタイル番号を引くか、あらかじめどのフォントをVRAMに置くかを合わせて使います。本書の m_text のフォント配置とは自動的には共通になりません。
5 スプライト・アニメーション
sprite_init、sprite_alloc、sprite_set_tile、sprite_set_pos の順に準備します。GBでは全体40個、走査線あたり10個という制約があるため、メタスプライトを大量に並べる場合は行ごとの密度も考えます。sprite_warn_scanline_overflow と sprite_max_scanline_count は配置の調査に使えます。
MetaSpritePart は複数OBJの相対配置、SpriteAnim はタイルのフレーム番号と更新間隔を持ちます。metasprite_draw の数と確保したOBJ枠を合わせます。gb_sprite.c の文字Aも、文字のタイルをOBJに使った例です。
6 カラー・スクロール・ラスタ・カメラ
cgb_bg_rgb / cgb_obj_rgb のRGBは各0~31です。PCの0~255の値をそのまま渡しません。CGB_RGB15 はこの3成分を16ビットの容器へ詰めます。高水準のCGBパレットヘルパーはDMGでは何もしないように作られています。
Scroll_SetBg は背景座標、Scroll_SetWindow はウィンドウ、camera はゲーム世界の座標から表示範囲を決めます。カメラの固定小数点値と画面のピクセル値を区別してください。
raster.c は画面を帯に分けるスクロール表や、行ごとのX方向変形を組み立てます。Scroll_SplitCommit 系はVBlank/STATベクターを使用します。別の音楽IRQ処理と同じベクターを独立に所有させず、必要なら共通ディスパッチャーを設計します。
7 音楽・効果音
audio_hwregs_gb.c → audio.c → ゲーム本体の順にコンパイルします。既にゲーム側にNR10~NR52等の定義があるなら、同じ定義ファイルを重ねません。Audio_Init の後、通常は1フレームに1回 Audio_Update を呼びます。
Audio_PlayMusic(bank,song) は曲のバンクを明示します。Audio_PlaySFXBanked は別バンクの効果音用です。優先度による競合を考慮して同じ音源チャンネルを譲り合います。GBの物理音源はCH1/CH2/CH3/CH4の4系統です。
音楽ストリームの AUDIO_CMD_NOTE / AUDIO_CMD_SET_INST / AUDIO_CMD_STOP_CHANNEL と通常APIは、共通の番号 0=CH1、1=CH2、2=CH3、3=CH4 を使います。CH1はスイープ付きパルス音、CH2はパルス音、CH3は波形メモリ、CH4はノイズです。ノート上限はヘッダーの AUDIO_NOTE_MAX=67 です。
効果音の基本CH1ストリームはノートと音量値の組をフレームごとに読み、ノート0で終了します。CH3は別のマーカーと書式です。gb_sound.c を参照してください。フェードは Audio_Update で進むため、更新を止めるとフェードも止まります。
8 VBlank IRQ音楽
audio_vblank.c は専用の再生形式を使います。時間を指定するレコードは delay, ch1_note, ch2_note, ch3_note, ch4_noise_param の5バイトです。直接参照できる曲を読む方式と、WRAMキューから読み出す方式があります。キュー方式ではAudioVBlank_QueueResetで初期化し、AudioVBlank_QueueRefillで5バイト単位のレコードを補充してからAudioVBlank_QueuePlayで開始します。補充は前景処理の1か所から行います。LOOP は直接参照方式だけ、IMMEDIATE はキュー方式だけで認識されます。通常の audio.c のストリームはそのまま渡せません。
# この方式を採用したROMに対してのみ行います。
.\kitaqgb\scripts\patch_gb_vblank_irq.ps1 .\out\game.gb .\out\game.mapパッチは0x0040のVBlankベクターを設定し、チェックサムを更新します。独自VBlank ISRやスクロール分割処理との所有権を確認します。ビルド成功だけで音が出たと判断せず、KOKURAで録音し、曲が進むことを確認してください。
9 固定小数点・物理・3D
fixed.h のQ8.8では256が1.0です。例えば128は0.5です。fix_from_int、fix_mul、fix_to_int を使う例を gb_fixed.c に示します。値が取り得る範囲を先に設計すると、オーバーフローを避けやすくなります。
physics2d は矩形、physics2d_circle は円、physics3d は3DのAABBを扱います。利用側でworldとbodyの配列を用意して初期化し、速度や重力を設定してstepを呼びます。位置と1ステップ当たりの速度は単位を統一してください。ライブラリによる暗黙の画素換算はありません。円の例は位置40・速度2を使います。逆質量0は固定物体です。係数の表現と途中計算の範囲は各ヘッダーを確認してください。特に kq2d_body_apply_friction は係数を符号付き8ビットに変換するため、128~255は負数になります。通常の符号なしQ8の減衰係数としては扱えません。1ステップのプログラム例は gb_circle.c です。
wire3d_dmg は、ゲームボーイ向けのモノクロ・ワイヤーフレーム描画ライブラリです。128×96は wire3d_dmg_96.c、128×120は wire3d_dmg.c をビルドし、Wire3DDMG_* を使います。wire3d と dmg3d も、それぞれ96行と120行の設定を選ぶ入口として使えます。1つのプログラムでは入口を1つだけビルドしてください。wire3d_cgb はカラー専用の描画ライブラリです。それぞれが使用するRAM・VRAM・画面領域を確保してください。白黒の2方式は、奥行きの範囲をまたぐ辺を切り詰めずに省略します。シーンの隠線処理も、面の外接矩形と線上の5点を使う近似であり、画素ごとの奥行き判定ではありません。 CGB版は倍速とDMAを使用し、--cgb=cgb_only が必要です。
Wire3DDMG_BeginFrame (WIRE3D_DMG_HEIGHT=96) は描画バッファを消去します。一方、Wire3DDMG_BeginFrame (WIRE3D_DMG_HEIGHT=120) は隠線判定の状態だけを戻し、描画データはVRAMへ転送したときに消去されます。差分転送では前フレームのタイルも処理して古い画素を消します。補助転送は主バッファの一部を共有しており、独立したバッファではありません。採用する方式に合ったフレーム処理順にしてください。
CGB版の線には色番号1・2・3を使います。通常の128×96描画では色のビットを重ねるため、色1と色2が交差すると色3になります。色0で線を消すことはできません。フレームを消去するか、専用の消去関数を使ってください。通常の Wire3DCGB_DrawLine2D とモデル描画は差分転送の範囲を記録しません。線の範囲も記録する場合は Wire3DCGB_DrawLineClipped2D を使い、画面全体を次の差分転送に含める場合は Wire3DCGB_InvalidateFrameHistory を呼びます。
160×144モードでは、1フレームに割り当てられるタイルは最大127枚です。高速な線描画でタイルの割り当てに失敗したり、画面外の座標を指定したりすると、Wire3DCGB_GetFullScreenOverflow() が非ゼロとなり、次のフレーム初期化まで画素の書き込みを停止します。頂点は選択した画面内に収めてください。三角形マスクの余白の右端は128×96ではX=127、全画面ではX=159までです。特に全画面描画とFastMapでは、APIの説明にあるWRAMバンク設定を守ってください。
10 ゲーム状態・オブジェクト・弾幕
scene はタイトル・ゲーム・ポーズ等の切り替え、entity は固定数のオブジェクトプール、chain は関節追従と座標履歴を扱います。蛇やひもの各節を動かすには ChainBody を使います。プールの生成失敗は0xFFなどの戻り値で確認し、成功を確認してから entity_get の指す先を使います。
danmaku は固定小数点の弾プール、方向・扇状生成、被弾・かすりなどを扱います。CGBのBG合成経路は通常のOBJ上限とは別ですが、更新時間とBG転送量の上限は残ります。多い弾数だけを目標にせず、1フレームの処理時間を測ります。
11 RPG・ADV・SLG・保存
rpg.h に乱数、フラグ、クエスト、圧縮、文字、メニュー、スクリプト、マップ、保存、経路探索の宣言がまとまっています。本体は rng.c、flags.c、rle.c、text.c などに分かれます。辞典の実装ファイルを確認して必要な単位だけ結合します。
rng_seed を固定すると乱数列を再現でき、テストが楽になります。rand_range / rng_range の上限の扱いは実装を確認してください。flag_get / flag_set はビット集合です。save.c はMBC5風SRAMアクセスを前提とした保存形式を持ち、ROMヘッダーのRAM容量とゲーム側の保存範囲を一致させます。
slg.h の盤面・合法手リスト・undoと、slg_path.c の経路探索は、ゲーム固有の評価やルールと分けて使います。幅・高さ・作業配列の大きさは、引数だけでなくライブラリ上限にも合わせます。
12 通信
基本の link.c はシリアルのバイト転送、link_packet.c は任意のパケット層です。link_hwregs_gb.c を先に結合します。ポーリングと割り込み方式では呼ぶ処理が異なり、割り込み方式のベクター0x0058はゲーム側で接続します。
論理的な Link4_* と物理Nintendo DMG-07用の LinkDmg07_* は別系統です。DMG-07は外部クロックで動き、LinkDmg07_Poll を高頻度に、LinkDmg07_TickFrame を1フレームに1回呼びます。1フレームに一度のPollだけでは間に合わない可能性があります。KOKURAのpair/dmg07ジョブを使い、接続、開始、切断、再接続を分けて試します。
13 バンク・素材・デバッグ
bank.h の BankPtr はバンク番号とポインタをまとめます。far_data_read は別バンクの素材をRAMへ読むための窓口です。asset は素材IDと記述表を結びます。素材の寿命・バンク・サイズはゲーム側で管理します。
debug_trace_u8 / debug_trace_u16 はRAM内の記録、debug_assert_fail はコード番号を残す機能です。PCのコンソールへ自動出力するprintfではありません。エミュレータのメモリ観測と合わせて読みます。gb_debug.c ではHP=42を記録します。
vram_get_queue_capacity() はキューの総項目数(標準32項目)、vram_get_queue_free() は未使用の項目数、vram_get_queue_used() は使用中の項目数を返します。転送量にかかわらず、予約した処理1件で1項目を使用します。いずれも転送キューの容量であり、実際のVRAMの未使用領域を調べる関数ではありません。
完全なサンプルプログラム
各プログラムの本体、共通ヘッダー、指定ascii.cとその変換素材を同梱しています。説明中のm_*は本書専用ヘルパーです。ビルドと操作・期待結果を一組として参照してください。
英数字・記号の一覧表示 gb_font.c
目的:同梱の自作フォントで使える文字と、その表示方法を確認します。
処理の流れ:m_initで画面とフォントを準備し、m_textで文字列を行ごとに表示します。二重引用符は文字コード34をm_putへ渡して描きます。
操作と期待する結果:大文字26字、小文字26字、数字10字、記号30字が並びます。各文字が欠けずに表示されるか確認してください。操作は不要です。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_font.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_font.gb --profile=dev --rst-disable --stack-bank=fixed// Display the letters, digits and punctuation from the supplied ascii.c font.
// Expected: Uppercase, lowercase, numbers and all 30 supplied punctuation glyphs
#include "gb_common.h"
// Lay out every supplied character category, yielding between rows. The final
// m_put uses character code 34 to display the double quote separately.
void main() {
m_init();
m_text(2,3,"FONT");
m_text(2,5,"ABCDEFGHIJKLMNOP"); m_wait();
m_text(2,6,"QRSTUVWXYZ"); m_wait();
m_text(2,8,"0123456789"); m_wait();
m_text(2,10,"abcdefghijklmnop"); m_wait();
m_text(2,11,"qrstuvwxyz"); m_wait();
m_text(2,13,"!#$%&'()*+,-./"); m_wait();
m_text(2,14,":;<=>?@[]^_`{}~"); m_wait();
m_put(16,13,34); m_wait();
while (1) { m_wait(); }
}最初の文字表示 gb_hello.c
目的:ROMを起動し、文字列と数値を背景に表示する最小の流れを学びます。
処理の流れ:m_initの後にm_textで挨拶を描き、m_numberに42を渡します。最後のループは画面を保ったままフレームを待ちます。
操作と期待する結果:HELLO WORLDと042が表示されます。042は3桁にそろえた十進数42です。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_hello.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_hello.gb --profile=dev --rst-disable --stack-bank=fixed// Display a first message on screen.
// Expected: HELLO WORLD and 042
#include "gb_common.h"
// Initialize the display/font, write HELLO WORLD and a three-digit 042,
// then wait for frames so the result remains visible.
void main() {
m_init();
m_text(2,3,"HELLO");
m_text(2,5,"HELLO WORLD");
m_number(42);
while (1) { m_wait(); }
}桁あふれを避ける算術 gb_arithmetic.c
目的:8ビットに収まらない途中結果を16ビットで扱う方法を学びます。
処理の流れ:(u16)200 + 100で300を作り、10で割ってから表示用にu8へ変換します。足し算の前の型変換に注目してください。
操作と期待する結果:030、つまり計算結果30が表示されます。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_arithmetic.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_arithmetic.gb --profile=dev --rst-disable --stack-bank=fixed// Variables, arithmetic and type conversion.
// Expected: 030
#include "gb_common.h"
// Widen before adding 200 + 100, divide the 16-bit sum, and display 30.
// The cast demonstrates avoiding an 8-bit intermediate result.
void main() {
m_init();
m_text(2,3,"ARITHMETIC");
u16 sum;
sum = (u16)200 + 100;
m_number((u8)(sum / 10));
while (1) { m_wait(); }
}条件分岐と繰り返し gb_control.c
目的:for、continue、while、do/while、switchを小さな計算で試します。
処理の流れ:0~7を加算しますが3はcontinueで飛ばすため合計は25です。whileで30、do/whileで32まで増やし、switchのcase 32で42に置き換えます。
操作と期待する結果:042が表示されます。各ループと分岐を順番に通った計算結果です。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_control.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_control.gb --profile=dev --rst-disable --stack-bank=fixed// Conditional branches and loops.
// Expected: 042
#include "gb_common.h"
// Exercise loop control and a final branch, then display the selected value 42.
void main() {
m_init();
m_text(2,3,"CONTROL");
u8 i; u8 sum; sum=0;
for (i=0;i<8;i++) { if (i==3) continue; sum=(u8)(sum+i); }
while (sum<30) { sum++; }
do { sum++; } while (sum<32);
switch (sum) { case 32: sum=42; break; default: sum=0; break; }
m_number(sum);
while (1) { m_wait(); }
}関数・配列・構造体・ポインタ gb_aggregate.c
目的:複数の値をまとめ、関数やポインタを通して扱う方法を学びます。
処理の流れ:Pointの(10,20)を別の構造体へコピーし、ポインタから配列に1~4を書きます。addの戻り値30に配列の1、2、4と定数5を加えます。
操作と期待する結果:合計42が042と表示されます。構造体コピー、ptr[i]による書き込み、関数の戻り値を順に追ってください。
![関数・配列・構造体・ポインタ: 合計42が042と表示されます。構造体コピー、ptr[i]による書き込み、関数の戻り値を順に追ってください。](verification/gb_aggregate.png)
.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_aggregate.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_aggregate.gb --profile=dev --rst-disable --stack-bank=fixed// Functions, arrays, structures and pointers.
// Expected: 042
#include "gb_common.h"
typedef struct { u8 x; u8 y; } Point;
// Add two byte values and explicitly narrow the returned result to u8.
u8 add(u8 a,u8 b) { return (u8)(a+b); }
// Copy a structure, fill an array through a pointer, and combine selected values
// through add() to produce 42. Keep the final screen visible in the idle loop.
void main() {
m_init();
m_text(2,3,"AGGREGATE");
Point a; Point b; u8 i; u8 data[4]; u8* ptr;
a.x=10; a.y=20; b=a; ptr=data;
for(i=0;i<4;i++) ptr[i]=(u8)(i+1);
m_number((u8)(add(b.x,b.y)+ptr[0]+ptr[1]+ptr[3]+5));
while (1) { m_wait(); }
}ビット操作とバッファのコピー gb_bits_memory.c
目的:複数のフラグを1バイトにまとめ、バッファを別の領域へコピーします。
処理の流れ:__memsetで4バイトを0にし、ビット3を立て、ビット1を反転します。__memcpyで4バイトすべてを別配列にコピーします。
操作と期待する結果:コピー先の先頭バイトが010になります。ビット3の8とビット1の2を合わせた十進数10です。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_bits_memory.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bits_memory.gb --profile=dev --rst-disable --stack-bank=fixed// Bit operations and memory copying.
// Expected: 010
#include "gb_common.h"
void __memset(void* dst,u8 value,u16 len);
void __memcpy(void* dst,const void* src,u16 len);
void __bit_set(u8* base,u16 bit);
void __bit_toggle(u8* base,u16 bit);
// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main() {
m_init();
m_text(2,3,"BITS MEMORY");
u8 data[4]; u8 copy[4];
__memset(data,0,4); __bit_set(data,3); __bit_toggle(data,1);
__memcpy(copy,data,4); m_number(copy[0]);
while (1) { m_wait(); }
}押した瞬間だけを数える gb_input.c
目的:ボタンを押し続ける状態と、新しく押した瞬間を区別します。
処理の流れ:毎フレームinput_updateを呼び、input_pressed(BTN_A)が真のときだけカウンターを増やします。
操作と期待する結果:最初は000です。Aを押すと001になり、押し続けても増えません。離して押し直すと002になります。255の次は000に戻ります。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/input.c kitaq-docs/samples/gb_input.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_input.gb --profile=dev --rst-disable --stack-bank=fixed// Distinguish a new button press from a held button.
// Expected: 000 initially; A increments once per press
#include "gb_common.h"
#include "input.h"
u8 manual_count;
// Sample buttons once per frame and increment only on a new A press.
// Holding A does not repeatedly increment; the byte-sized counter wraps after 255.
void main() {
m_init();
m_text(2,3,"INPUT");
input_init();
m_number(0);
while (1) { m_wait(); input_update(); if(input_pressed(BTN_A)) { manual_count++; m_number(manual_count); } }
}Q8.8固定小数点の掛け算 gb_fixed.c
目的:整数とQ8.8固定小数点の相互変換、および掛け算の基本を学びます。
処理の流れ:3と4をfix_from_intでQ8.8へ変換し、fix_mulで掛け、fix_to_intで整数へ戻します。
操作と期待する結果:3×4の結果12が012と表示されます。画面に出るのは内部の256倍された値ではなく、整数へ戻した値です。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaq-docs/samples/gb_fixed.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_fixed.gb --profile=dev --rst-disable --stack-bank=fixed// Calculate with signed Q8.8 fixed-point values.
// Expected: 012
#include "gb_common.h"
#include "fixed.h"
// Convert 3 and 4 to Q8.8, multiply them, convert the result back to an
// integer, and display 12.
void main() {
m_init();
m_text(2,3,"FIXED");
fix8 a; fix8 b; a=fix_from_int(3); b=fix_from_int(4);
m_number((u8)fix_to_int(fix_mul(a,b)));
while (1) { m_wait(); }
}固定長プールからの生成 gb_entity.c
目的:動的メモリ確保を使わず、決まった個数の領域からオブジェクトを作ります。
処理の流れ:entity_initで初期化し、種類1のオブジェクトを(20,30)に作ります。IDが失敗値0xFFでないことを確認してvxへ2を設定します。
操作と期待する結果:001は使用中オブジェクトの個数です。IDや座標ではありません。この例は更新・描画を行わないので、動くキャラクターは表示されません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/entity.c kitaq-docs/samples/gb_entity.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_entity.gb --profile=dev --rst-disable --stack-bank=fixed// Allocate an entity from a fixed-capacity object pool.
// Expected: 001
#include "gb_common.h"
#include "entity.h"
// Create one entity and check the 0xFF allocation-failure sentinel before
// accessing its velocity. Display the pool occupancy, not the entity ID.
void main() {
m_init();
m_text(2,3,"ENTITY");
u8 id; entity_init(); id=entity_create(1,20,30);
if(id!=0xFF) entity_get(id)->vx=2; m_number(entity_count_active());
while (1) { m_wait(); }
}診断値をRAMへ記録する gb_debug.c
目的:画面表示とは別に、ゲーム中の変数を診断用バッファへ残します。
処理の流れ:debug_initで初期化し、フレームタグ1を設定してHPという名前で42を記録します。
操作と期待する結果:画面の001は記録件数です。HP=42という値そのものは画面ではなく診断バッファにあります。画面だけでは記録内容までは確認できません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/debug.c kitaq-docs/samples/gb_debug.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_debug.gb --profile=dev --rst-disable --stack-bank=fixed// Record diagnostic values in the debug trace.
// Expected: 001; trace log contains HP=42
#include "gb_common.h"
#include "debug.h"
// Record HP = 42 with frame tag 1 and display the number of trace entries.
// The trace buffer contains the value; the screen displays the count, not HP.
void main() {
m_init();
m_text(2,3,"DEBUG");
debug_init(); debug_set_frame(1); debug_trace_u8("HP",42);
m_number(debug_get_trace_count());
while (1) { m_wait(); }
}座標履歴のリングバッファ gb_chain.c
目的:キャラクターの軌跡などに使う座標履歴の格納方法を学びます。
処理の流れ:4点分の配列をchain_initに渡し、chain_push_headで(20,30)を1点追加します。
操作と期待する結果:042は処理後に表示する固定の目印です。保存した座標や件数を表していません。履歴の内容を確かめるにはtrailとpointsを観察します。追従する胴体の描画例ではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/chain.c kitaq-docs/samples/gb_chain.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_chain.gb --profile=dev --rst-disable --stack-bank=fixed// Store a position history in a ring buffer.
// Expected: 042; history contains (20,30)
#include "gb_common.h"
#include "chain.h"
Chain trail; ChainPoint points[4];
// Attach four history slots and record one point. The displayed 42 is a fixed
// completion marker; inspect the chain to verify the stored (20,30) point.
void main() {
m_init();
m_text(2,3,"CHAIN");
chain_init(&trail,points,4); chain_push_head(&trail,20,30);
m_number(42);
while (1) { m_wait(); }
}ライブラリのフレームカウンター gb_system.c
目的:フレーム待機とライブラリが管理する経過フレーム数の関係を確認します。
処理の流れ:system_initの後にsystem_wait_vblankを2回呼び、system_get_frame8の値を表示します。
操作と期待する結果:002が表示されます。その後のループは本書のm_waitを使うため、この表示値は増え続けません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/system.c kitaq-docs/samples/gb_system.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_system.gb --profile=dev --rst-disable --stack-bank=fixed// Count completed frame waits.
// Expected: 002
#include "gb_common.h"
#include "system.h"
// Reset the library frame count, perform two counted waits, and display 2.
// Subsequent m_wait calls leave that library counter unchanged.
void main() {
m_init();
m_text(2,3,"SYSTEM");
system_init(); system_wait_vblank(); system_wait_vblank();
m_number(system_get_frame8());
while (1) { m_wait(); }
}現在のシーンIDを選ぶ gb_scene.c
目的:タイトル画面やゲーム本編などを区別するシーン番号の選択方法を学びます。
処理の流れ:0で初期化された2件のSceneDefを登録し、scene_change(1)でID 1を選択します。
操作と期待する結果:001はscene_get_currentが返す現在のIDです。この例にはシーンのコールバックや画面切り替えの描画は登録していません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scene.c kitaq-docs/samples/gb_scene.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scene.gb --profile=dev --rst-disable --stack-bank=fixed// Switch the current scene ID.
// Expected: 001; no callbacks registered
#include "gb_common.h"
#include "scene.h"
SceneDef manual_scenes[2];
// Register two zero-initialized scene entries, select ID 1 and display it.
// No callbacks are registered in this example.
void main() {
m_init();
m_text(2,3,"SCENE");
scene_init(manual_scenes,2); scene_change(1);
m_number(scene_get_current());
while (1) { m_wait(); }
}世界座標から画面座標へ gb_camera.c
目的:カメラ位置を引いて、ゲーム世界の位置を画面内の位置へ変換します。
処理の流れ:Camera_SetにQ8.8の2560、つまりX=10ピクセルを渡します。世界座標X=52をCamera_WorldToScreenXで変換します。
操作と期待する結果:52−10=42なので042と表示されます。このプログラムは座標変換の例で、背景を動かすものではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaqgb/lib/camera.c kitaq-docs/samples/gb_camera.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_camera.gb --profile=dev --rst-disable --stack-bank=fixed// Convert world coordinates to screen coordinates.
// Expected: 042: world x=52 minus camera x=10
#include "gb_common.h"
#include "camera.h"
Camera8_8 cam;
// Set the camera X coordinate to 10 pixels in Q8.8 (10 * 256), then subtract
// it from world X = 52 to display screen X = 42.
void main() {
m_init();
m_text(2,3,"CAMERA");
Camera_Init(&cam); Camera_Set(&cam,2560,0);
m_number((u8)Camera_WorldToScreenX(&cam,52));
while (1) { m_wait(); }
}RLEデータをRAMへ展開する gb_rle.c
目的:同じ値の繰り返しを、個数と値の組で保存する簡単な圧縮形式を試します。
処理の流れ:packedの{3,42,0}は「42を3回、0の個数で終了」です。rle_decodeへ3バイトの展開先を渡します。
操作と期待する結果:最後のバイトout[2]が042と表示されます。展開先の3バイトはすべて42になる想定です。
![RLEデータをRAMへ展開する: 最後のバイトout[2]が042と表示されます。展開先の3バイトはすべて42になる想定です。](verification/gb_rle.png)
.\kitaqgb\kitaqgb.exe kitaqgb/lib/rle.c kitaq-docs/samples/gb_rle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rle.gb --profile=dev --rst-disable --stack-bank=fixed// Decode run-length encoded data.
// Expected: 042; three bytes each equal to 42
#include "gb_common.h"
#include "rpg.h"
__prg_rom u8 packed[] = {3,42,0};
// Decode count/value pair (3,42), terminated by a zero count, into a
// three-byte buffer and display the final decoded byte.
void main() {
m_init();
m_text(2,3,"RLE");
u8 out[3]; rle_decode(out,packed); m_number(out[2]);
while (1) { m_wait(); }
}盤面のセルを読み書きする gb_board.c
目的:マス目のゲームで使う2次元座標と、保存用配列の関係を学びます。
処理の流れ:16バイトの配列を4×4の盤面として登録し、0でクリアしてからセル(2,1)へ42を書きます。同じ座標を読み返します。
操作と期待する結果:読み出した42が042と表示されます。4×4の盤面そのものを描画する例ではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/slg_board.c kitaq-docs/samples/gb_board.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_board.gb --profile=dev --rst-disable --stack-bank=fixed// Read and write cells in a game board.
// Expected: 042
#include "gb_common.h"
#include "slg.h"
SLGBoard board; u8 cells[16];
// Create a cleared 4-by-4 board in caller-owned storage, write 42 at (2,1),
// and display the value read back from the same cell.
void main() {
m_init();
m_text(2,3,"BOARD");
slg_board_init(&board,4,4,cells); slg_board_clear(&board,0);
slg_board_set(&board,2,1,42); m_number(slg_board_get(&board,2,1));
while (1) { m_wait(); }
}矩形ボディを1ステップ進める gb_physics.c
目的:ワールドと矩形ボディを初期化し、速度から位置を更新する最小の例です。
処理の流れ:X=40、Y=40にボディを作り、速度を(2,0)、重力を0にしてkq2d_stepを1回呼びます。
操作と期待する結果:更新後のX=42が042と表示されます。この例の位置は整数ピクセルです。箱の形や連続したアニメーションを描く例ではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaqgb/lib/physics2d.c kitaq-docs/samples/gb_physics.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_physics.gb --profile=dev --rst-disable --stack-bank=fixed// Advance a rectangular body through one physics step.
// Expected: 042: integer pixel coordinates
#include "gb_common.h"
#include "physics2d.h"
KQWorld2D world; KQBody2D boxes[1];
// Create one box at X = 40, set horizontal velocity 2 and zero gravity,
// then display its X coordinate after a single physics step.
void main() {
m_init();
m_text(2,3,"PHYSICS");
kq2d_world_init(&world,boxes,1);
kq2d_body_init(&boxes[0],40,40,4,4);
kq2d_body_set_velocity(&boxes[0],2,0);
world.gravity_x=0; world.gravity_y=0;
kq2d_step(&world); m_number((u8)boxes[0].x);
while (1) { m_wait(); }
}通信の完了とタイムアウト gb_link.c
目的:シリアル転送が完了した場合と、制限時間内に完了しない場合を分けて扱います。
処理の流れ:マスターとして42を送信し、Link_WaitByteで待ちます。成功すれば受信値、タイムアウトならLink_Cancelの後に0を表示します。
操作と期待する結果:未接続で転送が完了すれば255、待機がタイムアウトすれば000、相手が接続されていれば受信した値が表示されます。042固定の例ではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/link_hwregs_gb.c kitaqgb/lib/link.c kitaq-docs/samples/gb_link.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_link.gb --profile=dev --rst-disable --stack-bank=fixed// Handle serial transfers that do not deliver a byte in time.
// Expected: 255 for disconnected link; 000 if timeout; peer value when connected
#include "gb_common.h"
#include "link.h"
// Start a master transfer of byte 42 and wait with a bounded timeout.
// Display the received byte on completion; cancel and display zero on timeout.
void main() {
m_init();
m_text(2,3,"LINK");
u8 value; Link_InitMaster(); Link_BeginTransfer(42);
if(Link_WaitByte(4,&value)) m_number(value);
else { Link_Cancel(); m_number(0); }
while (1) { m_wait(); }
}バンクを指定してROMを読む gb_bank.c
目的:ROMのデータをRAMへコピーするAPIの引数を学びます。
処理の流れ:値7と42の2バイトをfar_data_readでcopiedへ読み込みます。バンク引数は0、コピー長は2です。
操作と期待する結果:コピー先の2バイト目が042と表示されます。この例は固定ROMの読み出しで、異なる切替バンク間の転送を実証するものではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/bank.c kitaq-docs/samples/gb_bank.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bank.gb --profile=dev --rst-disable --stack-bank=fixed// Read data using an explicit ROM bank.
// Expected: 042; explicit bank argument 0
#include "gb_common.h"
#include "bank.h"
__prg_rom u8 values[] = {7,42}; u8 copied[2];
// Copy two ROM bytes using bank argument zero and display the second byte.
// This introduces the API; it does not test switching between distinct payload banks.
void main() {
m_init();
m_text(2,3,"BANK");
far_data_read(0,values,copied,2); m_number(copied[1]);
while (1) { m_wait(); }
}素材IDからデータを読み込む gb_asset.c
目的:素材の位置と長さを表へ登録し、IDで参照する仕組みを学びます。
処理の流れ:ID 0に種類RAW、バンク0、{7,42}のポインタ、長さ2を登録します。asset_load_rawで2バイトのRAMへ読み込みます。
操作と期待する結果:コピー先の2バイト目が042と表示されます。画像表示ではなく、生のバイト列を素材IDで取得する例です。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/asset.c kitaq-docs/samples/gb_asset.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_asset.gb --profile=dev --rst-disable --stack-bank=fixed// Load asset bytes into RAM by asset ID.
// Expected: 042
#include "gb_common.h"
#include "asset.h"
AssetDesc table[1]; __prg_rom u8 values[] = {7,42}; u8 copied[2];
// Register a two-byte raw asset, copy it into RAM and display its second byte.
// This example uses bank zero and a destination sized for the complete payload.
void main() {
m_init();
m_text(2,3,"ASSET");
table[0].type=ASSET_TYPE_RAW; table[0].bank=0;
table[0].ptr=values; table[0].len=2;
asset_set_table(0,table,1); asset_load_raw(0,copied,2);
m_number(copied[1]);
while (1) { m_wait(); }
}弾をプールへ1個登録する gb_danmaku.c
目的:弾の管理領域を初期化し、発射後の使用数を調べます。
処理の流れ:danmaku_resetの後にdanmaku_spawn(40,40,16,0)を呼び、dm_countを表示します。
操作と期待する結果:001は登録された弾の個数です。この例には更新ループや背景への弾描画処理がないため、画面に弾が飛ぶ様子は表示されません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/danmaku.c kitaq-docs/samples/gb_danmaku.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_danmaku.gb --profile=dev --rst-disable --stack-bank=fixed// Allocate a bullet and inspect the pool count.
// Expected: 001; numeric pool example, no bullet BG compositor
#include "gb_common.h"
#include "danmaku.h"
// Reset the bullet pool, spawn one bullet and display the active count.
// This numeric example does not install the background bullet compositor.
void main() {
m_init();
m_text(2,3,"DANMAKU");
danmaku_reset(); danmaku_spawn(40,40,16,0);
m_number(dm_count);
while (1) { m_wait(); }
}CGB背景パレットの色を設定する gb_cgb_palette.c
目的:Game Boy ColorのRGB値で、背景と文字の色を指定します。
処理の流れ:VBlank中にLCDを止め、パレット0の色0を白(31,31,31)、色3を紫(12,0,22)へ設定してから表示を再開します。
操作と期待する結果:CGBモードで実行すると白い背景に紫色の文字と042が表示されます。DMGの白黒表示では色の結果を確認できません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/cgb_palette.c kitaq-docs/samples/gb_cgb_palette.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_cgb_palette.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb// Set Game Boy Color palette entries.
// Expected: 042 in purple on white; CGB mode
#include "gb_common.h"
#include "cgb_palette.h"
// Set palette-zero background colors to white and purple, then display 42.
// Run the ROM in CGB mode to observe the color result.
void main() {
m_init();
m_text(2,3,"CGB PALETTE");
// Disable the LCD during VBlank so both palette bytes remain accessible.
m_wait(); M_LCDC = 0;
cgb_bg_rgb(0,0,31,31,31); cgb_bg_rgb(0,3,12,0,22);
M_LCDC = 0x91;
m_number(42);
while (1) { m_wait(); }
}背景を横へスクロールする gb_scroll.c
目的:背景の内容を書き換えず、表示開始位置を動かす方法を学びます。
処理の流れ:毎フレームphaseを1増やし、Scroll_SetBgへ渡します。8ビット値なので255の次は0になります。
操作と期待する結果:SCROLLという文字が左へ動きます。画面の数字ではなく、時間を追って文字の位置が変わることを確認してください。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaq-docs/samples/gb_scroll.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scroll.gb --profile=dev --rst-disable --stack-bank=fixed// Scroll the background horizontally.
// Expected: title moves left as SCX increases
#include "gb_common.h"
#include "scroll.h"
u8 phase;
// Advance an 8-bit horizontal scroll position once per frame, wrapping naturally
// after 255. Background motion demonstrates the changing scroll register.
void main() {
m_init();
m_text(2,3,"SCROLL");
Scroll_SetBg(0,0);
while (1) { m_wait(); phase++; Scroll_SetBg(phase,0); }
}タイル更新をキューへ積む gb_vram_queue.c
目的:描画の要求と、VRAMへ実際に書き込むタイミングを分けます。
処理の流れ:vram_queue_bg_tileでタイル座標(3,8)へ番号52を予約し、vram_flushで転送します。同梱フォントの番号52は数字4です。
操作と期待する結果:背景のタイル座標(3,8)に数字4が表示されます。全画面の数値カウンターではなく、1マスへの描画結果です。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/vram.c kitaq-docs/samples/gb_vram_queue.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_vram_queue.gb --profile=dev --rst-disable --stack-bank=fixed// Queue a VRAM update and flush it at VBlank.
// Expected: 4 at (3,8)
#include "gb_common.h"
#include "vram.h"
// Queue font tile 52 (the digit 4) at tile coordinate (3,8), then wait for
// VBlank and execute the queued transfer.
void main() {
m_init();
m_text(2,3,"VRAM QUEUE");
vram_init();
vram_queue_bg_tile(3,8,52); vram_flush();
while (1) { m_wait(); }
}OAM DMAでスプライトを表示する gb_sprite.c
目的:背景とは別に動かせるスプライトの割り当てと表示を学びます。
処理の流れ:空のプールから最初のスロット0を確保し、タイル65と位置(70,80)を設定します。OBJ表示を有効にして、毎フレームシャドーOAMを転送します。
操作と期待する結果:指定位置に文字Aのスプライトが表示されます。位置は固定です。実際のゲームでは割り当て失敗値を確認してからIDを使います。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/sprite.c kitaq-docs/samples/gb_sprite.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sprite.gb --profile=dev --rst-disable --stack-bank=fixed// Display a sprite through the OAM DMA path.
// Expected: letter A at sprite position
#include "gb_common.h"
#include "sprite.h"
// Allocate the first sprite, choose font tile 65 (A), set its position and
// enable sprite display. Flush shadow OAM after each frame wait.
void main() {
m_init();
m_text(2,3,"SPRITE");
// The freshly reset pool guarantees slot zero for this first allocation.
// In a running game, check the allocation-failure sentinel before using the returned ID.
sprite_init(); sprite_set_tile(sprite_alloc(),65);
sprite_set_pos(0,70,80); M_LCDC=(u8)(M_LCDC|2);
while (1) { m_wait(); sprite_flush_oam_now(); }
}シード付き疑似乱数 gb_rng.c
目的:同じ初期値から同じ乱数列を再現する方法を学びます。
処理の流れ:rng_seed(1234)で初期化し、rng_range(10)を1回呼びます。上限10は結果に含まれません。
操作と期待する結果:000~009のいずれかが表示されます。同じプログラムを同じシードで起動すれば同じ値になります。毎フレーム変化する例ではありません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/rng.c kitaq-docs/samples/gb_rng.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rng.gb --profile=dev --rst-disable --stack-bank=fixed// Generate a repeatable pseudorandom value from a seed.
// Expected: value 000 through 009; same seed repeats
#include "gb_common.h"
#include "rpg.h"
// Seed the generator with 1234 and display one value in [0,10).
// Restarting with the same seed reproduces the sequence.
void main() {
m_init();
m_text(2,3,"RNG");
rng_seed(1234); m_number(rng_range(10));
while (1) { m_wait(); }
}ゲーム進行フラグ gb_flags.c
目的:イベントを達成したかなどの真偽値を、フラグ番号で管理します。
処理の流れ:フラグ12をクリアしてから立て、flag_getで読み返します。
操作と期待する結果:001はフラグ12が真であることを示します。この例はRAM上のフラグ操作で、電源を切っても残るセーブ処理は行いません。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/flags.c kitaq-docs/samples/gb_flags.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_flags.gb --profile=dev --rst-disable --stack-bank=fixed// Read and write RAM-backed game-state flags.
// Expected: 001
#include "gb_common.h"
#include "rpg.h"
// Clear and set flag 12, then display its Boolean value. This does not write
// a save file or demonstrate persistence across power cycles.
void main() {
m_init();
m_text(2,3,"FLAGS");
flag_clear(12); flag_set(12); m_number(flag_get(12));
while (1) { m_wait(); }
}円形ボディを1ステップ進める gb_circle.c
目的:円の物理ワールドへボディを登録し、速度による位置更新を試します。
処理の流れ:半径4、位置(40,40)、速度(2,0)の円を有効にします。重力0、linear_damping_q8=255でkq2dc_stepを1回呼びます。
操作と期待する結果:更新後のX=42が042と表示されます。円の輪郭や衝突アニメーションを描画する例ではなく、整数座標の更新例です。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/physics2d_circle.c kitaq-docs/samples/gb_circle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_circle.gb --profile=dev --rst-disable --stack-bank=fixed// Advance a circular body through one physics step.
// Expected: 042 after one step; this example uses integer pixels
#include "gb_common.h"
#include "physics2d_circle.h"
KQCircleBody2D balls[1]; KQCircleWorld2D world;
// Activate one circle at X = 40 with horizontal velocity 2 and zero gravity.
// Advance one step and display its resulting integer-pixel X coordinate.
void main() {
m_init();
m_text(2,3,"CIRCLE");
kq2dc_world_init(&world,balls,1);
balls[0].active=1; balls[0].x=40; balls[0].y=40;
balls[0].radius=4; balls[0].inv_mass_q8=256;
balls[0].vx=2; balls[0].vy=0;
world.gravity_x=0; world.gravity_y=0; world.linear_damping_q8=255;
kq2dc_step(&world); m_number((u8)balls[0].x);
while (1) { m_wait(); }
}フレーム更新で効果音を再生する gb_sound.c
目的:効果音データを開始し、毎フレームの更新で最後まで進める方法を学びます。
処理の流れ:Audio_Initの後、Audio_PlaySFXへtoneとチャンネルID 1を渡します。メインループからAudio_Updateを1フレームに1回呼びます。
操作と期待する結果:042を表示しながら短いパルス波の効果音が鳴ります。042は固定の目印で、音量や再生位置ではありません。音の確認には音声出力または録音を使ってください。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/audio_hwregs_gb.c kitaqgb/lib/audio.c kitaq-docs/samples/gb_sound.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sound.gb --profile=dev --rst-disable --stack-bank=fixed// Play a Game Boy pulse-channel sound effect.
// Expected: 042 and a short pulse tone
#include "gb_common.h"
#include "audio.h"
__prg_rom u8 tone[] = { 24,0xF0,24,0xE0,24,0xC0,24,0xA0,24,0x80,24,0x60,0 };
// Start a pulse-channel effect and service Audio_Update once per frame
// so the effect stream advances while 42 remains on screen.
void main() {
m_init();
m_text(2,3,"SOUND");
Audio_Init(); Audio_PlaySFX(tone,1); m_number(42);
while (1) { m_wait(); Audio_Update(); }
}ライブラリAPI辞典
全554項目。各項目では、機能、引数、戻り値、制約、使用例と期待する結果を説明します。
zx0
ZX0互換の圧縮素材をRAMやVRAMへ展開するライブラリです。PC側のkitaqgb-zx0.exeで素材を作り、実行中は必要なデータだけ展開します。--format=autoはraw・RLE・ZX0のペイロードを比較して最小を選びます。同サイズならraw、RLE、ZX0の順です。9バイトのヘッダーを含め65535バイト以内に収め、実際のCPUバンクやRAM容量にも合わせて素材を分割してください。
ZX0形式と元の圧縮アルゴリズムの設計者はEinar Saukas氏です。ここで提供する圧縮器・展開器はDAISUKE OBAによるKITAQ独自実装で、MITライセンスを適用します。
リポジトリ直下で実行するPC側のコマンド例です。ツールは`tools/zx0/build.ps1`でビルドできます。
.\kitaqgb-zx0.exe tiles.bin tiles.zx0
.\kitaqgb-zx0.exe tiles.bin tiles.h --header=tiles
.\kitaqgb-zx0.exe map.bin map.kqa --format=auto
.\kitaqgb-zx0.exe tiles.zx0 restored.bin --decompresszx0_decompress
ZX0 v2の圧縮列を指定容量内のRAMへ展開します。既に展開したデータを参照して繰り返しを復元するため、出力先は読み書きできる必要があります。タイルマップやステージ表をCPUで加工してから使う場合に適しています。
書式
u16 zx0_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);引数
dst展開先の書き込み可能なRAMの先頭です。NULLは使えません。容量は配列の要素数ではなくバイト数で指定します。
capacity出力先に書き込めるバイト数です。ZX0の通常のデータは最低1バイト必要です。空のKQA1 rawデータだけは容量0で成功できます。
src現在のバンクから読み出せる圧縮データの先頭です。NULLは使えません。
packed_size入力データ全体のバイト数です。ZX0では終端までを含み、余分な末尾データは付けません。KQA1では9バイトのヘッダーも数えます。最大65535バイトです。
戻り値
成功時は展開したバイト数、エラー時は0を返します。必ずzx0_errorも確認してください。
使用条件と制限
zx0.hを読み込み、lib/zx0.cをコンパイルします。共有作業領域を使い、割り込みからの再入には対応しません。入力と出力を重ねず、CPUアドレスの折り返しや現在選択しているバンクの境界をまたがないようにしてください。ライブラリは素材のバンクを切り替えません。
zx0_errorは各呼び出しで更新します。0=成功、1=不正な引数、2=入力不足、3=不正な形式、4=出力容量不足、5=表示が有効です。エラーでは途中まで出力を書き込む場合があるため、失敗した出力は使わないでください。エラー番号は検出順で決まります。
順方向のZX0 v2に対応します。ZX0 v1、逆方向ストリーム、外部辞書は対象外です。展開前にPC側で作った素材と展開後サイズを把握し、十分な容量を確保してください。
使用例
count=zx0_decompress(decoded_map,16,packed_map,packed_map_SIZE);
if(count!=16||zx0_error!=ZX0_OK)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上段のRAM MAPには四角・ひし形・市松模様が順に並びます。中央のAUTO RLE MAPは16個の四角がつながった横棒です。下段のVRAM TILESは、VRAMへ展開した同じ3種類のタイルを並べます。CGBとFCでは四角が白、ひし形が赤、市松模様が青で、DMGでは濃淡になります。末尾のCHECKS OKは3回の展開が期待するサイズで成功したことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/zx0.h:15
Return decoded bytes, or zero on error (possibly after partial writes). Source and output must not overlap or cross their current CPU bank windows. Capacity and packed_size are byte counts. No prefix/backward/v1 streams.
kitaqgb/lib/zx0.c:52
u16 zx0_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
u8 phase; u8 low; u8* out; u8* match;
u16 count; u16 distance; u16 used; u16 upper;
zx0_error=ZX0_OK;
if(!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
kq_zx_input=(const u8*)src; kq_zx_left=packed_size;
kq_zx_mask=0; kq_zx_borrowed=0;
out=(u8*)dst; used=0; distance=1; phase=0;
// Phase 0 is literal, 1 reuses the last distance, 2 carries a new distance.
while(1) {
if(phase==2) {
upper=kq_zx_number(1);
if(zx0_error) return 0;
if(upper==256) {
if(kq_zx_left!=0) { zx0_error=ZX0_BAD_STREAM; return 0; }
return used;
}
if(upper>255) { zx0_error=ZX0_BAD_STREAM; return 0; }
low=kq_zx_byte();
distance=(u16)((upper<<7)-(low>>1));
kq_zx_borrowed=1; kq_zx_borrow_value=(u8)(low&1);
count=kq_zx_number(0);
if(count==65535) { zx0_error=ZX0_BAD_STREAM; return 0; }
count++;
} else count=kq_zx_number(0);
if(zx0_error) return 0;
if(count>capacity-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
if(phase==0) {
if(count>kq_zx_left) { zx0_error=ZX0_TRUNCATED; return 0; }
kq_zx_left-=count; used+=count;
while(count!=0) { *out++=*kq_zx_input++; count--; }
phase=kq_zx_bit()?2:1;
} else {
if(distance>used) { zx0_error=ZX0_BAD_STREAM; return 0; }
match=out-distance; used+=count;
// Forward byte copying deliberately supports overlapping matches.
while(count!=0) { *out++=*match++; count--; }
phase=kq_zx_bit()?2:0;
}
if(zx0_error) return 0;
}
}asset_decompress
--format=autoで作ったKQA1ヘッダーから方式とサイズを読み、rawコピー・RLE展開・ZX0展開を選んでRAMへ復元します。呼び出し側で方式ごとに分岐する必要はありません。
書式
u16 asset_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);引数
dst展開先の書き込み可能なRAMの先頭です。NULLは使えません。容量は配列の要素数ではなくバイト数で指定します。
capacity出力先に書き込めるバイト数です。ZX0の通常のデータは最低1バイト必要です。空のKQA1 rawデータだけは容量0で成功できます。
src現在のバンクから読み出せる圧縮データの先頭です。NULLは使えません。
packed_size入力データ全体のバイト数です。ZX0では終端までを含み、余分な末尾データは付けません。KQA1では9バイトのヘッダーも数えます。最大65535バイトです。
戻り値
成功時は展開したバイト数、エラー時は0を返します。必ずzx0_errorも確認してください。
使用条件と制限
zx0.hを読み込み、lib/zx0.cをコンパイルします。共有作業領域を使い、割り込みからの再入には対応しません。入力と出力を重ねず、CPUアドレスの折り返しや現在選択しているバンクの境界をまたがないようにしてください。ライブラリは素材のバンクを切り替えません。
zx0_errorは各呼び出しで更新します。0=成功、1=不正な引数、2=入力不足、3=不正な形式、4=出力容量不足、5=表示が有効です。エラーでは途中まで出力を書き込む場合があるため、失敗した出力は使わないでください。エラー番号は検出順で決まります。
KQA1はKITAQの素材コンテナーです。識別子4バイト、方式1バイト(0=raw、1=RLE、2=ZX0)、展開後サイズu16、ペイロードサイズu16、ペイロードの順です。u16は下位バイトが先です。RLEは個数1~255と値の組を繰り返し、個数0で終えます。空のrawは戻り値0・エラー0で成功します。
使用例
count=asset_decompress(decoded_auto,16,packed_auto,packed_auto_SIZE);
if(count!=16||zx0_error!=ZX0_OK)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上段のRAM MAPには四角・ひし形・市松模様が順に並びます。中央のAUTO RLE MAPは16個の四角がつながった横棒です。下段のVRAM TILESは、VRAMへ展開した同じ3種類のタイルを並べます。CGBとFCでは四角が白、ひし形が赤、市松模様が青で、DMGでは濃淡になります。末尾のCHECKS OKは3回の展開が期待するサイズで成功したことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/zx0.h:18
Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto. Empty raw assets return zero with zx0_error==ZX0_OK.
kitaqgb/lib/zx0.c:95
u16 asset_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
const u8* in; u8* out; u16 raw; u16 packed; u16 used;
u8 codec; u8 count; u8 value;
zx0_error=ZX0_OK;
if(packed_size<9||!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
in=(const u8*)src; out=(u8*)dst;
if(in[0]!=75||in[1]!=81||in[2]!=65||in[3]!=49) { zx0_error=ZX0_BAD_STREAM; return 0; }
codec=in[4]; raw=(u16)((u16)in[5]|((u16)in[6]<<8));
packed=(u16)((u16)in[7]|((u16)in[8]<<8));
if(packed!=packed_size-9||codec>2) { zx0_error=ZX0_BAD_STREAM; return 0; }
if(raw>capacity) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
in+=9;
if(codec==2) {
used=zx0_decompress(dst,raw,in,packed);
if(zx0_error==0&&used!=raw) zx0_error=ZX0_BAD_STREAM;
return zx0_error?0:used;
}
if(codec==0) {
if(raw!=packed) { zx0_error=ZX0_BAD_STREAM; return 0; }
used=raw; while(raw!=0) { *out++=*in++; raw--; } return used;
}
used=0;
while(packed!=0) {
count=*in++; packed--;
if(count==0) {
if(packed!=0||used!=raw) { zx0_error=ZX0_BAD_STREAM; return 0; }
return used;
}
if(packed==0) { zx0_error=ZX0_TRUNCATED; return 0; }
value=*in++; packed--;
if((u16)count>raw-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
used+=count; while(count!=0) { *out++=value; count--; }
}
zx0_error=ZX0_TRUNCATED; return 0;
}zx0_decompress_vram
LCDを停止した状態で、ZX0のタイル画像やマップを選択中のVRAMバンクへ直接展開します。VRAM自身を既出データの参照先として使うため、RAMの中間バッファは不要です。
書式
u16 zx0_decompress_vram(u16 vram_addr, const void* src, u16 packed_size);引数
vram_addrVRAMの開始アドレス0x8000~0x9FFFです。書き込み上限は0xA000直前となります。CGBでは呼び出し前にVBKを選択してください。
src現在のバンクから読み出せる圧縮データの先頭です。NULLは使えません。
packed_size入力データ全体のバイト数です。ZX0では終端までを含み、余分な末尾データは付けません。KQA1では9バイトのヘッダーも数えます。最大65535バイトです。
戻り値
成功時は展開したバイト数、エラー時は0を返します。必ずzx0_errorも確認してください。
使用条件と制限
zx0.hを読み込み、lib/zx0.cをコンパイルします。共有作業領域を使い、割り込みからの再入には対応しません。入力と出力を重ねず、CPUアドレスの折り返しや現在選択しているバンクの境界をまたがないようにしてください。ライブラリは素材のバンクを切り替えません。
zx0_errorは各呼び出しで更新します。0=成功、1=不正な引数、2=入力不足、3=不正な形式、4=出力容量不足、5=表示が有効です。エラーでは途中まで出力を書き込む場合があるため、失敗した出力は使わないでください。エラー番号は検出順で決まります。
順方向のZX0 v2に対応します。ZX0 v1、逆方向ストリーム、外部辞書は対象外です。展開前にPC側で作った素材と展開後サイズを把握し、十分な容量を確保してください。
LCDが有効ならエラー5です。VBlank中というだけでは使えません。表示・VBK・割り込みの設定は変更せず、呼び出し側がLCD停止と再開を担当します。
使用例
count=zx0_decompress_vram(0x8800,packed_tiles,packed_tiles_SIZE);
if(count!=48||zx0_error!=ZX0_OK)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上段のRAM MAPには四角・ひし形・市松模様が順に並びます。中央のAUTO RLE MAPは16個の四角がつながった横棒です。下段のVRAM TILESは、VRAMへ展開した同じ3種類のタイルを並べます。CGBとFCでは四角が白、ひし形が赤、市松模様が青で、DMGでは濃淡になります。末尾のCHECKS OKは3回の展開が期待するサイズで成功したことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/zx0.h:21
Decode directly into the selected GB VRAM bank ($8000..$9FFF), with LCD off. Preserve LCDC, VBK and interrupt state. Output must fit before $A000.
kitaqgb/lib/zx0.c:132
u16 zx0_decompress_vram(u16 vram_addr,const void* src,u16 packed_size) {
zx0_error=ZX0_OK;
if(vram_addr<0x8000||vram_addr>=0xA000) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
if(kq_zx_lcdc&0x80) { zx0_error=ZX0_DISPLAY_ACTIVE; return 0; }
return zx0_decompress((void*)vram_addr,(u16)(0xA000-vram_addr),src,packed_size);
}asset.h — 素材IDとデータ記述表
素材ライブラリは、ROMバンクに置いた素材を8ビットのIDで検索します。asset.cを取り込み、FC版ではbank.cも取り込みます。パックされた6バイトのAssetDescには、1バイトの種別、1バイトの素材バンク番号、16ビットのアドレス、バイト単位の長さを格納します。一覧と素材本体は別のバンクに配置できます。
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
RAWまたはTILES種別の素材を検索し、__vram_copyでdesc.lenバイトをすべて転送します。呼び出す前に素材本体のROMバンクを選んでください。GB版はdesc.ptrを直接使い、転送時にdesc.bankを選択する処理を持ちません。
asset_get
登録済み一覧のROMバンクからAssetDescを1件読み出し、呼び出し側が用意した領域へコピーします。取得するのは素材の情報であり、素材本体ではありません。
書式
u8 asset_get(u8 asset_id, AssetDesc* out_desc);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。out_desc6バイトの記述子1件を書き込める領域です。ヌルポインタなら一覧を読む前に失敗を返します。
戻り値
記述子をコピーしたら1、出力先がヌルまたはIDが範囲外なら0です。これらの失敗時は出力先を書き換えません。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
使用例
loaded=asset_get(0,&descriptor);
expect(loaded==1,1);
expect(descriptor.type==ASSET_TYPE_TILES && descriptor.bank==2,2);
expect(descriptor.ptr==asset_shapes && descriptor.len==144,3);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
記述子0を読み、TILES種別・バンク2・素材ポインタ・144バイトの長さを確認します。範囲外IDとヌル出力先は失敗し、検索失敗時には出力先の元の値が残ることも確認します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:24
Copy one descriptor from its ROM bank into caller storage. Return zero for a null destination or an out-of-range ID; table validity is the caller's responsibility.
kitaqgb/lib/asset.c:18
u8 asset_get(u8 asset_id, AssetDesc* out_desc)
{
if (out_desc == 0) return 0;
if (asset_id >= asset_table_count) return 0;
__farmemcpy(out_desc, asset_table_bank, asset_table + asset_id, sizeof(AssetDesc));
return 1;
}asset_get_bank
記述子から素材本体のバンク番号を読み出します。一覧を置いたバンクの番号ではありません。取得後に素材本体のバンクが選ばれた状態になるわけでもありません。
書式
u8 asset_get_bank(u8 asset_id);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。
戻り値
記述子の8ビットのbankを返し、検索失敗なら0です。有効なバンク番号も0になり得るため、成否を区別する場合はasset_get()を使います。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
各取得関数はその都度asset_getを呼び、記述子をコピーします。バンク・ポインタ・長さをまとめて使うなら、asset_getを1回呼んで各フィールドを参照します。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
使用例
payload_bank=asset_get_bank(0);
expect(payload_bank==2,17); // Payload bank, not descriptor-table bank 3.
expect(asset_get_bank(4)==0,18); // Failure sentinel; a valid descriptor may also use bank 0.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
一覧がバンク3にある場合でも、素材本体のバンク2が返ることを確認します。無効IDでは0を返すことも検査します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:32
Return the descriptor bank, or zero on lookup failure; zero can also be a valid bank.
kitaqgb/lib/asset.c:51
u8 asset_get_bank(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.bank;
}asset_get_len
記述子から素材の長さをバイト単位で取得します。返すのは登録情報であり、素材本体を調べたり、タイル数・要素数・フレーム数へ換算したりはしません。
書式
u16 asset_get_len(u8 asset_id);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。
戻り値
バイト単位の16ビット値lenを返し、検索失敗なら0です。空の素材も0なので、区別が必要ならasset_get()を使います。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
各取得関数はその都度asset_getを呼び、記述子をコピーします。バンク・ポインタ・長さをまとめて使うなら、asset_getを1回呼んで各フィールドを参照します。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
使用例
expect(asset_get_len(0)==144,21); // Bytes, not tiles.
expect(asset_get_len(3)==0 && asset_get_len(4)==0,22); // Empty asset and invalid ID both yield zero.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
9タイルの素材について、9ではなくバイト数の144を返すことを確認します。空の素材と無効IDはいずれも0です。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:37
Return the descriptor byte length, or zero if its ID cannot be resolved.
kitaqgb/lib/asset.c:68
u16 asset_get_len(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.len;
}asset_get_ptr
記述子に保存された16ビットの素材アドレスを取得します。戻るのはバンク番号を含まない近ポインタです。別のROMバンクの素材を読むには、asset_get_bank()で得た番号と組み合わせてバンク間コピー関数を使います。
書式
const u8* asset_get_ptr(u8 asset_id);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。
戻り値
保存されたconst u8*を返し、検索失敗ならヌルです。記述子の素材ポインタがヌルかどうかは検査しないため、検索の成否はasset_get()で区別します。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
各取得関数はその都度asset_getを呼び、記述子をコピーします。バンク・ポインタ・長さをまとめて使うなら、asset_getを1回呼んで各フィールドを参照します。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
使用例
payload_pointer=asset_get_ptr(0);
expect(payload_pointer==asset_shapes,19);
expect(*asset_bank_marker==0xA1,20); // Getting the pointer leaves bank 1 selected.
// Use payload_bank together with payload_pointer for a far copy; do not dereference it here.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
取得したポインタがROM配置後の素材アドレスと一致し、バンク1の確認用データも引き続き読めることを確認します。その後、そのポインタと素材のバンク2をバンク間コピー関数へ渡します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:35
Return the stored address without switching banks. Use the descriptor bank with far-data helpers before dereferencing banked assets; failure returns null.
kitaqgb/lib/asset.c:60
const u8* asset_get_ptr(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.ptr;
}asset_load_raw
素材を検索し、そのROMバンクから書き込み先へmin(desc.len, max_len)バイトをコピーします。種別は判定せず、タイル・マップ・曲などもそのままバイト列として扱います。形式の解析や圧縮データの展開は行いません。
書式
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。dst書き込み先のメモリです。実際のコピー量を収められる領域を確保してください。0バイト転送以外では、ヌルポインタや無効な書き込み先を検出する処理はありません。
max_lenコピーする最大バイト数を0~65535で指定します。素材より小さければ途中までコピーします。0では素材本体をコピーしません。
戻り値
検索失敗なら0、それ以外は1です。途中までのコピー、上限0、空の素材でも1になります。戻り値はコピーしたバイト数ではなく、素材全体が収まった保証にもなりません。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
コピー元全体が目的のROM配置で読め、コピー先全体が書き込み可能な範囲になるようにしてください。バンク窓の端で次のバンクへ進む処理、memmoveのような重なりへの対応、VRAMの安全な書き込み制御はありません。VRAMには描画用の転送APIを使います。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
使用例
loaded=asset_load_raw(0,buffer,7); // Success with truncation: copy seven of 144 bytes.
expect(loaded==1 && buffer[7]==0x55,7);
for(i=0;i<7;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
144バイトの素材に上限7を指定し、先頭7バイトだけをコピーして次のバイトの0x55を残しながら、成功を返すことを確認します。全体コピー、上限0、空の素材、MAP種別のバイト列コピーも検査します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:27
Copy at most max_len bytes using the asset's bank. Success means the ID was valid, even when the payload was truncated to fit the supplied limit.
kitaqgb/lib/asset.c:28
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len)
{
AssetDesc desc;
u16 len;
if (asset_get(asset_id, &desc) == 0) return 0;
len = desc.len;
if (len > max_len) len = max_len;
__farmemcpy(dst, desc.bank, desc.ptr, len);
return 1;
}asset_load_tiles
RAWまたはTILES種別の素材を検索し、__vram_copyでdesc.lenバイトをすべて転送します。呼び出す前に素材本体のROMバンクを選んでください。GB版はdesc.ptrを直接使い、転送時にdesc.bankを選択する処理を持ちません。
書式
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。vram_dst$8000~$9FFFのVRAMバイトアドレスで、タイル番号ではありません。タイル絵柄なら16バイト境界のアドレスと16の倍数の長さを使います。この形式条件をライブラリが検査するわけではありません。
戻り値
IDが無効、または種別がRAW/TILES以外なら0です。受け付けた転送を終えると1で、長さ0の素材も含みます。成功値はタイル形式やVRAM表示の正しさを検証した結果ではありません。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
転送はバイトを書き込むだけで、マップへのタイル配置、配色、VRAMの割り当ては行いません。タイルマップとパレットは別途設定します。サンプルでは描画を止めて転送し、画像の準備ができてから表示を有効にします。
GB版の補助処理は、コンパイラが保持するROMバンク情報から復帰先を決めます。その情報を更新する__bankswitchを使ってください。マッパーへの直接書き込みでは更新されません。HRAMの復帰バンク保存領域は共有なので、入れ子の転送で上書きする使い方は安全ではありません。
使用例
__bankswitch(payload_bank); // GB tile upload expects the payload bank already visible.
loaded=asset_load_tiles(0,0x8010); // Nine tiles = 144 bytes, LCD off.
expect(loaded==1,24);
__bankswitch(1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
タイル転送例は独自の8×8ピクセルの絵柄9枚、合計144バイトを使います。MAP種別と無効IDが拒否されることも確認します。FC版では144バイトを転送することで、128バイトの塊と残り16バイトの両方を検証します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:30
Accept tile/raw descriptors and transfer their bytes to VRAM. This path passes the descriptor pointer directly; callers must arrange the correct visible ROM bank.
kitaqgb/lib/asset.c:41
u8 asset_load_tiles(u8 asset_id, u16 vram_dst)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
if (desc.type != ASSET_TYPE_TILES && desc.type != ASSET_TYPE_RAW) return 0;
__vram_copy(vram_dst, desc.ptr, desc.len);
return 1;
}asset_set_table
使用する素材一覧のバンク番号・アドレス・件数を登録します。有効な一覧は一つです。この関数は三つの値を保持するだけで、一覧のコピー、素材の読み込み、メモリ確保は行いません。
書式
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);引数
bank素材本体ではなく、一覧を格納するROMバンクの番号です。8ビット値で指定します。
tableAssetDescをcount件連続して並べた領域への近ポインタです。登録中は、一覧のバンクでcount * 6バイトを読み出せる状態に保ってください。count件数を0~255で指定します。0ならすべてのIDが無効です。
asset_set_table(0, 0, 0)で空の一覧を登録できます。
戻り値
ありません(void)。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
使用例
asset_set_table(3,asset_descriptors,4); // Four descriptors in bank 3, payloads in bank 2.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この例はバンク3の記述子4件を登録し、空でない素材本体をバンク2から取得します。空の一覧も登録して、アドレス0を読まずに検索が失敗することを確認します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
GB版では、(16,64)の青い三角形と枠が__farmemcpyの結果、(16,96)の緑の組が__far_memcpyの結果です。各組は16×8ピクセルです。CGBはこの配色、DMGは同じ形を白地に黒で表示します。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/asset.h:21
Retain the descriptor table address, bank and count; the table is not copied.
kitaqgb/lib/asset.c:9
void asset_set_table(u8 bank, const AssetDesc* table, u8 count)
{
asset_table_bank = bank;
asset_table = table;
asset_table_count = count;
}audio.h — 音楽と効果音・音源制御
4音源の直接発音、バンク付き音楽・効果音、左右定位、音量フェード、独自波形を扱います。音楽ストリームのチャンネル番号はCH1=0・CH2=1・CH3=2・CH4=3で、直接操作APIと共通です。CH3は波形、CH4はノイズです。
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
Audio_CancelMasterVolumeFade
フェードの進行を止め、現在の音量とレジスター値を保持します。
書式
void Audio_CancelMasterVolumeFade();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}期待する結果
A4が左、右、中央へ移動します。中央では音量7から1へ下がり、次に4まで上がって固定されます。最後に音量0でも小さく鳴り、チャンネル停止で無音になります。定位切り替え後にはフィルターの過渡音が残ることがあります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:125
Cancel further volume steps, retaining the current cached levels and hardware output.
kitaqgb/lib/audio.c:548
void Audio_CancelMasterVolumeFade() {
Audio_FadeTargetLeft = Audio_MasterLeft;
Audio_FadeTargetRight = Audio_MasterRight;
Audio_FadeStepFrames = 0;
Audio_FadeCounter = 0;
Audio_FadeActive = 0;
}Audio_Ch1NoteOn
CH1のスイープ・デューティ・エンベロープ・音程を設定してパルス波を開始します。
書式
void __stackcall Audio_Ch1NoteOn(u8 note, u8 vol_env, u8 duty2);引数
note0~67の音程表番号。詳しい音域とCH3のオクターブ差は制約欄を参照してください。
vol_envNR12/NR22/NR42形式。上位4ビットが初期音量、bit3が増加方向、下位3ビットが変化間隔です。例0xC0は音量12を保持します。
duty20=12.5%、1=25%、3=75%、それ以外は50%のパルス波です。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
noteは0~67の音程表の番号です。0はC2、33はA4です。68以上は67に制限し、60~67は48~55と同じ周波数を使います。CH3は同じ番号のパルス音より1オクターブ低くなります。ノートオン関数は無効化・一時停止フラグを確認せず直接発音します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:151
Trigger CH1 with the cached sweep and the supplied raw envelope. Clamp the note index; duty values 0, 1 and 3 select their encodings, and all others use 2.
kitaqgb/lib/audio.c:132
void __stackcall Audio_Ch1NoteOn(u8 note, u8 vol_env, u8 duty2) {
u16 freq;
u8 duty_reg;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
duty_reg = 128;
if (duty2 == 0) duty_reg = 0;
else if (duty2 == 1) duty_reg = 64;
else if (duty2 == 3) duty_reg = 192;
NR10 = Audio_Ch1Sweep;
NR11 = duty_reg;
NR12 = vol_env;
NR13 = (u8)(freq & 0xFF);
hi = (u8)((u16)freq >> 8);
NR14 = (u8)(hi | 0x80);
}Audio_Ch2NoteOn
CH2のデューティ・エンベロープ・音程を設定してパルス波を開始します。
書式
void __stackcall Audio_Ch2NoteOn(u8 note, u8 vol_env, u8 duty2);引数
note0~67の音程表番号。詳しい音域とCH3のオクターブ差は制約欄を参照してください。
vol_envNR12/NR22/NR42形式。上位4ビットが初期音量、bit3が増加方向、下位3ビットが変化間隔です。例0xC0は音量12を保持します。
duty20=12.5%、1=25%、3=75%、それ以外は50%のパルス波です。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
noteは0~67の音程表の番号です。0はC2、33はA4です。68以上は67に制限し、60~67は48~55と同じ周波数を使います。CH3は同じ番号のパルス音より1オクターブ低くなります。ノートオン関数は無効化・一時停止フラグを確認せず直接発音します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:154
Trigger CH2 with the supplied raw envelope and a clamped note index. Duty values 0, 1 and 3 select their encodings; all other values use duty 2.
kitaqgb/lib/audio.c:666
void __stackcall Audio_Ch2NoteOn(u8 note, u8 vol_env, u8 duty2) {
u16 freq;
u8 duty_reg;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
duty_reg = 128;
if (duty2 == 0) duty_reg = 0;
else if (duty2 == 1) duty_reg = 64;
else if (duty2 == 3) duty_reg = 192;
NR21 = duty_reg;
NR22 = vol_env;
NR23 = (u8)(freq & 0xFF);
hi = (u8)((u16)freq >> 8);
NR24 = (u8)(hi | 0x80);
}Audio_Ch3NoteOn
ロード済みの波形を、指定音程とNR32音量でCH3から再生します。
書式
void __stackcall Audio_Ch3NoteOn(u8 note, u8 level);引数
note0~67の音程表番号。詳しい音域とCH3のオクターブ差は制約欄を参照してください。
levelNR32形式:0x00=無音、0x20=100%、0x40=50%、0x60=25%。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
noteは0~67の音程表の番号です。0はC2、33はA4です。68以上は67に制限し、60~67は48~55と同じ周波数を使います。CH3は同じ番号のパルス音より1オクターブ低くなります。ノートオン関数は無効化・一時停止フラグを確認せず直接発音します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:157
Clamp the note index, program the raw NR32 output level, and trigger CH3. Wave RAM must already contain the desired waveform; this does not load one.
kitaqgb/lib/audio.c:115
void __stackcall Audio_Ch3NoteOn(u8 note, u8 level) {
u16 freq;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
NR30 = 128;
NR31 = 0;
NR32 = level;
NR33 = (u8)(freq & 255);
hi = (u8)((u16)freq >> 8);
NR34 = (u8)(hi | 128);
}Audio_Ch4NoteOn
ノイズ設定を保存し、エンベロープと多項式カウンターを設定してCH4を開始します。
書式
void __stackcall Audio_Ch4NoteOn(u8 param, u8 vol_env);引数
paramNR43の生の値。bit7~4=クロックシフト、bit3=短いLFSR、bit2~0=分周比番号です。
vol_envNR12/NR22/NR42形式。上位4ビットが初期音量、bit3が増加方向、下位3ビットが変化間隔です。例0xC0は音量12を保持します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
長さによる自動停止は有効にしません。エンベロープまたは明示的停止で音を終えます。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:160
Cache the supplied noise parameter, program the raw envelope and polynomial registers, then trigger CH4 with the length-enable bit clear.
kitaqgb/lib/audio.c:688
void __stackcall Audio_Ch4NoteOn(u8 param, u8 vol_env) {
Audio_SetCh4Param(param);
NR41 = 0x20;
NR42 = vol_env;
NR43 = Audio_Ch4Param;
NR44 = 0x80;
}Audio_FadeToMasterVolume
左右それぞれの音量を、指定更新間隔ごとに目標へ1段階ずつ近づけます。
書式
void __stackcall Audio_FadeToMasterVolume(u8 left, u8 right, u8 step_frames);引数
left左出力のマスター音量0~7。7より大きい値は7へ制限します。0も完全な無音ではありません。
right右出力のマスター音量0~7。7より大きい値は7へ制限します。
step_frames各音量を1段階動かすAudio_Update呼び出し間隔。0なら目標を即時適用します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
使用例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}期待する結果
A4が左、右、中央へ移動します。中央では音量7から1へ下がり、次に4まで上がって固定されます。最後に音量0でも小さく鳴り、チャンネル停止で無音になります。定位切り替え後にはフィルターの過渡音が残ることがあります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:123
Move left/right volume toward clamped targets at the requested Audio_Update call interval. An interval of zero applies the levels immediately.
kitaqgb/lib/audio.c:528
void __stackcall Audio_FadeToMasterVolume(u8 left, u8 right, u8 step_frames) {
Audio_FadeTargetLeft = Audio_ClampVolume(left);
Audio_FadeTargetRight = Audio_ClampVolume(right);
if (step_frames == 0) {
Audio_SetMasterVolume(Audio_FadeTargetLeft, Audio_FadeTargetRight);
return;
}
Audio_FadeStepFrames = step_frames;
Audio_FadeCounter = step_frames;
if (Audio_MasterLeft == Audio_FadeTargetLeft && Audio_MasterRight == Audio_FadeTargetRight) {
Audio_FadeActive = 0;
Audio_FadeCounter = 0;
} else {
Audio_FadeActive = 1;
}
}Audio_Init
APUを有効にし、音楽・効果音・一時停止・音量・波形の状態を初期化して全チャンネルを無音にします。
書式
void Audio_Init();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:76
Enable the APU and reset software streams, instruments, mix and fade state. Load the initial triangle wave, then leave every channel silent. Call at startup before concurrent interrupt-driven audio service can access these shared fields.
kitaqgb/lib/audio.c:864
void Audio_Init() {
NR52 = 0x80;
NR51 = 0xFF;
Audio_Pan = 0xFF;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_UpdateNR51();
Audio_SetMasterVolume(AUDIO_VOLUME_MAX, AUDIO_VOLUME_MAX);
Audio_SilenceAll();
Audio_PlayingMusic = 0;
Audio_Paused = 0;
Audio_MusicEnabled = 1;
Audio_SfxEnabled = 1;
Audio_Delay = 0;
Audio_MusicBank = 0;
Audio_MusicPointer = 0;
Audio_MusicLoopPoint = 0;
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectUsesCh2 = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_LastChMask = 0;
Audio_LastCh1Note = 0;
Audio_LastCh2Note = 0;
Audio_LastCh3Note = 0;
Audio_LastCh4Param = 0;
Audio_Ch1Duty = 2;
Audio_Ch2Duty = 2;
Audio_Ch1Env = 0xF1;
Audio_Ch2Env = 0xF2;
Audio_Ch4Env = 0xF1;
Audio_Ch4Param = 0x00;
Audio_Ch3Level = AUDIO_CH3_LEVEL_100;
Audio_Ch3Wave = AUDIO_WAVE_TRIANGLE;
Audio_Ch1Sweep = 0x00;
Audio_SetCustomWaveTriangle();
#ifdef AUDIO_EXCLUDE_WAVE_LOAD_API
Audio_ApplyWaveBytes(Audio_CustomWave);
#else
Audio_LoadWave(Audio_Ch3Wave);
#endif
Audio_Ch3Off();
}Audio_LoadCustomWave
16バイトを独自波形キャッシュとCH3の波形RAMへコピーします。
書式
void __stackcall Audio_LoadCustomWave(const u8 *wave16);引数
wave16読み取り可能な16バイト。各バイトの上位・下位4ビットが順に1サンプルを表します。NULLは無視します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
コピー後は入力配列を再利用できます。関数内部でDACを停止して書き込み、ノートの再トリガーは呼び出し側で行います。
使用例
// Compare a triangle preset with an original packed 32-sample pulse waveform.
#include "gb_sound_example.h"
__prg_rom u8 custom_wave[16]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0,0,0,0,0};
void main() {
sound_begin("WAVE PRESET CUSTOM");
Audio_SetMasterVolume(3,3);
Audio_LoadWave(AUDIO_WAVE_TRIANGLE);
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// Loading copies all sixteen bytes and enables the DAC without triggering a note.
Audio_LoadCustomWave(custom_wave);
sound_result[0]=Audio_CustomWave[0]; sound_result[1]=Audio_CustomWave[8];
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// The cache survives another preset load; select CUSTOM to reuse it.
Audio_LoadWave(AUDIO_WAVE_SAW);
Audio_LoadWave(AUDIO_WAVE_CUSTOM);
sound_result[2]=WAVE0; sound_result[3]=WAVE8;
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
sound_end();
}期待する結果
三角波、独自の矩形波、キャッシュから再読込した同じ矩形波を順に鳴らします。各音は45フレーム、音間は15フレームです。3音とも音程は約220Hzですが、最初だけ音色が異なります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:138
Copy 16 packed sample bytes into the persistent custom cache and wave RAM. A null pointer is ignored; the source is needed only for this call.
kitaqgb/lib/audio.c:614
void __stackcall Audio_LoadCustomWave(const u8 *wave16) {
if (wave16 == 0) return;
Audio_CustomWave[0] = wave16[0]; Audio_CustomWave[1] = wave16[1];
Audio_CustomWave[2] = wave16[2]; Audio_CustomWave[3] = wave16[3];
Audio_CustomWave[4] = wave16[4]; Audio_CustomWave[5] = wave16[5];
Audio_CustomWave[6] = wave16[6]; Audio_CustomWave[7] = wave16[7];
Audio_CustomWave[8] = wave16[8]; Audio_CustomWave[9] = wave16[9];
Audio_CustomWave[10] = wave16[10]; Audio_CustomWave[11] = wave16[11];
Audio_CustomWave[12] = wave16[12]; Audio_CustomWave[13] = wave16[13];
Audio_CustomWave[14] = wave16[14]; Audio_CustomWave[15] = wave16[15];
Audio_ApplyWaveBytes(Audio_CustomWave);
}Audio_LoadWave
プリセットまたはキャッシュした独自波形をCH3の波形RAMへ設定します。
書式
void __stackcall Audio_LoadWave(u8 wave_id);引数
wave_idAUDIO_WAVE_*の0~7。7は保存した独自波形です。8以上は何もしません。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
波形をロードしてもノートは開始せず、Audio_Ch3Waveの選択値も変更しません。三角波プリセットは独自波形キャッシュも三角波へ戻します。必要ならAudio_Ch3NoteOnで発音します。
使用例
// Compare a triangle preset with an original packed 32-sample pulse waveform.
#include "gb_sound_example.h"
__prg_rom u8 custom_wave[16]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0,0,0,0,0};
void main() {
sound_begin("WAVE PRESET CUSTOM");
Audio_SetMasterVolume(3,3);
Audio_LoadWave(AUDIO_WAVE_TRIANGLE);
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// Loading copies all sixteen bytes and enables the DAC without triggering a note.
Audio_LoadCustomWave(custom_wave);
sound_result[0]=Audio_CustomWave[0]; sound_result[1]=Audio_CustomWave[8];
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// The cache survives another preset load; select CUSTOM to reuse it.
Audio_LoadWave(AUDIO_WAVE_SAW);
Audio_LoadWave(AUDIO_WAVE_CUSTOM);
sound_result[2]=WAVE0; sound_result[3]=WAVE8;
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
sound_end();
}期待する結果
三角波、独自の矩形波、キャッシュから再読込した同じ矩形波を順に鳴らします。各音は45フレーム、音間は15フレームです。3音とも音程は約220Hzですが、最初だけ音色が異なります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:135
Install a named preset or the cached custom wave. Unknown IDs are ignored. This does not update Audio_Ch3Wave or trigger a new note.
kitaqgb/lib/audio.c:631
void __stackcall Audio_LoadWave(u8 wave_id) {
if (wave_id == AUDIO_WAVE_TRIANGLE) Audio_LoadWave0();
else if (wave_id == AUDIO_WAVE_SAW) Audio_LoadWave1();
else if (wave_id == AUDIO_WAVE_VOWEL_A) Audio_LoadWave2();
else if (wave_id == AUDIO_WAVE_VOWEL_I) Audio_LoadWave3();
else if (wave_id == AUDIO_WAVE_VOWEL_U) Audio_LoadWave4();
else if (wave_id == AUDIO_WAVE_VOWEL_E) Audio_LoadWave5();
else if (wave_id == AUDIO_WAVE_VOWEL_O) Audio_LoadWave6();
else if (wave_id == AUDIO_WAVE_CUSTOM) Audio_ApplyWaveBytes(Audio_CustomWave);
}Audio_PlayMusic
指定バンクの曲を先頭から再生するよう設定し、楽器・定位・待ち状態をリセットします。
書式
void __stackcall Audio_PlayMusic(u8 bank, u8 *song);引数
bankストリームを格納したROMバンク番号。データの配置と一致させます。
song曲データの先頭。コピーしないため、再生中ずっと読み取れる配列を渡します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
データを読むのは後のAudio_Updateです。NULLは検査しません。既存の効果音は残ります。
使用例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}期待する結果
発音、一時停止の無音、再開、無効化しても残る音、チャンネル停止の無音、再有効化後の次の音、曲停止の順です。一時停止の30更新では待ちカウンターが変わらず、無効化中は30へ進むこともRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:83
Retain the bank and song pointer, reset legacy instruments/cursors, and arm playback from the beginning. Supply a valid, persistent song; no bytes are copied and null is not checked. Existing effect streams remain active.
kitaqgb/lib/audio.c:924
void __stackcall Audio_PlayMusic(u8 bank, u8 *song) {
Audio_PlayingMusic = 0;
Audio_MusicBank = bank;
Audio_MusicPointer = song;
Audio_MusicLoopPoint = song;
Audio_Delay = 0;
Audio_LastChMask = 0;
Audio_Ch1Duty = 2;
Audio_Ch2Duty = 2;
Audio_Ch1Env = 0xF1;
Audio_Ch2Env = 0xF2;
Audio_Ch4Env = 0xF1;
Audio_Ch4Param = 0x00;
Audio_Ch3Level = AUDIO_CH3_LEVEL_100;
Audio_Ch3Wave = AUDIO_WAVE_TRIANGLE;
Audio_Ch1Sweep = 0x00;
Audio_Pan = 0xFF;
Audio_UpdateNR51();
Audio_LoadWave(Audio_Ch3Wave);
Audio_PlayingMusic = 1;
}Audio_PlaySFX
現在のROMバンクを記憶し、基本定位を継承する効果音を要求します。
書式
void __stackcall Audio_PlaySFX(u8 *sfx, u8 priority);引数
sfx効果音バイト列の先頭。パルス用とCH3用の形式は制約欄を参照してください。
priority0~255。値が大きいほど優先し、同値も現在の効果音を置き換えられます。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:93
Capture the currently mapped ROM bank and start an effect with inherited pan. Use the explicit-bank API when the pointer belongs to another bank.
kitaqgb/lib/audio.c:1085
void __stackcall Audio_PlaySFX(u8 *sfx, u8 priority) {
Audio_PlaySFXBanked(__rom_bank, sfx, priority);
}Audio_PlaySFXBanked
指定バンクから効果音の種類を判定し、基本定位を継承して再生を要求します。
書式
void __stackcall Audio_PlaySFXBanked(u8 bank, u8 *sfx, u8 priority);引数
bankストリームを格納したROMバンク番号。データの配置と一致させます。
sfx効果音バイト列の先頭。パルス用とCH3用の形式は制約欄を参照してください。
priority0~255。値が大きいほど優先し、同値も現在の効果音を置き換えられます。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:95
Start a banked effect with the channel's base pan, subject to priority arbitration.
kitaqgb/lib/audio.c:1078
void __stackcall Audio_PlaySFXBanked(u8 bank, u8 *sfx, u8 priority) {
Audio_PlaySFXPannedBanked(bank, sfx, priority, AUDIO_PAN_INHERIT);
}Audio_PlaySFXPanned
現在のROMバンクを記憶し、定位を指定した効果音を要求します。
書式
void __stackcall Audio_PlaySFXPanned(u8 *sfx, u8 priority, u8 pan);引数
sfx効果音バイト列の先頭。パルス用とCH3用の形式は制約欄を参照してください。
priority0~255。値が大きいほど優先し、同値も現在の効果音を置き換えられます。
pan0=中央、1=左、2=右、3=無音。効果音APIでは0xFFなど3を超える値で基本定位を継承します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:98
Capture the currently mapped ROM bank and request an effect with a pan override. Use the explicit-bank API when the pointer belongs to another bank.
kitaqgb/lib/audio.c:1091
void __stackcall Audio_PlaySFXPanned(u8 *sfx, u8 priority, u8 pan) {
Audio_PlaySFXPannedBanked(__rom_bank, sfx, priority, pan);
}Audio_PlaySFXPannedBanked
指定バンク・優先度・定位で、パルスまたはCH3の効果音スロットを取得します。
書式
void __stackcall Audio_PlaySFXPannedBanked(u8 bank, u8 *sfx, u8 priority, u8 pan);引数
bankストリームを格納したROMバンク番号。データの配置と一致させます。
sfx効果音バイト列の先頭。パルス用とCH3用の形式は制約欄を参照してください。
priority0~255。値が大きいほど優先し、同値も現在の効果音を置き換えられます。
pan0=中央、1=左、2=右、3=無音。効果音APIでは0xFFなど3を超える値で基本定位を継承します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:103
Retain a banked effect stream if its priority equals or exceeds the current owner's priority, or that slot is empty. A leading CH3 marker selects the wave slot and is skipped; other streams use the pulse slot. Paused requests are ignored. Keep stream storage valid until completion; acceptance does not trigger a note.
kitaqgb/lib/audio.c:1041
void __stackcall Audio_PlaySFXPannedBanked(u8 bank, u8 *sfx, u8 priority, u8 pan) {
u8 sfx_pan = pan;
u8 use_ch3 = 0;
if (sfx_pan > AUDIO_PAN_MUTE) sfx_pan = AUDIO_PAN_INHERIT;
if (Audio_Paused != 0) return;
if (sfx != 0) {
use_ch3 = Audio_SfxStartsOnCh3Banked(bank, sfx);
if (use_ch3 != 0) {
if (Audio_EffectPointer3 == 0 || priority >= Audio_EffectPriority3) {
Audio_EffectPointer3 = sfx + 1;
Audio_EffectPriority3 = priority;
Audio_EffectBank3 = bank;
Audio_EffectPan3 = sfx_pan;
Audio_UpdateNR51();
}
return;
}
}
if (Audio_EffectPointer == 0 || priority >= Audio_EffectPriority) {
#ifdef AUDIO_VBLANK_SFX_AUTO_CH2
// Choose pulse-channel ownership when accepting the effect, so its later
// updates and deferred recovery agree on which physical channel was borrowed.
Audio_EffectUsesCh2 = (u8)(AudioVBlank_MusicPlaying != 0 &&
AudioVBlank_MusicEnabled != 0);
#else
Audio_EffectUsesCh2 = 0;
#endif
Audio_EffectPointer = sfx;
Audio_EffectPriority = priority;
Audio_EffectBank = bank;
Audio_EffectPan = sfx_pan;
Audio_UpdateNR51();
}
}Audio_SetCh1Duty
次のCH1ノートに使うデューティ番号を保存します。
書式
void __stackcall Audio_SetCh1Duty(u8 duty);引数
dutyパルス波のデューティ比番号。下位2ビットを保存します。0=12.5%、1=25%、2=50%、3=75%。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
この呼び出しだけでは現在の音源レジスターを書き換えません。ノートオンの引数へ保存値を渡すか、対応するストリームを再生して反映します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:141
Cache the low two duty bits for the next CH1 note; do not change the active register.
kitaqgb/lib/audio.c:644
void __stackcall Audio_SetCh1Duty(u8 duty) {
Audio_Ch1Duty = (u8)(duty & 3);
}Audio_SetCh2Duty
次のCH2ノートに使うデューティ番号を保存します。
書式
void __stackcall Audio_SetCh2Duty(u8 duty);引数
dutyパルス波のデューティ比番号。下位2ビットを保存します。0=12.5%、1=25%、2=50%、3=75%。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
この呼び出しだけでは現在の音源レジスターを書き換えません。ノートオンの引数へ保存値を渡すか、対応するストリームを再生して反映します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:143
Cache the low two duty bits for the next CH2 note; do not change the active register.
kitaqgb/lib/audio.c:649
void __stackcall Audio_SetCh2Duty(u8 duty) {
Audio_Ch2Duty = (u8)(duty & 3);
}Audio_SetCh3Level
次のCH3ノートに使うNR32形式の音量を保存します。
書式
void __stackcall Audio_SetCh3Level(u8 level);引数
levelNR32形式:0x00=無音、0x20=100%、0x40=50%、0x60=25%。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
この呼び出しだけでは現在の音源レジスターを書き換えません。ノートオンの引数へ保存値を渡すか、対応するストリームを再生して反映します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:145
Cache the raw NR32 level encoding for the next CH3 note without writing hardware.
kitaqgb/lib/audio.c:654
void __stackcall Audio_SetCh3Level(u8 level) {
Audio_Ch3Level = level;
}Audio_SetCh4Param
次のCH4ノートに使うNR43形式のノイズ設定を保存します。
書式
void __stackcall Audio_SetCh4Param(u8 param);引数
paramNR43の生の値。bit7~4=クロックシフト、bit3=短いLFSR、bit2~0=分周比番号です。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
この呼び出しだけでは現在の音源レジスターを書き換えません。ノートオンの引数へ保存値を渡すか、対応するストリームを再生して反映します。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:147
Cache the raw noise polynomial encoding for the next CH4 note without writing hardware.
kitaqgb/lib/audio.c:659
void __stackcall Audio_SetCh4Param(u8 param) {
Audio_Ch4Param = param;
}Audio_SetMasterVolume
左右のマスター音量を0~7へ制限して即時設定し、進行中のフェードを取り消します。
書式
void __stackcall Audio_SetMasterVolume(u8 left, u8 right);引数
left左出力のマスター音量0~7。7より大きい値は7へ制限します。0も完全な無音ではありません。
right右出力のマスター音量0~7。7より大きい値は7へ制限します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}期待する結果
A4が左、右、中央へ移動します。中央では音量7から1へ下がり、次に4まで上がって固定されます。最後に音量0でも小さく鳴り、チャンネル停止で無音になります。定位切り替え後にはフィルターの過渡音が残ることがあります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:120
Clamp both output levels to 0..7, cancel any fade, and write NR50 immediately.
kitaqgb/lib/audio.c:515
void __stackcall Audio_SetMasterVolume(u8 left, u8 right) {
Audio_MasterLeft = Audio_ClampVolume(left);
Audio_MasterRight = Audio_ClampVolume(right);
Audio_FadeTargetLeft = Audio_MasterLeft;
Audio_FadeTargetRight = Audio_MasterRight;
Audio_FadeStepFrames = 0;
Audio_FadeCounter = 0;
Audio_FadeActive = 0;
Audio_UpdateNR50();
}Audio_SetMusicEnabled
通常の音楽デコーダーが新しいノートを書き込むかを切り替えます。
書式
void __stackcall Audio_SetMusicEnabled(u8 on);引数
on0で無効、0以外で有効にします。音を止めるか、更新だけを止めるかは、この関数の説明を確認してください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
無効化しても曲の読み取り位置は進み、楽器・定位などの制御命令も処理します。鳴っている音は止めません。一時停止にはAudio_SetPausedを使います。
使用例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}期待する結果
発音、一時停止の無音、再開、無効化しても残る音、チャンネル停止の無音、再有効化後の次の音、曲停止の順です。一時停止の30更新では待ちカウンターが変わらず、無効化中は30へ進むこともRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:114
Set the music-processing gate. This does not silence notes already playing or stop the legacy stream cursor from consuming commands.
kitaqgb/lib/audio.c:498
void __stackcall Audio_SetMusicEnabled(u8 on) {
if (on != 0) Audio_MusicEnabled = 1;
else Audio_MusicEnabled = 0;
}Audio_SetPan
1つの物理チャンネルの基本定位を変え、実行中の効果音の定位を再適用します。
書式
void __stackcall Audio_SetPan(u8 ch, u8 pan);引数
chチャンネル番号:0=CH1、1=CH2、2=CH3、3=CH4。直接操作APIと音楽ストリームで同じ番号を使います。
pan0=中央、1=左、2=右、3=無音。効果音APIでは0xFFなど3を超える値で基本定位を継承します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
未知のチャンネル番号はCH4を選びます。このAPIでは0~3以外のpanは無音であり、効果音APIの継承指定とは異なります。
使用例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}期待する結果
A4が左、右、中央へ移動します。中央では音量7から1へ下がり、次に4まで上がって固定されます。最後に音量0でも小さく鳴り、チャンネル停止で無音になります。定位切り替え後にはフィルターの過渡音が残ることがあります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:129
Update the base pan for a physical channel and reapply active effect overrides. Unknown channel IDs select CH4; unsupported pan values mute the selected channel.
kitaqgb/lib/audio.c:558
void __stackcall Audio_SetPan(u8 ch, u8 pan) {
u8 r_bit;
u8 l_bit;
u8 mask;
if (ch == AUDIO_CHANNEL_CH1) {
r_bit = 0x01;
l_bit = 0x10;
} else if (ch == AUDIO_CHANNEL_CH2) {
r_bit = 0x02;
l_bit = 0x20;
} else if (ch == AUDIO_CHANNEL_CH3) {
r_bit = 0x04;
l_bit = 0x40;
} else {
r_bit = 0x08;
l_bit = 0x80;
}
mask = (u8)(l_bit | r_bit);
Audio_Pan = (u8)(Audio_Pan & (u8)~mask);
if (pan == AUDIO_PAN_CENTER) Audio_Pan = (u8)(Audio_Pan | mask);
else if (pan == AUDIO_PAN_LEFT) Audio_Pan = (u8)(Audio_Pan | l_bit);
else if (pan == AUDIO_PAN_RIGHT) Audio_Pan = (u8)(Audio_Pan | r_bit);
Audio_UpdateNR51();
}Audio_SetPanPacked
4音源の基本定位を1バイトから展開し、NR51を1回更新します。
書式
void __stackcall Audio_SetPanPacked(u8 packed);引数
packed各物理チャンネルに2ビット。CH1がbit0~1、CH2が2~3、CH3が4~5、CH4が6~7で、各値はpanと同じです。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}期待する結果
A4が左、右、中央へ移動します。中央では音量7から1へ下がり、次に4まで上がって固定されます。最後に音量0でも小さく鳴り、チャンネル停止で無音になります。定位切り替え後にはフィルターの過渡音が残ることがあります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:132
Two bits per channel (CH1 in bits 0-1): center, left, right, mute. Applies all four positions with one NR51 update for sequencer automation.
kitaqgb/lib/audio.c:589
void __stackcall Audio_SetPanPacked(u8 packed) {
u8 mix = 0;
u8 pan = (u8)(packed & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x11);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x10);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x01);
pan = (u8)((packed >> 2) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x22);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x20);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x02);
pan = (u8)((packed >> 4) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x44);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x40);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x04);
pan = (u8)((packed >> 6) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x88);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x80);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x08);
Audio_Pan = mix;
Audio_UpdateNR51();
}Audio_SetPaused
一時停止時に音を止め、曲・効果音の位置を保持します。再開時は条件を満たすCH1・CH2・CH4を再発音します。
書式
void __stackcall Audio_SetPaused(u8 on);引数
on0で無効、0以外で有効にします。音を止めるか、更新だけを止めるかは、この関数の説明を確認してください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
CH3はここでは再発音しません。効果音は次の更新から再開します。直接ノートオン関数を呼ぶと一時停止中でも鳴ります。
使用例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}期待する結果
発音、一時停止の無音、再開、無効化しても残る音、チャンネル停止の無音、再有効化後の次の音、曲停止の順です。一時停止の30更新では待ちカウンターが変わらず、無効化中は30へ進むこともRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:90
On pause entry, silence hardware while retaining cursors. On resume, retrigger eligible legacy CH1, CH2 and CH4 notes; CH3 is not retriggered here. Updates still advance master fades while paused, and effect playback resumes on a later update.
kitaqgb/lib/audio.c:1010
void __stackcall Audio_SetPaused(u8 on) {
if (on != 0) {
if (Audio_Paused == 0) {
Audio_Paused = 1;
Audio_SilenceAll();
}
} else {
if (Audio_Paused != 0) {
Audio_Paused = 0;
if (Audio_PlayingMusic != 0 && Audio_MusicEnabled != 0) {
if ((Audio_LastChMask & AUDIO_RESUME_CH1) != 0 && Audio_EffectPointer == 0) {
Audio_Ch1NoteOn(Audio_LastCh1Note, Audio_Ch1Env, Audio_Ch1Duty);
}
if ((Audio_LastChMask & AUDIO_RESUME_CH2) != 0) {
Audio_Ch2NoteOn(Audio_LastCh2Note, Audio_Ch2Env, Audio_Ch2Duty);
}
if ((Audio_LastChMask & AUDIO_RESUME_CH4) != 0) {
Audio_Ch4NoteOn(Audio_LastCh4Param, Audio_Ch4Env);
}
}
}
}
}Audio_SetSfxEnabled
効果音のノート書き込みを切り替え、無効化時には効果音スロットを解放します。
書式
void __stackcall Audio_SetSfxEnabled(u8 on);引数
on0で無効、0以外で有効にします。音を止めるか、更新だけを止めるかは、この関数の説明を確認してください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
無効化後に開始APIを呼ぶとストリームは登録されますが、更新時に音は書き込まれません。再生したいときは先に有効化します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:117
Enable effect note writes, or disable them and release both effect streams. Disabling follows Audio_StopSfx recovery rules rather than silencing all channels.
kitaqgb/lib/audio.c:505
void __stackcall Audio_SetSfxEnabled(u8 on) {
if (on != 0) {
Audio_SfxEnabled = 1;
} else {
Audio_SfxEnabled = 0;
Audio_StopSfx();
}
}Audio_StopChannel
指定した物理チャンネルを止め、その音楽ノートを再開対象から外します。
書式
void __stackcall Audio_StopChannel(u8 ch);引数
chチャンネル番号:0=CH1、1=CH2、2=CH3、3=CH4。直接操作APIと音楽ストリームで同じ番号を使います。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
CH1/CH3では対応する効果音スロットも消します。0~3以外は何もしません。VBlank版のラッチ済みノートは別管理です。
使用例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}期待する結果
約0.5秒の無音のあと、CH1のA4、CH2のA4、CH3のA3、ノイズを順に鳴らします。各音は30フレーム、音間は15フレームです。最後のSEQUENCE COMPLETEは処理終了を表し、音の検証には録音を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:110
Stop a physical channel and clear its legacy resume bit. CH1 also clears the pulse effect stream; CH3 clears the wave effect stream. This is the legacy channel API and does not clear a VBlank driver's independently latched notes.
kitaqgb/lib/audio.c:981
void __stackcall Audio_StopChannel(u8 ch) {
if (ch == AUDIO_CHANNEL_CH1) {
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH1);
Audio_UpdateNR51();
Audio_SilenceCh1();
} else if (ch == AUDIO_CHANNEL_CH2) {
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH2);
Audio_SilenceCh2();
} else if (ch == AUDIO_CHANNEL_CH3) {
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH3);
Audio_UpdateNR51();
Audio_Ch3Off();
} else if (ch == AUDIO_CHANNEL_CH4) {
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH4);
Audio_SilenceCh4();
}
}Audio_StopMusic
曲の再生フラグと復帰ノートのマスクを消し、4音源を即座に止めます。
書式
void Audio_StopMusic();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音のポインタは残るため、次のAudio_Updateで効果音が鳴る場合があります。全停止にはAudio_StopSfxも呼びます。
使用例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}期待する結果
発音、一時停止の無音、再開、無効化しても残る音、チャンネル停止の無音、再有効化後の次の音、曲停止の順です。一時停止の30更新では待ちカウンターが変わらず、無効化中は30へ進むこともRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:86
Stop legacy music and clear its resume mask, then silence all physical channels. Effect pointers remain active and may produce sound on a later update.
kitaqgb/lib/audio.c:952
void Audio_StopMusic() {
Audio_PlayingMusic = 0;
Audio_LastChMask = 0;
Audio_SilenceAll();
}Audio_StopSfx
2つの効果音ストリームと定位の上書きを解除し、借りていたチャンネルの解放処理を行います。
書式
void Audio_StopSfx();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
効果音は「note,値」の組を1更新につき1組読み、note=0で終わります。パルス音の値はNR12、CH3の値はNR32です。CH3用には先頭に0xFFを置きます。配列は再生終了まで保持します。優先度は同じスロット内で比較し、同値でも置き換えます。
通常構成では、終了時にCH3を止めます。CH1は音楽が停止中なら止め、音楽再生中は以前の音楽ノートを自動的に再発音しません。VBlank構成のAUDIO_VBLANK_SFX_RESTOREはISRへ復帰を依頼します。
使用例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}期待する結果
バンク2に置いた短い上昇音を、左パルス、右の波形音、中央パルスの順で鳴らします。その後は右パルスを途中停止し、無効化で止める例と再有効化の例を示します。優先度5に対する4の要求は採用されません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:106
Clear both effect streams and their pan overrides. When not paused, release borrowed channels through the configured legacy or VBlank recovery path.
kitaqgb/lib/audio.c:960
void Audio_StopSfx() {
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_UpdateNR51();
if (Audio_Paused != 0) return;
Audio_ReleaseCh1AfterSfx();
Audio_RestoreCh3FromMusic();
}Audio_Update
音量フェードを1刻み進め、一時停止中でなければ効果音、音楽の順で処理します。
書式
void Audio_Update();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_hwregs_gb.c、audio.cの順で読み込み、起動時にAudio_Initを呼びます。Audio_Updateは約59.7Hzで1フレームに1回だけ呼びます。ストリームの読み込み中はROMバンクを一時変更するため、同じ処理を割り込みから重ねて呼ばないでください。
一時停止中もフェードは進みます。WAIT nを読む呼び出しのあとn回を待つため、次のコマンドまでの間隔はn+1更新です。
使用例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}期待する結果
発音、一時停止の無音、再開、無効化しても残る音、チャンネル停止の無音、再有効化後の次の音、曲停止の順です。一時停止の30更新では待ちカウンターが変わらず、無効化中は30へ進むこともRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio.h:79
Advance the master fade first. Unless paused, service both effect streams then the optional legacy music decoder. Call once per frame for frame-based timing.
kitaqgb/lib/audio.c:849
void Audio_Update() {
Audio_UpdateFade();
if (Audio_Paused != 0) return;
Audio_ServiceSfx();
#ifndef AUDIO_EXCLUDE_LEGACY_MUSIC_SERVICE
Audio_ServiceMusic();
#endif
}audio_vblank.h — VBlank IRQによる音楽再生
音楽をVBlank割り込みで更新し、効果音との所有権調整とフレームコールバックを提供します。通常の音楽デコーダーと同時に同じ曲を処理しないでください。
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
AudioVBlank_DisableIrq
IEのVBlankビットだけを消します。ほかの割り込み源や音源は変更しません。
書式
void AudioVBlank_DisableIrq();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:118
Clear only the VBlank bit in IE. Other interrupt sources and current sound remain unchanged.
kitaqgb/lib/audio_vblank_control.inc:129
void AudioVBlank_DisableIrq()
{
AudioVBlank_IE = (u8)(AudioVBlank_IE & 0xFE);
}AudioVBlank_EnableIrq
IEのVBlankビットを立て、EIでCPU割り込みを全体として有効化します。
書式
void AudioVBlank_EnableIrq();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:116
Enable the VBlank source in IE and execute EI, enabling CPU interrupts globally. Install the correct interrupt vector and initialize driver state first.
kitaqgb/lib/audio_vblank_control.inc:122
void AudioVBlank_EnableIrq()
{
AudioVBlank_IE = (u8)(AudioVBlank_IE | 0x01);
__asm { EI }
}AudioVBlank_Init
VBlank音楽のソフトウェア状態・キュー・楽器・コールバックを初期化します。APU初期化と割り込み禁止は行いません。
書式
void AudioVBlank_Init();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:85
Reset software playback state, queue counters, instruments and the frame hook. Call before enabling this ISR; the routine does not mask interrupts or initialize the APU registers and cannot synchronize with an active handler.
kitaqgb/lib/audio_vblank_control.inc:6
void AudioVBlank_Init()
{
AudioVBlank_MusicEnabled = 1;
AudioVBlank_MusicPlaying = 0;
AudioVBlank_MusicDelay = 0;
AudioVBlank_MusicStart = 0;
AudioVBlank_MusicPointer = 0;
AudioVBlank_MusicLoopPoint = 0;
AudioVBlank_Step = 0;
AudioVBlank_Ch2Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch3Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch2LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch1LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch3LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
AudioVBlank_Ch4Param = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Envelope = 0xF1;
AudioVBlank_Ch2Envelope = 0xF2;
AudioVBlank_Ch4Envelope = 0xF1;
AudioVBlank_Ch1Duty = 0x80;
AudioVBlank_Ch2Duty = 0x80;
AudioVBlank_Ch1Sweep = 0x00;
AudioVBlank_Ch3Level = 0x20;
AudioVBlank_ControlCommand = 0;
AudioVBlank_ControlArg0 = 0;
AudioVBlank_ControlArg1 = 0;
AudioVBlank_ControlArg2 = 0;
AudioVBlank_QueueMode = 0;
AudioVBlank_QueueReadIndex = 0;
AudioVBlank_QueueWriteIndex = 0;
AudioVBlank_QueueCount = 0;
AudioVBlank_QueueUnderruns = 0;
AudioVBlank_FrameHook = 0;
}AudioVBlank_Play
選択済みの曲を先頭から再生し、キューモードを解除します。曲がNULLなら停止します。
書式
void AudioVBlank_Play();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:103
Restart the selected direct song at its beginning, leaving queue mode. A null song invokes Stop; this does not enable the music or IRQ gates.
kitaqgb/lib/audio_vblank_control.inc:58
void AudioVBlank_Play()
{
if (AudioVBlank_MusicStart == 0) {
AudioVBlank_Stop();
return;
}
AudioVBlank_MusicPointer = AudioVBlank_MusicStart;
AudioVBlank_MusicLoopPoint = AudioVBlank_MusicStart;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_QueueMode = 0;
AudioVBlank_MusicPlaying = 1;
}AudioVBlank_PlayMusic
直接参照する曲を選択し、先頭からの再生を開始します。
書式
void __stackcall AudioVBlank_PlayMusic(u8 *song);引数
song曲データの先頭。コピーしないため、再生中ずっと読み取れる配列を渡します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:105
Select and restart a directly addressed song. This is not the banked WRAM-queue path.
kitaqgb/lib/audio_vblank_control.inc:73
void __stackcall AudioVBlank_PlayMusic(u8 *song)
{
AudioVBlank_SetMusic(song);
AudioVBlank_Play();
}AudioVBlank_QueuePlay
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
キューモードで1レコード以上あれば再生フラグを有効にします。読み書き位置・待ち時間・音楽の有効設定・一時停止状態を変更しません。初期再生の前にQueueResetと補充を行います。
書式
u8 AudioVBlank_QueuePlay();戻り値
開始を受け付けると1、空またはキューモード以外なら0です。ISRが動作し、音楽が有効で一時停止されていなければ消費が進みます。
使用条件と制限
AudioVBlank_Initの後、QueueReset、QueueRefill、QueuePlayの順に呼びます。通常のaudio_vblank.cをコンパイルしてください。AUDIO_VBLANK_CORE_ONLYでは補充用C関数は含まれません。16レコード×5バイトのキューを使い、前景処理で補充し、VBlank ISRが消費します。
補充を呼ぶ側は前景処理の1か所に限定し、ISRやFrameHookから重ねて呼ばないでください。レコードを公開する短い区間だけIEを退避して割り込みを抑え、終了時にIEとSVBKを戻します。IMEは変更しません。素材のROM/RAMバンクは呼び出し側で選択して保持します。
時間付きレコードはdelay, ch1_note, ch2_note, ch3_note, ch4_noise_paramの5バイトです。ENDも5バイトで用意します。LOOPは補充側で展開し、必要なレコードを再投入してください。直接再生用の可変長コマンド列をそのまま渡す用途ではありません。
使用例
if(AudioVBlank_QueuePlay()==0) sound_result[0]++;
AudioVBlank_EnableIrq();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
独自の8音のフレーズを6回鳴らし、最後に停止します。16枠のキューへ49レコード(48音+END)を順に補充します。検証では全49レコードの受理、アンダーラン0、再生停止、空・満杯・NULL・個数0の処理、CGBのWRAMバンク復元、IME保持、再生中の音声と終了後の無音を確認しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:96
Start/resume queued playback; return 1 on success, 0 for an empty/nonqueue mode. Does not enable the interrupt, music gate or clear pause. Queue before starting.
kitaqgb/lib/audio_vblank_queue.inc:112
u8 AudioVBlank_QueuePlay()
{
u8 saved_ie;
u8 started;
saved_ie = AudioVBlank_IE;
AudioVBlank_IE = 0;
started = 0;
if (AudioVBlank_QueueMode != 0 && AudioVBlank_QueueCount != 0) {
AudioVBlank_MusicPlaying = 1;
started = 1;
}
AudioVBlank_IE = saved_ie;
return started;
}AudioVBlank_QueueRefill
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
完全な5バイトレコードを空きキューへコピーし、コピーが終わったレコードだけISRへ公開します。1回の呼び出しで最大16レコードを受理します。
書式
u8 __stackcall AudioVBlank_QueueRefill(const u8 *records, u8 count);引数
records現在読み出せるバンクにあるレコード列。受理される個数×5バイトを読める状態にしてください。NULLでは0を返します。
countコピーを試みるレコード数です。バイト数ではありません。16を超える指定は16に制限し、0では何もしません。
戻り値
実際に受理したレコード数0~16です。満杯、NULL、個数0、キューモード以外では0です。次の素材位置は戻り値×5バイト進めます。呼び出し中にISRが空きを作る場合があるため、単なる呼び出し前の空き数とは限りません。
使用条件と制限
AudioVBlank_Initの後、QueueReset、QueueRefill、QueuePlayの順に呼びます。通常のaudio_vblank.cをコンパイルしてください。AUDIO_VBLANK_CORE_ONLYでは補充用C関数は含まれません。16レコード×5バイトのキューを使い、前景処理で補充し、VBlank ISRが消費します。
補充を呼ぶ側は前景処理の1か所に限定し、ISRやFrameHookから重ねて呼ばないでください。レコードを公開する短い区間だけIEを退避して割り込みを抑え、終了時にIEとSVBKを戻します。IMEは変更しません。素材のROM/RAMバンクは呼び出し側で選択して保持します。
時間付きレコードはdelay, ch1_note, ch2_note, ch3_note, ch4_noise_paramの5バイトです。ENDも5バイトで用意します。LOOPは補充側で展開し、必要なレコードを再投入してください。直接再生用の可変長コマンド列をそのまま渡す用途ではありません。
使用例
next=AudioVBlank_QueueRefill(queue_song,16);
// next counts records; each record occupies five source bytes.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
独自の8音のフレーズを6回鳴らし、最後に停止します。16枠のキューへ49レコード(48音+END)を順に補充します。検証では全49レコードの受理、アンダーラン0、再生停止、空・満杯・NULL・個数0の処理、CGBのWRAMバンク復元、IME保持、再生中の音声と終了後の無音を確認しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:93
Copy complete records from the currently mapped source; return records accepted. Each call attempts at most 16 records and stops when full. Null/zero returns 0. Supports ROM and RAM sources, restores SVBK and IE, and never changes IME. Single foreground producer only: do not call from an ISR or frame hook. LOOP must be expanded by the producer. END occupies a full five-byte record.
kitaqgb/lib/audio_vblank_queue.inc:93
u8 __stackcall AudioVBlank_QueueRefill(const u8 *records, u8 count)
{
u8 accepted;
u8 i;
if (records == 0 || count == 0 || AudioVBlank_QueueMode == 0) return 0;
if (count > AUDIO_VBLANK_QUEUE_RECORDS) count = AUDIO_VBLANK_QUEUE_RECORDS;
accepted = 0;
while (accepted < count) {
if (AudioVBlank_QueueCount >= AUDIO_VBLANK_QUEUE_RECORDS) break;
for (i = 0; i < 5; i++) AudioVBlank_QueueStaging[i] = records[i];
if (AudioVBlank_QueuePublish() == 0) break;
records += 5;
accepted++;
}
return accepted;
}AudioVBlank_QueueReset
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
再生を止めて音を消し、キューモードを選択して読み書き位置・残数・アンダーラン数を0へ戻します。既存のレコードを破棄して新しい曲を準備する操作です。
書式
void AudioVBlank_QueueReset();戻り値
戻り値はありません。音楽の有効・一時停止の設定は保持します。
使用条件と制限
AudioVBlank_Initの後、QueueReset、QueueRefill、QueuePlayの順に呼びます。通常のaudio_vblank.cをコンパイルしてください。AUDIO_VBLANK_CORE_ONLYでは補充用C関数は含まれません。16レコード×5バイトのキューを使い、前景処理で補充し、VBlank ISRが消費します。
補充を呼ぶ側は前景処理の1か所に限定し、ISRやFrameHookから重ねて呼ばないでください。レコードを公開する短い区間だけIEを退避して割り込みを抑え、終了時にIEとSVBKを戻します。IMEは変更しません。素材のROM/RAMバンクは呼び出し側で選択して保持します。
時間付きレコードはdelay, ch1_note, ch2_note, ch3_note, ch4_noise_paramの5バイトです。ENDも5バイトで用意します。LOOPは補充側で展開し、必要なレコードを再投入してください。直接再生用の可変長コマンド列をそのまま渡す用途ではありません。
使用例
AudioVBlank_QueueReset(); // Silence playback and discard any queued records.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
独自の8音のフレーズを6回鳴らし、最後に停止します。16枠のキューへ49レコード(48音+END)を順に補充します。検証では全49レコードの受理、アンダーラン0、再生停止、空・満杯・NULL・個数0の処理、CGBのWRAMバンク復元、IME保持、再生中の音声と終了後の無音を確認しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:87
Stop music and select an empty WRAM queue; preserve IE, IME and enable/pause gates.
kitaqgb/lib/audio_vblank_queue.inc:76
void AudioVBlank_QueueReset()
{
u8 saved_ie;
saved_ie = AudioVBlank_IE;
AudioVBlank_IE = 0;
AudioVBlank_Stop();
AudioVBlank_QueueMode = 1;
AudioVBlank_QueueReadIndex = 0;
AudioVBlank_QueueWriteIndex = 0;
AudioVBlank_QueueCount = 0;
AudioVBlank_QueueUnderruns = 0;
AudioVBlank_IE = saved_ie;
}AudioVBlank_RequestRestoreCh1
パルス音の保留要求がなければ解放要求2を設定し、既存の復帰要求1を保持します。
書式
void AudioVBlank_RequestRestoreCh1();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
復帰関数を直接呼ぶ場合、効果音がチャンネルを解放済みであることを呼び出し側が保証し、保留値も自分で消します。要求関数だけではレジスターを変更しません。
使用例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}期待する結果
パルスの復帰、無音、波形音の復帰、無音を各30フレームで示します。保留値1は復帰要求を追加しても維持され、保留なしからの要求は発音し直さない値2になります。このサンプルでは割り込みを使わず呼び出し側が所有権を管理します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:129
Request deferred pulse-channel release only when no recovery is pending. Preserve value 1, which records a music event suppressed while an effect owned it.
kitaqgb/lib/audio_vblank.c:908
void AudioVBlank_RequestRestoreCh1()
{
if (AudioVBlank_RestoreCh1Pending == 0)
AudioVBlank_RestoreCh1Pending = 2;
}AudioVBlank_RequestRestoreCh3
波形音の保留要求がなければ解放要求2を設定し、既存の復帰要求1を保持します。
書式
void AudioVBlank_RequestRestoreCh3();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
復帰関数を直接呼ぶ場合、効果音がチャンネルを解放済みであることを呼び出し側が保証し、保留値も自分で消します。要求関数だけではレジスターを変更しません。
使用例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}期待する結果
パルスの復帰、無音、波形音の復帰、無音を各30フレームで示します。保留値1は復帰要求を追加しても維持され、保留なしからの要求は発音し直さない値2になります。このサンプルでは割り込みを使わず呼び出し側が所有権を管理します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:132
Request a deferred wave-channel release only if no recovery is pending. Preserve value 1, which records a new music event suppressed by the effect.
kitaqgb/lib/audio_vblank.c:916
void AudioVBlank_RequestRestoreCh3()
{
if (AudioVBlank_RestoreCh3Pending == 0)
AudioVBlank_RestoreCh3Pending = 2;
}AudioVBlank_RestoreCh1
効果音が借りたCH1またはCH2へラッチ済み音楽ノートを戻すか、解放だけを行います。
書式
void AudioVBlank_RestoreCh1();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
復帰関数を直接呼ぶ場合、効果音がチャンネルを解放済みであることを呼び出し側が保証し、保留値も自分で消します。要求関数だけではレジスターを変更しません。
使用例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}期待する結果
パルスの復帰、無音、波形音の復帰、無音を各30フレームで示します。保留値1は復帰要求を追加しても維持され、保留なしからの要求は発音し直さない値2になります。このサンプルでは割り込みを使わず呼び出し側が所有権を管理します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:122
Recover the borrowed pulse channel (CH1 or CH2) from its latched music event. Pending value 2, disabled music, or a non-note latch silences it instead. The caller must first establish that the effect has released the channel.
kitaqgb/lib/audio_vblank.c:769
void AudioVBlank_RestoreCh1()
{
__asm {
LD_A_MEM Audio_EffectUsesCh2
OR_A
JR_NZ audiovb_restore_ch2
LD_A_MEM AudioVBlank_RestoreCh1Pending
CP_IMM 2
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_Ch1LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch1_off
LD_C_A
LD_A_MEM AudioVBlank_Ch1Sweep
LDH_MEM_A 0x10
LD_A_MEM AudioVBlank_Ch1Duty
LDH_MEM_A 0x11
LD_A_MEM AudioVBlank_Ch1Envelope
LDH_MEM_A 0x12
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x13
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x14
JR audiovb_restore_ch1_done
audiovb_restore_ch1_off:
XOR_A
LDH_MEM_A 0x12
audiovb_restore_ch1_done:
JR audiovb_restore_square_done
audiovb_restore_ch2:
LD_A_MEM AudioVBlank_RestoreCh1Pending
CP_IMM 2
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_Ch2LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch2_off
LD_C_A
LD_A_MEM AudioVBlank_Ch2Duty
LDH_MEM_A 0x16
LD_A_MEM AudioVBlank_Ch2Envelope
LDH_MEM_A 0x17
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x18
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x19
JR audiovb_restore_square_done
audiovb_restore_ch2_off:
XOR_A
LDH_MEM_A 0x17
audiovb_restore_square_done:
}
}AudioVBlank_RestoreCh3
効果音終了後にCH3のラッチ済みノートを戻すか、DACを無効化します。
書式
void AudioVBlank_RestoreCh3();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
復帰関数を直接呼ぶ場合、効果音がチャンネルを解放済みであることを呼び出し側が保証し、保留値も自分で消します。要求関数だけではレジスターを変更しません。
使用例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}期待する結果
パルスの復帰、無音、波形音の復帰、無音を各30フレームで示します。保留値1は復帰要求を追加しても維持され、保留なしからの要求は発音し直さない値2になります。このサンプルでは割り込みを使わず呼び出し側が所有権を管理します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:126
Recover a latched wave-channel music event after effect release, or disable the DAC for a release-only request, disabled music, or a non-note latch. The caller owns effect arbitration and clearing the pending flag.
kitaqgb/lib/audio_vblank.c:860
void AudioVBlank_RestoreCh3()
{
__asm {
LD_A_MEM AudioVBlank_RestoreCh3Pending
CP_IMM 2
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_Ch3LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch3_off
LD_C_A
LD_A_IMM 0x80
LDH_MEM_A 0x1A
XOR_A
LDH_MEM_A 0x1B
LD_A_MEM AudioVBlank_Ch3Level
LDH_MEM_A 0x1C
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x1D
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x1E
JR audiovb_restore_ch3_done
audiovb_restore_ch3_off:
XOR_A
LDH_MEM_A 0x1A
audiovb_restore_ch3_done:
}
}AudioVBlank_SetEnabled
VBlank音楽処理を切り替え、無効化時に復帰要求を消します。鳴っている音とIRQは保持します。
書式
void __stackcall AudioVBlank_SetEnabled(u8 on);引数
on0で無効、0以外で有効にします。音を止めるか、更新だけを止めるかは、この関数の説明を確認してください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:113
Gate music processing and clear pending CH1/CH3 recovery on disable. No audio register is written here: disabling does not itself silence a note already playing or disable the VBlank interrupt.
kitaqgb/lib/audio_vblank_control.inc:109
void __stackcall AudioVBlank_SetEnabled(u8 on)
{
if (on != 0) AudioVBlank_MusicEnabled = 1;
else {
AudioVBlank_MusicEnabled = 0;
// Do not carry a stale SFX-recovery request into a later song.
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
}
}AudioVBlank_SetMusic
直接参照する曲を選び、読み取り位置をリセットします。再生中フラグは変更しません。
書式
void __stackcall AudioVBlank_SetMusic(u8 *song);引数
song曲データの先頭。コピーしないため、再生中ずっと読み取れる配列を渡します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:100
Select a directly addressed song and reset its cursors, leaving the playing flag unchanged. Song storage must remain accessible to the fixed-bank ISR; coordinate this multi-field update with interrupt execution.
kitaqgb/lib/audio_vblank_control.inc:46
void __stackcall AudioVBlank_SetMusic(u8 *song)
{
AudioVBlank_MusicStart = song;
AudioVBlank_MusicPointer = song;
AudioVBlank_MusicLoopPoint = song;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_QueueMode = 0;
}AudioVBlank_Stop
再生・待ち・復帰状態を消し、パルス・ノイズ・波形音を即座に止めます。IRQは無効化しません。
書式
void AudioVBlank_Stop();戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
audio_vblank.cが__kq_vblank_vectorを実装します。他のVBlank処理とベクターを重複させないでください。サンプルはAudio_NoiseEffectActiveを定義し、通常の音楽デコーダーを除外して、Audio_Updateを効果音・フェードの更新にだけ使います。曲のポインタや共有状態を変更する間は割り込みを止めます。
直接参照する曲は固定ROMなどISRが常に読める場所に置きます。レコードはdelay,CH1,CH2,CH3,CH4の5バイトです。ノートの0xFFは保持、0xFDは停止です。delay位置の0xFFは終端、0xFEはループです。これはチャンネルIDではなく、レコード内の配置順です。WRAMキューは別モードで、AudioVBlank_QueueRefillで完全な5バイトレコードを補充します。
使用例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}期待する結果
2音の曲をISRで再生し、音楽更新の無効化では鳴っている音が続くこと、Stopでは止まること、再開後にIRQだけを止めても音が続くことを聞き比べます。コールバックは音楽無効時も動き、IRQ無効時は止まることをRAMで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/audio_vblank.h:109
Stop software playback and silence pulse/noise envelopes and the wave DAC. Song pointers and the IRQ enable remain unchanged; the routine writes the physical channels directly, so coordinate ownership with sound effects.
kitaqgb/lib/audio_vblank_control.inc:82
void AudioVBlank_Stop()
{
AudioVBlank_MusicPlaying = 0;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_Ch2Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch3Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch2LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch1LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch3LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
AudioVBlank_Ch4Param = AUDIO_VBLANK_REST;
__asm {
XOR_A
LDH_MEM_A 0x12
LDH_MEM_A 0x17
LDH_MEM_A 0x1C
LDH_MEM_A 0x1A
LDH_MEM_A 0x21
}
}bank.h — バンク番号付きのコード・データアクセス
ROMバンクは、カートリッジ内の記憶領域をCPUのアドレス空間へ割り当てる単位です。同じCPUアドレスに別のバンクを表示できるため、16ビットのポインターだけではバンク付きデータを特定できません。BankPtrは、1バイトのバンク番号と2バイトのポインターを詰めた3バイトの記述子です。farptr_makeで作成し、farptr_read8、farptr_read16、farptr_readで内容にアクセスします。
GBの例はMBC5カートリッジを使用します。固定バンク0のコードは$0000〜$3FFFから常に参照でき、$4000〜$7FFFにバンク1・2・3を選択します。bank_switchと、その後に実行を続けるコードは固定ROMに配置してください。ライブラリのバンク番号を調べる前に、bank_switchで記録を初期化します。GBのfar_callはコールバックポインターを受け付け、__farcallには関数名を渡します。
bank_get_current
bank_switchが最後に記録したバンク番号を返します。カートリッジのレジスターではなく、ソフトウェア側の記録を読みます。
書式
u8 bank_get_current();戻り値
bank_switchが最後に保存したu8値です。初期化前は0です。
使用条件と制限
ライブラリの記録は0で始まり、カートリッジの起動時のマッピングを調べるものではありません。bank_switchで初期化してください。組み込み関数による直接切り替えやマッパーへの直接書き込みではこの記録を更新しないため、bank_get_currentで実際のハードウェア状態を検証することはできません。
使用例
starting_bank=bank_get_current(); // Last value recorded by bank_switch.
bank_expect(starting_bank==2,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
最初のbank_switch(2)でバンク2を記録して選びます。その後の直接切り替えではバンク1を選び、home1からA1を読みますが、bank_get_currentの記録は2です。戻すとhome2から再びB2を読めます。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:21
Return the last bank recorded by bank_switch; this does not read cartridge hardware.
kitaqgb/lib/bank.c:15
u8 bank_get_current()
{
return kq_bank_current;
}bank_switch
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
ROMバンクを選択し、bankライブラリとコンパイラの両方に番号を記録します。遠方の読み出しや呼び出しは、コンパイラ側の記録を使って元のバンクに戻します。
書式
void bank_switch(u8 bank);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
戻り値
ありません(void)。
使用条件と制限
切り替えを維持する操作では、実行中のコードや戻り先を見えなくしてはいけません。サンプルのように、この呼び出し、使用するbank_switch本体、その後に続くコードを共通・固定バンク0に置いてください。
ライブラリの記録は0で始まり、カートリッジの起動時のマッピングを調べるものではありません。bank_switchで初期化してください。組み込み関数による直接切り替えやマッパーへの直接書き込みではこの記録を更新しないため、bank_get_currentで実際のハードウェア状態を検証することはできません。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
bank_switch(2); // Select the payload window and update library bookkeeping.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
最初のbank_switch(2)でバンク2を記録して選びます。その後の直接切り替えではバンク1を選び、home1からA1を読みますが、bank_get_currentの記録は2です。戻すとhome2から再びB2を読めます。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:19
Update both the library bank record and the compiler shadow used by far reads and calls. Invoke this permanent bank change from fixed-bank code.
kitaqgb/lib/bank.c:8
void bank_switch(u8 bank)
{
kq_bank_current = bank;
__bankswitch(bank);
}far_call
バンク番号とコールバックのポインターを__farcall_ptrへ渡します。引数なしのコールバックを実行し、呼び出し元のバンクへ復帰してから戻るライブラリ関数です。
書式
void far_call(u8 bank, const void* func);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
func引数のないコールバックの、有効な実行アドレスです。関数名または実行時のポインターを指定できます。ヌルや、バンクとアドレスの組み合わせの誤りを実行時には検査しません。
戻り値
ありません(void)。
使用条件と制限
これらの呼び出しは、ユーザー引数を渡したり、コールバックを割り込みに対して再入可能にしたりするものではありません。コールバック自身のデータ・スタック・割り込みの使い方を適切に管理してください。バンク復帰は入れ子呼び出しと反復呼び出しで検証していますが、この例で実機や任意の割り込みタイミングを検証したわけではありません。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
far_call(3,callback); // No arguments; callback reads payload[0] in bank 3.
bank_expect(callback_count==1 && callback_byte==0xD3,15);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムのこの位置では、far_callによってcallback_countが1になります。コールバックはバンク3からD3を読みます。その後にhome2を近ポインターで読んでB2を確認し、バンク2が選ばれた状態で復帰したことを確かめます。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:30
Invoke a no-argument callback in its ROM bank, then restore the caller bank. The pointer must identify executable code; no arguments are forwarded.
kitaqgb/lib/bank.c:40
void far_call(u8 bank, const void* func)
{
__farcall_ptr(bank, func);
}far_data_read
遠方メモリー転送の組み込み関数を使い、指定ROMバンクから呼び出し側のRAM領域へlenバイトをコピーします。終了時に元のバンクへ戻します。
書式
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
addr選んだROMバンク内の読み出せるバイト列を指す近ポインタです。ポインタ自体にはバンク番号を含みません。
dst書き込み先のメモリです。実際のコピー量を収められる領域を確保してください。0バイト転送以外では、ヌルポインタや無効な書き込み先を検出する処理はありません。
lenバイト数を0~65535で指定します。0ならメモリはコピーしませんが、バンクの選択と復帰の処理は実行します。
戻り値
ありません(void)。
使用条件と制限
コピー元全体が目的のROM配置で読め、コピー先全体が書き込み可能な範囲になるようにしてください。バンク窓の端で次のバンクへ進む処理、memmoveのような重なりへの対応、VRAMの安全な書き込み制御はありません。VRAMには描画用の転送APIを使います。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
far_data_read(3,payload,buffer,4); // Copy into four writable RAM bytes.
bank_expect(buffer[0]==0xD3 && buffer[1]==0x7A && buffer[2]==0x5C && buffer[3]==0x96,7);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片はfar_data_readで4バイトをコピーし、バッファーをD3 7A 5C 96と照合します。呼び出し側の転送元記述子やポインターは維持されます。完成プログラムでは、長さ0のコピーとバンク復帰も別に確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:27
Copy len bytes from banked ROM into caller-owned writable storage.
kitaqgb/lib/bank.c:33
void far_data_read(u8 bank, const void* addr, void* dst, u16 len)
{
__farmemcpy(dst, bank, addr, len);
}far_data_read16
__farpeek16で連続する2バイトを読み、リトルエンディアンのワードとして返します。先頭のバイトが下位8ビットになります。
書式
u16 far_data_read16(u8 bank, const void* addr);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
addr選んだROMバンク内の読み出せるバイト列を指す近ポインタです。ポインタ自体にはバンク番号を含みません。
戻り値
読み出したリトルエンディアンのワードをu16(0〜65535)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
word_value=far_data_read16(3,payload+1); // Little-endian 7A 5C -> 0x5C7A.
bank_expect(word_value==0x5C7A,6);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片ではfar_data_read16でバンク3のpayload+1を読み、5C7Aを確認します。完成プログラムでは、home2の近ポインター読み出しでもバンク2への復帰を確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:25
Read a 16-bit value from the supplied bank and address through the far-read intrinsic.
kitaqgb/lib/bank.c:27
u16 far_data_read16(u8 bank, const void* addr)
{
return __farpeek16(bank, addr);
}far_data_read8
指定したROMバンクとアドレスから__farpeek8で1バイト読み、元のバンクに戻して値を返します。
書式
u8 far_data_read8(u8 bank, const void* addr);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
addr選んだROMバンク内の読み出せるバイト列を指す近ポインタです。ポインタ自体にはバンク番号を含みません。
戻り値
読み出したバイトをu8(0〜255)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
byte_value=far_data_read8(3,payload); // One byte: 0xD3.
bank_expect(byte_value==0xD3,5);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片ではfar_data_read8でバンク3のpayloadを読み、D3を確認します。完成プログラムでは、home2の近ポインター読み出しでもバンク2への復帰を確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:23
Read one byte through the compiler intrinsic that accepts an explicit ROM bank.
kitaqgb/lib/bank.c:21
u8 far_data_read8(u8 bank, const void* addr)
{
return __farpeek8(bank, addr);
}farptr_make
バンク番号1バイトとCPUから見えるポインターを、呼び出し側のBankPtrに格納します。記述子を作るだけで、バンクの切り替えや参照先の読み出しは行いません。
書式
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);引数
out3バイトのBankPtrを1件書き込める領域へのポインターです。outがヌルなら何も書かずに戻ります。
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
ptr記述子に格納する、CPUから見える16ビットのアドレスです。そのまま保存し、指定バンクにそのアドレスが存在するかは検査しません。
戻り値
ありません(void)。
使用例
farptr_make(&pointer,3,payload+1); // Store a bank and address; do not read ROM.
bank_expect(pointer.bank==3 && pointer.ptr==payload+1,8);
farptr_make(0,1,home1); // A null destination is ignored.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
記述子にバンク3とpayload+1を設定します。その読み出し関数で7A、5C7A、3バイトの7A 5C 96を取得します。出力先がヌルの呼び出しでは状態を変えません。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:32
Store a bank/address pair without accessing ROM; a null output pointer is ignored.
kitaqgb/lib/bank.c:46
void farptr_make(BankPtr* out, u8 bank, const u8* ptr)
{
if (out == 0) return;
out->bank = bank;
out->ptr = ptr;
}farptr_read
BankPtrの参照先から書き込み可能なRAMへlenバイトをコピーします。記述子は値渡しなので、呼び出し側のポインターは転送元の先頭を指したままです。
書式
void farptr_read(BankPtr ptr, void* dst, u16 len);引数
ptrbankとptrを持つBankPtrの値です。アドレスではなく記述子そのものを渡します。呼び出し側の記述子は変更しません。
dst書き込み先のメモリです。実際のコピー量を収められる領域を確保してください。0バイト転送以外では、ヌルポインタや無効な書き込み先を検出する処理はありません。
lenバイト数を0~65535で指定します。0ならメモリはコピーしませんが、バンクの選択と復帰の処理は実行します。
戻り値
ありません(void)。
使用条件と制限
コピー元全体が目的のROM配置で読め、コピー先全体が書き込み可能な範囲になるようにしてください。バンク窓の端で次のバンクへ進む処理、memmoveのような重なりへの対応、VRAMの安全な書き込み制御はありません。VRAMには描画用の転送APIを使います。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
farptr_read(pointer,buffer,3);
bank_expect(buffer[0]==0x7A && buffer[1]==0x5C && buffer[2]==0x96,11);
bank_expect(pointer.bank==3 && pointer.ptr==payload+1,23);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片はfarptr_readで3バイトをコピーし、バッファーを7A 5C 96と照合します。呼び出し側の転送元記述子やポインターは維持されます。完成プログラムでは、長さ0のコピーとバンク復帰も別に確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:38
Copy len bytes from a bank-qualified pointer; dst must have room for the full copy.
kitaqgb/lib/bank.c:66
void farptr_read(BankPtr ptr, void* dst, u16 len)
{
__farmemcpy(dst, ptr.bank, ptr.ptr, len);
}farptr_read16
BankPtrが保持するバンクとアドレスから、リトルエンディアンのワードを読みます。記述子は値渡しで、ポインターは進めません。
書式
u16 farptr_read16(BankPtr ptr);引数
ptrbankとptrを持つBankPtrの値です。アドレスではなく記述子そのものを渡します。呼び出し側の記述子は変更しません。
戻り値
読み出したリトルエンディアンのワードをu16(0〜65535)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
bank_expect(farptr_read16(pointer)==0x5C7A,10);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片ではfarptr_read16でバンク3のpointer.ptrを読み、5C7Aを確認します。完成プログラムでは、home2の近ポインター読み出しでもバンク2への復帰を確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:36
Read a 16-bit value using both components of the bank-qualified pointer.
kitaqgb/lib/bank.c:60
u16 farptr_read16(BankPtr ptr)
{
return __farpeek16(ptr.bank, ptr.ptr);
}farptr_read8
値渡しされたBankPtrの両フィールドを使って1バイト読みます。通常の近ポインターには含まれないバンク番号を記述子から取得します。
書式
u8 farptr_read8(BankPtr ptr);引数
ptrbankとptrを持つBankPtrの値です。アドレスではなく記述子そのものを渡します。呼び出し側の記述子は変更しません。
戻り値
読み出したバイトをu8(0〜255)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
GBのこれらのバンクAPIは8ビットの番号を扱い、MBC5の9ビット目は設定しません。バンク0の扱いなど、カートリッジ固有の規則も適用されます。例ではMBC5のバンク1〜3を明示的に使います。記録に反映されないマッパーへの直接書き込みと混在させないでください。
使用例
bank_expect(farptr_read8(pointer)==0x7A,9);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
この断片ではfarptr_read8でバンク3のpointer.ptrを読み、7Aを確認します。完成プログラムでは、home2の近ポインター読み出しでもバンク2への復帰を確認します。
完成プログラムでは、バンク番号と近アドレスによるデータの指定、バンクをまたぐコールバック、ライブラリの記録と実際のマッピングの区別を学べます。数値はすべて16進数です。BANK INとBANK OUTは02、BYTE HEXはD3、WORD HEXは5C7A、FAILED CHECKSとFIRST FAILUREはどちらも00が期待値です。CGBとFCは青字、DMGは黒字で表示します。
CALL COUNTは03です。far_call、__farcall、__farcall_ptrがそれぞれ1回コールバックを実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/bank.h:34
Read one byte using both components of the bank-qualified pointer.
kitaqgb/lib/bank.c:54
u8 farptr_read8(BankPtr ptr)
{
return __farpeek8(ptr.bank, ptr.ptr);
}camera.h — 世界座標と表示範囲の変換
カメラの位置・目標・可動範囲をQ8.8で管理し、ワールドと画面の座標を変換します。独立したCamera8_8と、1つの共有カメラ用の小文字APIを使い分けられます。
Camera_Add
現在位置にQ8.8の移動量を加え、有効な境界内へ制限します。
書式
void Camera_Add(Camera8_8* cam, s16 dfx, s16 dfy);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。dfxQ8.8の符号付き移動量。256で1ピクセルです。
dfyQ8.8の符号付き移動量。256で1ピクセルです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_Add(&cam,256,512);result[13]=cam.fx;result[14]=cam.fy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:34
Add Q8.8 deltas and clamp afterward. Keep the intermediate sums within s16; clamping cannot repair arithmetic that already overflowed.
kitaqgb/lib/camera.c:79
void Camera_Add(Camera8_8* cam, s16 dfx, s16 dfy)
{
cam->fx += dfx;
cam->fy += dfy;
Camera_ApplyBounds(cam);
}camera_apply
共有カメラの現在位置を背景スクロールへ即時反映します。
書式
void camera_apply();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
VBlank待ちは行いません。描画中の途中変更を避ける場合は呼び出し側でタイミングを合わせます。負の小数は切り下げとなり、−1/256は−1ピクセル、レジスター値では255になります。
使用例
camera_apply();result[31]=M_SCX;result[32]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:64
Write the shared camera's current integer position to background scroll registers.
kitaqgb/lib/camera.c:179
void camera_apply()
{
Camera_ApplyBg(&camera_default);
}Camera_ApplyBg
カメラ座標を整数ピクセルへ切り下げ、下位8ビットを背景スクロールへ即時反映します。
書式
void Camera_ApplyBg(const Camera8_8* cam);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
VBlank待ちは行いません。描画中の途中変更を避ける場合は呼び出し側でタイミングを合わせます。負の小数は切り下げとなり、−1/256は−1ピクセル、レジスター値では255になります。
使用例
Camera_ApplyBg(&cam);result[21]=M_SCX;result[22]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:40
Discard fractional bits and write the low eight integer bits to background scroll registers.
kitaqgb/lib/camera.c:109
void Camera_ApplyBg(const Camera8_8* cam)
{
__scroll_bg_set((u8)(cam->fx >> 8), (u8)(cam->fy >> 8));
}Camera_ApplyBgBuffered
カメラ座標を整数ピクセルへ切り下げ、背景スクロールの保留値として保存します。
書式
void Camera_ApplyBgBuffered(const Camera8_8* cam);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
ハードウェア値と現在値は Scroll_Flush または __scroll_flush まで変わりません。VBlank待ちは行いません。
使用例
Camera_Set(&cam,2048,4096);Camera_ApplyBgBuffered(&cam);result[23]=M_SCX;Scroll_Flush();result[24]=M_SCX;result[25]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:42
Convert the Q8.8 position to byte scroll values and stage them for a later flush.
kitaqgb/lib/camera.c:115
void Camera_ApplyBgBuffered(const Camera8_8* cam)
{
__scroll_bg_set_buffered((u8)(cam->fx >> 8), (u8)(cam->fy >> 8));
}camera_clamp
共有カメラに、ワールド寸法と画面寸法から計算した可動範囲を適用します。
書式
void camera_clamp(u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);引数
world_w_pxワールドの整数ピクセル寸法。画面との差は0~127に収めてください。
world_h_pxワールドの整数ピクセル寸法。画面との差は0~127に収めてください。
screen_w_px表示領域の整数ピクセル寸法。ワールド以下でない場合、対応する可動範囲は0になります。
screen_h_px表示領域の整数ピクセル寸法。ワールド以下でない場合、対応する可動範囲は0になります。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
画面より小さいワールドでは範囲は0です。正の差が128以上だとQ8.8変換が符号付き範囲を超えます。u16 の引数でも大きなワールドのカメラ移動を保証するものではありません。 共有カメラを使うため、別オブジェクトのカメラを同時に保持するには大文字のAPIを使います。整数からQ8.8へ変換する座標は−128~127に収めてください。
使用例
camera_clamp(176,160,160,144);result[29]=camera_world_to_screen_x(100);result[30]=camera_world_to_screen_y(90);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:62
Derive and apply world/screen bounds to the shared convenience camera.
kitaqgb/lib/camera.c:173
void camera_clamp(u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px)
{
Camera_ClampToSize(&camera_default, world_w_px, world_h_px, screen_w_px, screen_h_px);
}Camera_ClampToSize
ワールド寸法から画面寸法を引いて可動範囲を作り、0以上のカメラ境界として適用します。
書式
void Camera_ClampToSize(Camera8_8* cam, u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。world_w_pxワールドの整数ピクセル寸法。画面との差は0~127に収めてください。
world_h_pxワールドの整数ピクセル寸法。画面との差は0~127に収めてください。
screen_w_px表示領域の整数ピクセル寸法。ワールド以下でない場合、対応する可動範囲は0になります。
screen_h_px表示領域の整数ピクセル寸法。ワールド以下でない場合、対応する可動範囲は0になります。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
画面より小さいワールドでは範囲は0です。正の差が128以上だとQ8.8変換が符号付き範囲を超えます。u16 の引数でも大きなワールドのカメラ移動を保証するものではありません。
使用例
Camera_ClampToSize(&cam,176,160,160,144);result[15]=cam.max_fx;result[16]=cam.max_fy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:46
Derive nonnegative camera extents from world minus screen size, then convert them to Q8.8 bounds. Extents must fit signed Q8.8; the u16 input type does not imply support for arbitrary large worlds.
kitaqgb/lib/camera.c:123
void Camera_ClampToSize(Camera8_8* cam, u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px)
{
s16 max_x = 0;
s16 max_y = 0;
if (world_w_px > screen_w_px) max_x = (s16)((u16)(world_w_px - screen_w_px) << 8);
if (world_h_px > screen_h_px) max_y = (s16)((u16)(world_h_px - screen_h_px) << 8);
Camera_SetBounds(cam, (s16)0, (s16)0, max_x, max_y);
}Camera_ClearBounds
範囲制限を無効にします。位置・目標・保存された境界は変更しません。
書式
void Camera_ClearBounds(Camera8_8* cam);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_ClearBounds(&cam);result[12]=cam.use_bounds;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:31
Disable clamping while retaining the stored bounds and current position.
kitaqgb/lib/camera.c:72
void Camera_ClearBounds(Camera8_8* cam)
{
cam->use_bounds = 0;
}Camera_ClearTarget
追従を無効にします。現在位置と保存した目標座標は保持します。
書式
void Camera_ClearTarget(Camera8_8* cam);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_ClearTarget(&cam);result[8]=cam.use_target;result[9]=cam.target_fx;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:27
Disable target following without moving the camera or erasing the stored target.
kitaqgb/lib/camera.c:55
void Camera_ClearTarget(Camera8_8* cam)
{
cam->use_target = 0;
}camera_follow_xy
ワールド上の指定点が画面上の指定アンカーに来るよう、共有カメラを即座に移動します。
書式
void camera_follow_xy(s16 x, s16 y, u8 screen_cx, u8 screen_cy);引数
xワールドX座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。yワールドY座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。screen_cx画面内のアンカー座標。単位は整数ピクセル、値は0~255です。
screen_cy画面内のアンカー座標。単位は整数ピクセル、値は0~255です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。 共有カメラを使うため、別オブジェクトのカメラを同時に保持するには大文字のAPIを使います。整数からQ8.8へ変換する座標は−128~127に収めてください。
使用例
camera_follow_xy(100,90,80,72);result[27]=camera_world_to_screen_x(100);result[28]=camera_world_to_screen_y(90);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:60
Position the shared camera so the world point appears at the requested screen anchor. This is an immediate move, not the smoothed target-following path.
kitaqgb/lib/camera.c:165
void camera_follow_xy(s16 x, s16 y, u8 screen_cx, u8 screen_cy)
{
s16 cx = (s16)(x - screen_cx);
s16 cy = (s16)(y - screen_cy);
Camera_Set(&camera_default, (s16)(cx << 8), (s16)(cy << 8));
}Camera_Init
位置・目標・境界をすべて0にし、追従と範囲制限を無効にします。
書式
void Camera_Init(Camera8_8* cam);引数
cam初期化先の書き込み可能な
Camera8_8。NULLは渡せません。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_Init(&cam);result[0]=cam.fx;result[1]=cam.use_target;result[2]=cam.use_bounds;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:21
Zero all position/target/bound fields and disable targeting and bounds.
kitaqgb/lib/camera.c:24
void Camera_Init(Camera8_8* cam)
{
cam->fx = 0;
cam->fy = 0;
cam->target_fx = 0;
cam->target_fy = 0;
cam->min_fx = 0;
cam->min_fy = 0;
cam->max_fx = 0;
cam->max_fy = 0;
cam->use_target = 0;
cam->use_bounds = 0;
}Camera_ScreenToWorldX
画面の整数X座標にカメラの整数X成分を加え、ワールドX座標へ戻します。
書式
s16 Camera_ScreenToWorldX(const Camera8_8* cam, s16 screen_x);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。screen_x画面X座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
result[19]=Camera_ScreenToWorldX(&cam,27);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:52
Convert integer-pixel screen X back to world X using the camera's integer component.
kitaqgb/lib/camera.c:146
s16 Camera_ScreenToWorldX(const Camera8_8* cam, s16 screen_x)
{
return (s16)(screen_x + (cam->fx >> 8));
}Camera_ScreenToWorldY
画面の整数Y座標にカメラの整数Y成分を加え、ワールドY座標へ戻します。
書式
s16 Camera_ScreenToWorldY(const Camera8_8* cam, s16 screen_y);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。screen_y画面Y座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
result[20]=Camera_ScreenToWorldY(&cam,24);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:54
Convert integer-pixel screen Y back to world Y using the camera's integer component.
kitaqgb/lib/camera.c:152
s16 Camera_ScreenToWorldY(const Camera8_8* cam, s16 screen_y)
{
return (s16)(screen_y + (cam->fy >> 8));
}Camera_Set
現在のカメラ座標を置き換え、有効な境界があれば直ちにその範囲へ制限します。
書式
void Camera_Set(Camera8_8* cam, s16 fx, s16 fy);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。fxX座標。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
fyY座標。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_Set(&cam,2560,5120);result[3]=cam.fx;result[4]=cam.fy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:23
Replace the current Q8.8 position and immediately apply enabled bounds.
kitaqgb/lib/camera.c:39
void Camera_Set(Camera8_8* cam, s16 fx, s16 fy)
{
cam->fx = fx;
cam->fy = fy;
Camera_ApplyBounds(cam);
}camera_set
共有カメラを整数ピクセル指定で移動します。有効な共有境界があれば制限します。
書式
void camera_set(s16 x, s16 y);引数
xワールドX座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。yワールドY座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。 共有カメラを使うため、別オブジェクトのカメラを同時に保持するには大文字のAPIを使います。整数からQ8.8へ変換する座標は−128~127に収めてください。
使用例
camera_set(5,6);result[26]=camera_world_to_screen_x(20);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:57
Set the shared camera from integer pixels, converting to the representable Q8.8 range.
kitaqgb/lib/camera.c:158
void camera_set(s16 x, s16 y)
{
Camera_Set(&camera_default, (s16)(x << 8), (s16)(y << 8));
}Camera_SetBounds
最小・最大座標を保存して範囲制限を有効にし、現在位置も直ちに制限します。
書式
void Camera_SetBounds(Camera8_8* cam, s16 min_fx, s16 min_fy, s16 max_fx, s16 max_fy);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。min_fxX座標の下限。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
min_fyY座標の下限。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
max_fxX座標の上限。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
max_fyY座標の上限。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
各軸で最小値≤最大値を守ってください。順序の補正はありません。目標座標は制限されません。
使用例
Camera_SetBounds(&cam,0,0,3072,3584);result[10]=cam.fx;result[11]=cam.fy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:29
Install ordered Q8.8 bounds, enable them and clamp the current position immediately.
kitaqgb/lib/camera.c:61
void Camera_SetBounds(Camera8_8* cam, s16 min_fx, s16 min_fy, s16 max_fx, s16 max_fy)
{
cam->min_fx = min_fx;
cam->min_fy = min_fy;
cam->max_fx = max_fx;
cam->max_fy = max_fy;
cam->use_bounds = 1;
Camera_ApplyBounds(cam);
}Camera_SetTarget
追従先の座標を保存し、追従を有効にします。現在位置は動かしません。
書式
void Camera_SetTarget(Camera8_8* cam, s16 fx, s16 fy);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。fxX座標。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
fyY座標。符号付きQ8.8の座標。256が1ピクセルで、範囲は−128から127+255/256ピクセルです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
Camera_SetTarget(&cam,4608,3072);result[5]=cam.use_target;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:25
Store a Q8.8 target and enable subsequent step-toward-target updates.
kitaqgb/lib/camera.c:47
void Camera_SetTarget(Camera8_8* cam, s16 fx, s16 fy)
{
cam->target_fx = fx;
cam->target_fy = fy;
cam->use_target = 1;
}Camera_StepTowardTarget
目標との差を右シフトした量だけ近づけます。追従が無効なら何もしません。
書式
void Camera_StepTowardTarget(Camera8_8* cam, u8 shift);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。shift差を割る2の指数。0なら差全体、1なら半分です。8以上は7として扱います。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
正の差は切り捨てで0になり得ます。負の差は算術右シフトなので−1になる場合があります。目標との一致は境界制限の前に判定します。そのため範囲外の目標では追従が続く場合も、制限後の位置が目標と違っていても追従が解除される場合もあります。
使用例
Camera_StepTowardTarget(&cam,1);result[6]=cam.fx;result[7]=cam.fy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:38
Move by the signed target delta shifted by 0..7 bits, then apply bounds. Quantization can make a small delta produce no movement; only exact equality clears targeting, and a target outside the bounds may remain active indefinitely.
kitaqgb/lib/camera.c:89
void Camera_StepTowardTarget(Camera8_8* cam, u8 shift)
{
s16 dx;
s16 dy;
if (cam->use_target == 0) return;
if (shift > 7) shift = 7;
// Keep target-minus-position differences within s16 as well as the individual positions.
dx = cam->target_fx - cam->fx;
dy = cam->target_fy - cam->fy;
if (dx != 0) cam->fx += (dx >> shift);
if (dy != 0) cam->fy += (dy >> shift);
if (cam->fx == cam->target_fx && cam->fy == cam->target_fy) cam->use_target = 0;
Camera_ApplyBounds(cam);
}camera_world_to_screen_x
共有カメラの整数X成分を引いて、ワールドXを画面Xへ変換します。
書式
s16 camera_world_to_screen_x(s16 world_x);引数
world_xワールドX座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。 共有カメラを使うため、別オブジェクトのカメラを同時に保持するには大文字のAPIを使います。整数からQ8.8へ変換する座標は−128~127に収めてください。
使用例
result[33]=camera_world_to_screen_x(20);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:66
Convert integer-pixel world X using the shared convenience camera.
kitaqgb/lib/camera.c:185
s16 camera_world_to_screen_x(s16 world_x)
{
return Camera_WorldToScreenX(&camera_default, world_x);
}camera_world_to_screen_y
共有カメラの整数Y成分を引いて、ワールドYを画面Yへ変換します。
書式
s16 camera_world_to_screen_y(s16 world_y);引数
world_yワールドY座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。 共有カメラを使うため、別オブジェクトのカメラを同時に保持するには大文字のAPIを使います。整数からQ8.8へ変換する座標は−128~127に収めてください。
使用例
result[34]=camera_world_to_screen_y(20);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:68
Convert integer-pixel world Y using the shared convenience camera.
kitaqgb/lib/camera.c:191
s16 camera_world_to_screen_y(s16 world_y)
{
return Camera_WorldToScreenY(&camera_default, world_y);
}Camera_WorldToScreenX
ワールドの整数X座標からカメラの整数X成分を引き、画面X座標を求めます。
書式
s16 Camera_WorldToScreenX(const Camera8_8* cam, s16 world_x);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。world_xワールドX座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
result[17]=Camera_WorldToScreenX(&cam,40);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:48
Convert an integer-pixel world X by subtracting the camera's integer X component.
kitaqgb/lib/camera.c:134
s16 Camera_WorldToScreenX(const Camera8_8* cam, s16 world_x)
{
return (s16)(world_x - (cam->fx >> 8));
}Camera_WorldToScreenY
ワールドの整数Y座標からカメラの整数Y成分を引き、画面Y座標を求めます。
書式
s16 Camera_WorldToScreenY(const Camera8_8* cam, s16 world_y);引数
cam初期化済みの
Camera8_8への有効なポインター。呼び出し元が保存領域を保持します。world_yワールドY座標、整数ピクセル単位。結果と中間差分が
s16に収まる必要があります。
戻り値
変換後の整数ピクセル座標を s16 で返します。画面内かどうかの判定やクリップは行いません。
使用条件と制限
座標の小数部は8ビットです。カメラ座標・対象座標だけでなく、加算や差分の中間値も s16 に収まるようにしてください。画面表示は別途 Camera_ApplyBg またはバッファー反映で更新します。
使用例
result[18]=Camera_WorldToScreenY(&cam,40);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Q8.8の追従・境界制限・座標変換を学ぶ例です。追従後のX/Yは3584/4096、境界適用後は3072/3584。保留中の背景Xは13、flush後は8です。別の画像では、カメラを(8,16)へ動かし、ワールド(32,32)の32×24矩形が画面(24,16)へ移ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/camera.h:50
Convert an integer-pixel world Y by subtracting the camera's integer Y component.
kitaqgb/lib/camera.c:140
s16 Camera_WorldToScreenY(const Camera8_8* cam, s16 world_y)
{
return (s16)(world_y - (cam->fy >> 8));
}cgb_palette.h — CGBのBG/OBJパレット
cgb_palette.hとcgb_palette.cは、RGB15値の生成とCGBパレットの即時更新を提供します。背景・ウィンドウにはBG系、スプライトにはOBJ系の関数を使います。1色指定・RGB成分指定は1色、範囲指定は連続する色ワード、全パレット指定は4ワードを書き込みます。raw範囲指定は色の通し番号、レジスタ組み込み関数はバイト位置を使います。パレットの割り当てはマップ属性やOAMで別途指定します。完全版サンプルのように実装をプログラムへ1回だけ組み込んでください。
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
cgb_bg_color
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
パック済みのRGB15色を背景・ウィンドウ用パレットへ直ちに1色書き込みます。パレット番号と色番号で全32色のうち1色を指定します。タイルデータや、タイルが使うパレットの指定は変更しません。
書式
void cgb_bg_color(u8 palette_index, u8 color_index, u16 rgb15);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。rgb15パック済みの15ビットRGB色(通常0〜32767)。渡した
u16の下位・上位バイトを順に書き込み、ライブラリ側ではビット15を明示的に消しません。正しい値の生成にはCGB_RGB15またはcgb_rgb15を使えます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_bg_color(1,3,31); // Red background color 3 of palette 1.
expect_color(0,7,31);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:22
Set one background color through the indexed palette wrapper.
kitaqgb/lib/cgb_palette.c:110
void cgb_bg_color(u8 palette_index, u8 color_index, u16 rgb15)
{
cgb__write_color_bg_raw(cgb__palette_color_slot(palette_index, color_index), rgb15);
}cgb_bg_colors
指定したパレット番号・開始色番号から、背景・ウィンドウ用パレットへRGB15の配列を順に書き込みます。4色単位のパレット境界を越えて書き込めますが、全32色の末尾で止まります。
書式
void cgb_bg_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。start_color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。color_count色ワード数です。バイト数ではありません。
u8(0〜255)で渡し、0なら何もしません。実際の書き込み数は32 - 正規化後の開始スロットまでに制限され、配列もその数だけ読みます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_bg_colors(3,2,tail_green,2); // Two color words; color 3 becomes green.
expect_color(0,14,0); expect_color(0,15,992);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:32
Write background colors starting at the indexed slot. The range may cross palette boundaries and is clamped only at the end of all 32 slots.
kitaqgb/lib/cgb_palette.c:135
void cgb_bg_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count)
{
cgb__write_colors_bg_raw(cgb__palette_color_slot(palette_index, start_color_index), colors, color_count);
}cgb_bg_colors_raw
全32色の通し番号を開始位置として、背景・ウィンドウ用パレットへRGB15値を順に書き込みます。パレット番号と色番号の組ではなく、0〜31の色スロット番号を使います。書き込む配列は色のワード列であり、タイルのバイト列ではありません。
書式
void cgb_bg_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);引数
start_color_slot全32色の通し番号(0〜31)。
& 31で正規化した後に書き込み数を制限します。番号は「パレット番号×4+色番号」で、バイト位置ではありません。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。color_count色ワード数です。バイト数ではありません。
u8(0〜255)で渡し、0なら何もしません。実際の書き込み数は32 - 正規化後の開始スロットまでに制限され、配列もその数だけ読みます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_bg_colors_raw(23,blue_word,1); // Absolute slot 23 = palette 5, color 3.
expect_color(0,23,31744);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:42
Expose sequential background writes using an absolute color-slot index.
kitaqgb/lib/cgb_palette.c:159
void cgb_bg_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count)
{
cgb__write_colors_bg_raw(start_color_slot, colors, color_count);
}cgb_bg_palette
4個のRGB15値で背景・ウィンドウ用パレット1組の全4色を設定します。範囲指定の書き込みを開始色0・色数4で呼び出す関数です。そのパレットを使うタイルやスプライトの指定は別に行います。
書式
void cgb_bg_palette(u8 palette_index, const u16* colors);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_bg_palette(4,red_palette); // Exactly four color words for BG palette 4.
expect_color(0,16,32767); expect_color(0,19,31);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:37
Write four consecutive colors for one background palette.
kitaqgb/lib/cgb_palette.c:147
void cgb_bg_palette(u8 palette_index, const u16* colors)
{
cgb_bg_colors(palette_index, 0, colors, 4);
}cgb_bg_rgb
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
赤・緑・青の成分をcgb_rgb15でまとめ、背景・ウィンドウ用パレットへ1色書き込みます。パック済みの値ではなく、各成分を個別に指定したい場合に使います。
書式
void cgb_bg_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。r5, g5, b5順に赤・緑・青の成分で、通常は各0〜31です。飽和処理ではなくマスクを行います。マクロ展開では各引数が1回ずつ現れます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_bg_rgb(2,3,0,0,31); // Blue, supplied as three five-bit components.
expect_color(0,11,31744);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:26
Pack component values and write one background color.
kitaqgb/lib/cgb_palette.c:122
void cgb_bg_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5)
{
cgb_bg_color(palette_index, color_index, cgb_rgb15(r5, g5, b5));
}cgb_obj_color
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
パック済みのRGB15色をスプライト(OBJ)用パレットへ直ちに1色書き込みます。パレット番号と色番号で全32色のうち1色を指定します。タイルデータや、タイルが使うパレットの指定は変更しません。
書式
void cgb_obj_color(u8 palette_index, u8 color_index, u16 rgb15);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。rgb15パック済みの15ビットRGB色(通常0〜32767)。渡した
u16の下位・上位バイトを順に書き込み、ライブラリ側ではビット15を明示的に消しません。正しい値の生成にはCGB_RGB15またはcgb_rgb15を使えます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_obj_color(1,3,31744); // Blue sprite color at the same palette/color indices.
expect_color(1,7,31744);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:24
Set one object color through the indexed palette wrapper.
kitaqgb/lib/cgb_palette.c:116
void cgb_obj_color(u8 palette_index, u8 color_index, u16 rgb15)
{
cgb__write_color_obj_raw(cgb__palette_color_slot(palette_index, color_index), rgb15);
}cgb_obj_colors
指定したパレット番号・開始色番号から、スプライト(OBJ)用パレットへRGB15の配列を順に書き込みます。4色単位のパレット境界を越えて書き込めますが、全32色の末尾で止まります。
書式
void cgb_obj_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。start_color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。color_count色ワード数です。バイト数ではありません。
u8(0〜255)で渡し、0なら何もしません。実際の書き込み数は32 - 正規化後の開始スロットまでに制限され、配列もその数だけ読みます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_obj_colors(3,2,tail_red,2); // Sprite color 3 becomes red.
expect_color(1,14,0); expect_color(1,15,31);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:34
Write object colors from the indexed slot, potentially crossing into later palettes.
kitaqgb/lib/cgb_palette.c:141
void cgb_obj_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count)
{
cgb__write_colors_obj_raw(cgb__palette_color_slot(palette_index, start_color_index), colors, color_count);
}cgb_obj_colors_raw
全32色の通し番号を開始位置として、スプライト(OBJ)用パレットへRGB15値を順に書き込みます。パレット番号と色番号の組ではなく、0〜31の色スロット番号を使います。書き込む配列は色のワード列であり、タイルのバイト列ではありません。
書式
void cgb_obj_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);引数
start_color_slot全32色の通し番号(0〜31)。
& 31で正規化した後に書き込み数を制限します。番号は「パレット番号×4+色番号」で、バイト位置ではありません。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。color_count色ワード数です。バイト数ではありません。
u8(0〜255)で渡し、0なら何もしません。実際の書き込み数は32 - 正規化後の開始スロットまでに制限され、配列もその数だけ読みます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_obj_colors_raw(23,green_word,1); // Same absolute slot in the separate OBJ store.
expect_color(1,23,992);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:44
Expose sequential object writes using an absolute color-slot index.
kitaqgb/lib/cgb_palette.c:165
void cgb_obj_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count)
{
cgb__write_colors_obj_raw(start_color_slot, colors, color_count);
}cgb_obj_palette
4個のRGB15値でスプライト(OBJ)用パレット1組の全4色を設定します。範囲指定の書き込みを開始色0・色数4で呼び出す関数です。そのパレットを使うタイルやスプライトの指定は別に行います。
書式
void cgb_obj_palette(u8 palette_index, const u16* colors);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。colors必要な色ワードを格納した、読み取り可能な
const u16*です。データは呼び出し中に読み取り、キューには保持しません。呼び出し中は配列とそのROMバンクを参照可能にしてください。バンク切り替えは自動では行いません。nullなら何も書き込みません。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_obj_palette(4,blue_palette); // Exactly four words; OBJ color zero is transparent on screen.
expect_color(1,16,32767); expect_color(1,19,31744);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:39
Write four consecutive colors for one object palette.
kitaqgb/lib/cgb_palette.c:153
void cgb_obj_palette(u8 palette_index, const u16* colors)
{
cgb_obj_colors(palette_index, 0, colors, 4);
}cgb_obj_rgb
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
赤・緑・青の成分をcgb_rgb15でまとめ、スプライト(OBJ)用パレットへ1色書き込みます。パック済みの値ではなく、各成分を個別に指定したい場合に使います。
書式
void cgb_obj_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);引数
palette_indexパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。color_indexパレット内の色番号(0〜3)。下位2ビットだけを使います(
& 3)。r5, g5, b5順に赤・緑・青の成分で、通常は各0〜31です。飽和処理ではなくマスクを行います。マクロ展開では各引数が1回ずつ現れます。
戻り値
ありません(void)。
使用条件と制限
パレット操作とガード付きレジスタ書き込みは、DMGモードでは何も書き込みません。ここでsafeはCGBモードの判定を指し、PPUへのアクセス待ちや割り込み禁止を意味しません。LCD停止中、または書き込み可能なPPU期間に収まる短い更新で使用してください。VBlank待ちや複数バイト更新の割り込み保護は行いません。
背景・ウィンドウ用とOBJ用のパレットは別々の記憶領域です。ライブラリの色書き込みは索引の自動増分を有効にし、索引レジスタを変更したままにします。OBJの色番号0は保存・読み出しができても、スプライト描画では透明です。
使用例
cgb_obj_rgb(2,3,0,31,0); // Green sprite color.
expect_color(1,11,992);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:28
Pack component values and write one object color.
kitaqgb/lib/cgb_palette.c:128
void cgb_obj_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5)
{
cgb_obj_color(palette_index, color_index, cgb_rgb15(r5, g5, b5));
}CGB_RGB15
RGB成分をu16にまとめます。赤はビット0〜4、緑は5〜9、青は10〜14に入り、ビット15は0です。各成分を31でマスクするため、範囲外の値は上限で止まらず下位5ビットに折り返します。ハードウェアへは書き込まず、DMGでもCGBでも値を計算できます。
書式
#define CGB_RGB15(r5, g5, b5) ((u16)((u16)((r5) & 31) | ((u16)((g5) & 31) << 5) | ((u16)((b5) & 31) << 10)))引数
r5, g5, b5順に赤・緑・青の成分で、通常は各0〜31です。飽和処理ではなくマスクを行います。マクロ展開では各引数が1回ずつ現れます。
戻り値
0〜32767のパック済みRGB15値(u16)。
使用例
if (CGB_RGB15(component,64,95)!=31775) failures++; // (31,0,31), not saturation.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:16
cgb_rgb15
RGB成分をu16にまとめます。赤はビット0〜4、緑は5〜9、青は10〜14に入り、ビット15は0です。各成分を31でマスクするため、範囲外の値は上限で止まらず下位5ビットに折り返します。ハードウェアへは書き込まず、DMGでもCGBでも値を計算できます。
書式
u16 cgb_rgb15(u8 r5, u8 g5, u8 b5);引数
r5, g5, b5順に赤・緑・青の成分で、通常は各0〜31です。飽和処理ではなくマスクを行います。マクロ展開では各引数が1回ずつ現れます。
戻り値
0〜32767のパック済みRGB15値(u16)。
使用例
if (cgb_rgb15(component,64,95)!=31775) failures++; // Same packed value through a function.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_palette.h:19
Pack the red, green and blue components using the public RGB15 macro.
kitaqgb/lib/cgb_palette.c:104
u16 cgb_rgb15(u8 r5, u8 g5, u8 b5)
{
return CGB_RGB15(r5, g5, b5);
}cgb_tile.h — CGBのタイル番号と属性
cgb_tile.hは、タイルマップ操作の組み込み命令の宣言と、CGBのタイル属性を組み立てるマクロをまとめたヘッダーです。タイル番号は表示する絵柄を選び、属性はパレット、絵柄のVRAMバンク、左右・上下反転、背景の優先度を指定します。属性はタイル画像そのものを書き換える機能ではありません。
__settile系はマップ0x9800、__settileat系は指定したマップ先頭、__settilebg系はLCDCが選ぶ背景マップ、__settilewin系はLCDCが選ぶウィンドウマップを使います。attrは属性だけ、cgbはタイル番号と属性の両方を書きます。bulkは指定アドレスから連続したバイト列を転送します。用途に合わせて、書き込み先と書く内容の両方から命令を選んでください。
KQ_CGB_ATTR_PALなどのマクロで各属性のビットを作り、KQ_CGB_ATTRでまとめて指定できます。bankは絵柄を読むVRAMバンクで、ROMバンク番号ではありません。通常のタイル番号書き込みは選択中のVRAMバンクを使うため、タイルと属性を一緒に設定する前にバンク0を選択してください。DMGでは属性だけの操作は何もせず、タイルと属性を組み合わせた操作はタイル番号だけを書きます。
__で始まる命令はコンパイラが実装を生成するため、これらのためにcgb_tile.cをリンクする必要はありません。通常の書き込み経路はVRAMアクセスを待ちますが、割り込み許可状態を変更するものがあります。_unsafeと_fastはその待機を省くため、LCD停止中など、呼び出し側が書き込み可能な期間を保証できる場面で使います。各命令の割り込み・バンク・転送長の条件も確認してください。
KQ_CGB_ATTR
パレット、タイルデータのバンク、左右反転、上下反転、BG優先の指定からCGBマップ属性の1バイトを作ります。パレットはビット0〜2、残りの4つの真偽値は順にビット3・5・6・7を設定します。ビット4は0です。
書式
#define KQ_CGB_ATTR(palette, bank, xflip, yflip, priority) ((u8)(((palette) & KQ_CGB_ATTR_PAL_MASK) | ((bank) ? KQ_CGB_ATTR_BANK1 : 0) | ((xflip) ? KQ_CGB_ATTR_XFLIP : 0) | ((yflip) ? KQ_CGB_ATTR_YFLIP : 0) | ((priority) ? KQ_CGB_ATTR_PRIORITY : 0)))引数
paletteパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。bank, xflip, yflip, priority0なら無効、0以外なら有効です。特に2も有効になります。これらのマクロは下位ビットではなく、値が0かどうかを判定します。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
CGBマップ属性のビット3を作ります。bankが0以外なら0x08、0なら0を返します。表示するタイルパターンを読み出すVRAMバンクの指定であり、VBKやCPUのアクセス先バンクは変更しません。
左右反転フラグを作ります。enabledが0以外なら0x20、0なら0です。CGBマップ属性に指定すると、タイル各行の8画素を逆順に表示します。保存されているパターンは変更しません。
上下反転フラグを作ります。enabledが0以外なら0x40、0なら0です。CGBマップ属性に指定すると、保存パターンを変えずにタイルの8行を逆順で表示します。
BGをOBJより手前にする優先フラグを作ります。enabledが0以外なら0x80、0なら0です。CGBの優先制御(LCDCビット0)が有効なら、BGの色番号0以外の画素をスプライトより手前に表示します。色番号0の部分にはスプライトが見えるため、タイル全体を不透明にする指定ではありません。
使用例
attr=KQ_CGB_ATTR(10,0,2,2,0); // Palette 2, bank 0, both reflections: 0x62.
if (attr!=98 || KQ_CGB_ATTR(10,2,3,4,5)!=234) failures++;
cell(10,11,1,attr);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:23
KQ_CGB_ATTR_BANK
CGBマップ属性のビット3を作ります。bankが0以外なら0x08、0なら0を返します。表示するタイルパターンを読み出すVRAMバンクの指定であり、VBKやCPUのアクセス先バンクは変更しません。
書式
#define KQ_CGB_ATTR_BANK(bank) ((u8)((bank) ? KQ_CGB_ATTR_BANK1 : 0))引数
bank0なら無効、0以外なら有効です。特に2も有効になります。これらのマクロは下位ビットではなく、値が0かどうかを判定します。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
使用例
attr=(u8)(2|KQ_CGB_ATTR_BANK(enabled)); // Any nonzero value selects tile-data bank 1.
if (attr!=10) failures++;
cell(4,11,1,attr); // Blue hollow square, unlike the bank-0 triangle.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:19
KQ_CGB_ATTR_PAL
CGBマップ属性のパレット番号部分をn & 7で作り、u8として返します。他の属性フラグとはビットORで組み合わせます。
書式
#define KQ_CGB_ATTR_PAL(n) ((u8)((n) & KQ_CGB_ATTR_PAL_MASK))引数
nパレット番号(0〜7)。下位3ビットだけを使います(
& 7)。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
使用例
attr=KQ_CGB_ATTR_PAL(9); // 9 & 7 = palette 1, red in this BG example.
if (attr!=1) failures++;
cell(2,11,1,attr);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:18
KQ_CGB_ATTR_PRIORITY_IF
BGをOBJより手前にする優先フラグを作ります。enabledが0以外なら0x80、0なら0です。CGBの優先制御(LCDCビット0)が有効なら、BGの色番号0以外の画素をスプライトより手前に表示します。色番号0の部分にはスプライトが見えるため、タイル全体を不透明にする指定ではありません。
書式
#define KQ_CGB_ATTR_PRIORITY_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_PRIORITY : 0))引数
enabled0なら無効、0以外なら有効です。特に2も有効になります。これらのマクロは下位ビットではなく、値が0かどうかを判定します。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
使用例
attr=(u8)(3|KQ_CGB_ATTR_PRIORITY_IF(enabled)); // Keep nonzero BG pixels in front of sprites.
if (attr!=131) failures++;
cell(12,11,1,attr);
sprite(7,96,88,2,3); // Red full tile behind the green priority triangle.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:22
KQ_CGB_ATTR_XFLIP_IF
左右反転フラグを作ります。enabledが0以外なら0x20、0なら0です。CGBマップ属性に指定すると、タイル各行の8画素を逆順に表示します。保存されているパターンは変更しません。
書式
#define KQ_CGB_ATTR_XFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_XFLIP : 0))引数
enabled0なら無効、0以外なら有効です。特に2も有効になります。これらのマクロは下位ビットではなく、値が0かどうかを判定します。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
使用例
attr=(u8)(3|KQ_CGB_ATTR_XFLIP_IF(enabled));
if (attr!=35) failures++;
cell(6,11,1,attr); // Green triangle reflected horizontally.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:20
KQ_CGB_ATTR_YFLIP_IF
上下反転フラグを作ります。enabledが0以外なら0x40、0なら0です。CGBマップ属性に指定すると、保存パターンを変えずにタイルの8行を逆順で表示します。
書式
#define KQ_CGB_ATTR_YFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_YFLIP : 0))引数
enabled0なら無効、0以外なら有効です。特に2も有効になります。これらのマクロは下位ビットではなく、値が0かどうかを判定します。
戻り値
上記のビット配置に従う属性フィールド、または組み合わせ済み属性バイト(u8)。
使用条件と制限
これらのマクロはu8の値を計算するだけで、VRAMやレジスタへは書き込みません。各引数は1回ずつ現れます。結果はCGB属性書き込みAPI、またはバンク1のマップへの明示的な書き込みで反映します。DMGの表示にはCGBマップ属性は使われません。
使用例
attr=(u8)(1|KQ_CGB_ATTR_YFLIP_IF(enabled));
if (attr!=65) failures++;
cell(8,11,1,attr); // Red triangle reflected vertically.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じタイルパターンでも、BGとOBJで別々のパレットの色が使われることを学ぶプログラムです。三角形は8×8画素で、上端左の1画素から下端の8画素まで広がります。x=16・32・48・64・80・96・112の位置で、y=24のBG行は赤・青・緑・赤・青・緑・赤、y=64のOBJ行は青・緑・赤・青・緑・赤・青になります。各列では順に、1色指定、RGB成分指定、番号付き範囲、全パレット、通し番号の範囲、標準名のレジスタ、別名のレジスタによる書き込みを使います。
y=88の7個の8×8マスは、x=16から順に、赤い三角形、バンク1の青い1画素幅の四角い枠(32)、左右反転した緑の三角形(48)、上下反転した赤の三角形(64)、両方向に反転した青の三角形(80)、赤いスプライトより手前の緑の三角形(96)、優先フラグなしの比較用にBGを覆う赤い正方形のスプライト(112)です。x=96ではBGの色番号0の部分から赤が見えます。座標は画面左上を原点とする画素単位です。余白は白で、文字は黒です。
FAILED CHECKS 000は、RGBのマスク、2を含む真偽フラグ、パレット番号の折り返し、パレット境界を越える書き込み、スロット31での打ち切り、null・長さ0で何も書かないこと、LCD停止中の色の読み戻しが通ったことを示します。兼用ROMのDMG/CGB実行、DMG専用ROMのDMG実行、CGB専用ROMのCGB実行の4条件を検証します。DMGではCGB MODEが0で、三角形は黒、バンク・反転属性による見た目の変化はなく、最下段の最後の2マスは黒い正方形です。CGBの検証ではCGB MODEが1です。実機とLCD表示中のパレットアクセス競合は未検証です。




ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/cgb_tile.h:21
chain.h — 関節追従と座標履歴
関節でつながる蛇・ひもなどを動かすChainBodyと、移動座標を記録するChainを提供します。ChainBodyは各節の現在位置と16方向の向きを保持し、曲がりを1節ずつ後方へ伝えます。画面端の折り返し、尾を複製する成長、1軸あたり最大2ピクセルの追従に対応します。
chain_wrap_delta
両端がつながった1本の座標軸で、現在位置から目標位置までの最短の符号付き差分を計算します。画面端をまたぐ追従や距離比較に使えます。
書式
s16 chain_wrap_delta(u8 target, u8 current, u16 size);引数
target目標位置。0~size−1の範囲で指定します。
current現在位置。0~size−1の範囲で指定します。
size折り返す座標軸の長さ、1~256。X座標なら幅、Y座標なら高さです。256も渡せる16ビット引数です。
戻り値
最短差分をs16で返します。正は座標が増える方向、負は減る方向、0は同じ位置です。有効な入力では−128~+128に収まります。画面の右や下へ向かう差分が正になります。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainやChainBodyの初期化は必要ありません。関数は計算だけを行い、座標配列、節数、画面を変更しません。
入力の範囲検査や正規化は行いません。size=0、size>256、またはsize以上の座標を渡さないでください。広い座標系から値を渡す場合は、呼び出し前に0~size−1へ折り返します。
左右の経路が同じ長さなら、直接計算したtarget−currentの符号を保ちます。size=160では0→80が+80、80→0が−80です。サイズが1なら有効な座標は0だけで、結果も0です。
使用例
distances[0]=chain_wrap_delta(1,159,160); // +2: cross the right edge.
distances[1]=chain_wrap_delta(159,1,160); // -2: cross the left edge.
distances[2]=chain_wrap_delta(80,0,160); // +80: keep the direct sign.
distances[3]=chain_wrap_delta(0,80,160); // -80: same-length routes.
distances[4]=chain_wrap_delta(0,0,1); // 0: the only valid position.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
このサンプルは座標の折り返しを学ぶためのものです。矢印の左が現在位置、右が目標位置です。幅160で画面端をまたぐ+02/−02、半周の+80/−80、幅1の+00を、関数の戻り値から表示します。先頭の0は2桁表示用です。物体を描画・移動する例ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_wrap.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_wrap.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:35
Return target minus current along the shortest wrapped route. Supply size 1..256 and coordinates in 0..size-1; inputs are not normalized. Exactly half a field retains the direct displacement sign.
kitaqgb/lib/chain.c:98
s16 chain_wrap_delta(u8 target, u8 current, u16 size)
{
s16 delta;
s16 half;
delta = (s16)((s16)target - (s16)current);
half = (s16)(size >> 1);
// At exactly half a field, retain the sign of the direct displacement.
if (delta > half) delta = (s16)(delta - (s16)size);
else if (delta < (s16)(0 - half)) delta = (s16)(delta + (s16)size);
return delta;
}chain_body_init
呼び出し側の3配列を関節データに割り当て、節数を0にします。メモリは確保しません。
書式
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height);引数
body対象の
ChainBody。0なら処理しません。初期化以外は初期化済みの状態を渡します。x各節のX座標を保持する配列。
y各節のY座標を保持する配列。
heading各節の向きを保持する配列。
capacity確保済み配列の長さ、1~255。
width折り返す画面の幅、1~256。
height折り返す画面の高さ、1~256。
戻り値
成功なら1、無効な引数なら0です。有効なbodyに対する失敗ではcapacityとcountを0にし、以後の更新を無効にします。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainBodyは各節の現在位置と向きを保持します。座標履歴を蓄積するChainとは別の状態型です。
x、y、headingはそれぞれcapacityバイトの書き込み可能な配列です。相互に重ねず、使用中は保持してください。添字0が頭で、countには頭も含みます。初期化後にポインター・容量・画面サイズを直接変更しないでください。
向きは時計回りの16方向で、0=右、4=下、8=左、12=上です。入力は下位4ビットを使います。画面座標は右・下が正で、幅・高さは1~256ピクセルです。
使用例
chain_body_init(&body,joint_x,joint_y,joint_heading,8,160,144);
// Eight caller-owned slots, including the head, on a wrapped 160x144 field.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画像は上から直線、下向きへ曲がって1回更新、4回更新の姿です。座標を2倍に拡大して描き、右端の頭から曲がりが後ろへ伝わります。CGB・FCでは頭が赤、胴が青、尾が緑、DMGでは黒です。成長時に重ねた尾を4回の更新で離してから比較しています。画像は同じ8節を3時点で並べたもので、24節の1本の鎖ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:53
Attach three non-overlapping capacity-byte arrays. Return 1 on success. Invalid storage, zero capacity or dimensions outside 1..256 disable the body.
kitaqgb/lib/chain.c:119
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height)
{
if (body == 0) return 0;
body->x = x;
body->y = y;
body->heading = heading;
body->count = 0;
body->capacity = 0;
body->width = width;
body->height = height;
if (x == 0 || y == 0 || heading == 0 || capacity == 0) return 0;
if (width == 0 || width > 256 || height == 0 || height > 256) return 0;
body->capacity = capacity;
return 1;
}chain_body_reset
指定した頭の位置から後方へ節を並べ、直線の初期姿勢を作ります。
書式
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading);引数
body対象の
ChainBody。0なら処理しません。初期化以外は初期化済みの状態を渡します。count頭を含む節数。1~capacity。
head_x頭のX座標。画面幅に合わせて折り返します。
head_y頭のY座標。画面高さに合わせて折り返します。
heading頭の16方向の向き。
戻り値
成功なら1。節数が0、容量超過、またはbodyが0なら姿勢を変えず0です。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainBodyは各節の現在位置と向きを保持します。座標履歴を蓄積するChainとは別の状態型です。
x、y、headingはそれぞれcapacityバイトの書き込み可能な配列です。相互に重ねず、使用中は保持してください。添字0が頭で、countには頭も含みます。初期化後にポインター・容量・画面サイズを直接変更しないでください。
向きは時計回りの16方向で、0=右、4=下、8=左、12=上です。入力は下位4ビットを使います。画面座標は右・下が正で、幅・高さは1~256ピクセルです。
上下左右は4ピクセル間隔です。斜めは整数ベクトル(4,1)、(3,3)、(1,4)などを使うため、実距離は厳密に4ピクセルではありません。画面端をまたぐ姿勢も生成できます。
使用例
chain_body_reset(&body,7,48,16,0); // Seven joints, facing right.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画像は上から直線、下向きへ曲がって1回更新、4回更新の姿です。座標を2倍に拡大して描き、右端の頭から曲がりが後ろへ伝わります。CGB・FCでは頭が赤、胴が青、尾が緑、DMGでは黒です。成長時に重ねた尾を4回の更新で離してから比較しています。画像は同じ8節を3時点で並べたもので、24節の1本の鎖ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:57
Seed count joints in a straight pose behind the head; count includes the head. Return 0 without changing the pose if count is zero or exceeds capacity. Coordinates wrap to the playfield and headings are masked to 0..15.
kitaqgb/lib/chain.c:135
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading)
{
u8 i;
u8 x;
u8 y;
s16 ox;
s16 oy;
if (body == 0) return 0;
if (count == 0 || count > body->capacity) return 0;
heading = (u8)(heading & 15);
x = chain_body_wrap((s16)head_x, body->width);
y = chain_body_wrap((s16)head_y, body->height);
ox = chain_body_offset_x(heading);
oy = chain_body_offset_y(heading);
i = 0;
while (i < count) {
body->x[(__safe_index u8)i] = x;
body->y[(__safe_index u8)i] = y;
body->heading[(__safe_index u8)i] = heading;
x = chain_body_wrap((s16)((s16)x - ox), body->width);
y = chain_body_wrap((s16)((s16)y - oy), body->height);
i = (u8)(i + 1);
}
body->count = count;
return 1;
}chain_body_step
頭を指定位置へ置き、前の節の後方へ各関節を少しずつ引き寄せます。曲がりが1更新につき1節ずつ後方へ伝わります。
書式
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading);引数
body対象の
ChainBody。0なら処理しません。初期化以外は初期化済みの状態を渡します。head_x頭のX座標。画面幅に合わせて折り返します。
head_y頭のY座標。画面高さに合わせて折り返します。
heading頭の16方向の向き。
戻り値
戻り値はありません。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainBodyは各節の現在位置と向きを保持します。座標履歴を蓄積するChainとは別の状態型です。
x、y、headingはそれぞれcapacityバイトの書き込み可能な配列です。相互に重ねず、使用中は保持してください。添字0が頭で、countには頭も含みます。初期化後にポインター・容量・画面サイズを直接変更しないでください。
向きは時計回りの16方向で、0=右、4=下、8=左、12=上です。入力は下位4ビットを使います。画面座標は右・下が正で、幅・高さは1~256ピクセルです。
前の節の更新済み座標から目標位置を求め、画面端をまたぐ最短差分を各軸で計算します。差分0なら移動せず、絶対値1~4なら1ピクセル、5以上なら2ピクセル動きます。ちょうど画面半分の距離では元の差分の符号を保ちます。
次の節へ渡すのは、上書きする前に保存した向きです。全身へ頭の新しい向きを一度に配る処理ではありません。空の状態では何もしません。
頭の速度・加速度、当たり判定、描画、待機は呼び出し側で行います。通常は頭を動かした後、シミュレーション1回につきchain_body_stepを1回呼びます。これは関節追従であり、剛体や厳密な長さ拘束を解く物理ソルバーではありません。
使用例
chain_body_step(&body,48,20,4); // The head turns down; only joint 1 receives 4.
show_pose(8,34);
for(step=1;step<4;step++) {
chain_body_step(&body,48,(u8)(20+step),4);
}
// After four ticks, the new heading has reached joints 1 through 4.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画像は上から直線、下向きへ曲がって1回更新、4回更新の姿です。座標を2倍に拡大して描き、右端の頭から曲がりが後ろへ伝わります。CGB・FCでは頭が赤、胴が青、尾が緑、DMGでは黒です。成長時に重ねた尾を4回の更新で離してから比較しています。画像は同じ8節を3時点で並べたもので、24節の1本の鎖ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:62
Install the caller's head pose, then pull joints from front to rear. Each joint aims four pixels behind its updated predecessor and receives that predecessor's OLD heading. Each axis moves 1 pixel, or 2 when error exceeds 4. Call once per simulation tick; this does not advance head physics or render.
kitaqgb/lib/chain.c:162
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading)
{
u8 i;
u8 previous;
u8 pull;
u8 old;
u8 x;
u8 y;
u8 tx;
u8 ty;
s16 sx;
s16 sy;
if (body == 0) return;
if (body->count == 0) return;
heading = (u8)(heading & 15);
body->x[0] = chain_body_wrap((s16)head_x, body->width);
body->y[0] = chain_body_wrap((s16)head_y, body->height);
body->heading[0] = heading;
pull = heading;
i = 1;
while (i < body->count) {
previous = (u8)(i - 1);
old = body->heading[(__safe_index u8)i];
x = body->x[(__safe_index u8)i];
y = body->y[(__safe_index u8)i];
tx = chain_body_wrap((s16)((s16)body->x[(__safe_index u8)previous] - chain_body_offset_x(pull)), body->width);
ty = chain_body_wrap((s16)((s16)body->y[(__safe_index u8)previous] - chain_body_offset_y(pull)), body->height);
sx = chain_body_correction(chain_wrap_delta(tx, x, body->width));
sy = chain_body_correction(chain_wrap_delta(ty, y, body->height));
body->heading[(__safe_index u8)i] = pull;
body->x[(__safe_index u8)i] = chain_body_wrap((s16)((s16)x + sx), body->width);
body->y[(__safe_index u8)i] = chain_body_wrap((s16)((s16)y + sy), body->height);
// Save before overwriting: a bend advances by one joint per update.
pull = old;
i = (u8)(i + 1);
}
}chain_body_grow
尾の現在位置と向きを次の空きスロットへ複製し、節数を1増やします。
書式
u8 chain_body_grow(ChainBody* body);引数
body対象の
ChainBody。0なら処理しません。初期化以外は初期化済みの状態を渡します。
戻り値
追加できたら1。空、容量一杯、無効化された状態、またはbodyが0なら0です。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainBodyは各節の現在位置と向きを保持します。座標履歴を蓄積するChainとは別の状態型です。
x、y、headingはそれぞれcapacityバイトの書き込み可能な配列です。相互に重ねず、使用中は保持してください。添字0が頭で、countには頭も含みます。初期化後にポインター・容量・画面サイズを直接変更しないでください。
向きは時計回りの16方向で、0=右、4=下、8=左、12=上です。入力は下位4ビットを使います。画面座標は右・下が正で、幅・高さは1~256ピクセルです。
追加直後の尾は前の尾と重なります。以後のchain_body_stepで徐々に離れます。成長そのものでは頭や他の節を移動させません。
使用例
chain_body_grow(&body); // Copy the tail pose into the eighth slot.
chain_body_step(&body,48,16,0);
chain_body_step(&body,48,16,0);
chain_body_step(&body,48,16,0);
chain_body_step(&body,48,16,0); // Allow the duplicated tail to separate.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画像は上から直線、下向きへ曲がって1回更新、4回更新の姿です。座標を2倍に拡大して描き、右端の頭から曲がりが後ろへ伝わります。CGB・FCでは頭が赤、胴が青、尾が緑、DMGでは黒です。成長時に重ねた尾を4回の更新で離してから比較しています。画像は同じ8節を3時点で並べたもので、24節の1本の鎖ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:65
Append a copy of the current tail; later steps separate the new joint. Return 1 on growth, or 0 for an empty, disabled or full body.
kitaqgb/lib/chain.c:200
u8 chain_body_grow(ChainBody* body)
{
u8 tail;
u8 next;
if (body == 0) return 0;
next = body->count;
if (next == 0 || next >= body->capacity) return 0;
tail = (u8)(next - 1);
body->x[(__safe_index u8)next] = body->x[(__safe_index u8)tail];
body->y[(__safe_index u8)next] = body->y[(__safe_index u8)tail];
body->heading[(__safe_index u8)next] = body->heading[(__safe_index u8)tail];
body->count = (u8)(next + 1);
return 1;
}chain_body_clear
節数だけを0にして、同じ配列と画面設定を再利用できるようにします。
書式
void chain_body_clear(ChainBody* body);引数
body対象の
ChainBody。0なら処理しません。初期化以外は初期化済みの状態を渡します。
戻り値
戻り値はありません。
使用条件と制限
chain.hを読み込み、chain.cを1回だけコンパイルします。ChainBodyは各節の現在位置と向きを保持します。座標履歴を蓄積するChainとは別の状態型です。
x、y、headingはそれぞれcapacityバイトの書き込み可能な配列です。相互に重ねず、使用中は保持してください。添字0が頭で、countには頭も含みます。初期化後にポインター・容量・画面サイズを直接変更しないでください。
向きは時計回りの16方向で、0=右、4=下、8=左、12=上です。入力は下位4ビットを使います。画面座標は右・下が正で、幅・高さは1~256ピクセルです。
配列のバイト列は消去しません。再開にはchain_body_resetを使います。クリア直後にchain_body_growしても追加されません。
使用例
chain_body_clear(&body); // Forget the pose; the three arrays remain owned by us.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画像は上から直線、下向きへ曲がって1回更新、4回更新の姿です。座標を2倍に拡大して描き、右端の頭から曲がりが後ろへ伝わります。CGB・FCでは頭が赤、胴が青、尾が緑、DMGでは黒です。成長時に重ねた尾を4回の更新で離してから比較しています。画像は同じ8節を3時点で並べたもので、24節の1本の鎖ではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/chain.h:67
Remove all joints while retaining caller-owned storage and playfield settings.
kitaqgb/lib/chain.c:215
void chain_body_clear(ChainBody* body)
{
if (body == 0) return;
body->count = 0;
}chain_clear
保存している座標履歴を論理的に空にし、同じ配列を再利用できるようにします。
書式
void chain_clear(Chain* chain);引数
chain対象の初期化済み
Chainへのポインター。
戻り値
戻り値はありません。
使用条件と制限
点数と先頭位置を0に戻します。保存配列のバイト列は消去せず、メモリも解放しません。Chain と座標配列は呼び出し側が用意し、使用中は保持します。要素番号の8ビット加算があふれないよう、容量は128以下にしてください。
GB版は有効な chain ポインターが必要です。
使用例
chain_clear(&trail);result[11]=chain_get_count(&trail);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4点を保持している履歴を消去し、点数が0になることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/chain.h:21
Forget the recorded points without clearing or freeing the backing storage.
kitaqgb/lib/chain.c:14
void chain_clear(Chain* chain)
{
chain->count = 0;
chain->head = 0;
}chain_get_count
現在取り出せる座標の点数を調べます。
書式
u8 chain_get_count(const Chain* chain);引数
chain対象の初期化済み
Chainへのポインター。
戻り値
0~容量の点数。ヌルの履歴なら0です。
使用条件と制限
Chain と座標配列は呼び出し側が用意し、使用中は保持します。要素番号の8ビット加算があふれないよう、容量は128以下にしてください。
使用例
result[4]=chain_get_count(&trail);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
容量4へ5回追加しても点数は4です。消去後と容量0の履歴では0になることも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/chain.h:30
Return the number of stored points, or zero for a null chain.
kitaqgb/lib/chain.c:63
u8 chain_get_count(const Chain* chain)
{
if (chain == 0) return 0;
return chain->count;
}chain_get_segment
座標履歴から、何個前の点かを指定して座標を取り出します。
書式
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out);引数
chain対象の初期化済み
Chainへのポインター。index点の古さ。0が最新、
count - 1が保存中の最古です。out結果を書き込む独立した
ChainPoint。ヌルなら失敗します。
戻り値
読み出せれば1、不正な要求なら0です。ヌルでない out は検査前に(0,0)へ初期化されます。
使用条件と制限
out を履歴配列や Chain 自体と重ねないでください。先に出力を消去するため、元データが壊れます。Chain と座標配列は呼び出し側が用意し、使用中は保持します。要素番号の8ビット加算があふれないよう、容量は128以下にしてください。
使用例
result[1]=chain_get_segment(&trail,0,&point);result[2]=point.x;result[3]=point.y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
番号0から最新点(40,24)を読み、番号3から最古点(16,8)を読みます。範囲外の番号4では失敗し、出力は(0,0)になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/chain.h:28
Read a point by age, with index zero denoting the newest point. Return zero for an invalid request and clear a non-null output first. Keep head + index within the 8-bit range: addition is narrowed before the capacity wrap.
kitaqgb/lib/chain.c:40
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out)
{
u8 pos;
if (out != 0) {
out->x = 0;
out->y = 0;
}
if (out == 0) return 0;
if (chain == 0) return 0;
if (chain->capacity == 0) return 0;
if (index >= chain->count) return 0;
pos = (u8)(chain->head + index);
while (pos >= chain->capacity) {
pos = (u8)(pos - chain->capacity);
}
out->x = chain->points[pos].x;
out->y = chain->points[pos].y;
return 1;
}chain_init
座標履歴用のリングバッファに保存領域を割り当て、履歴を空にします。
書式
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity);引数
chain初期化する書き込み可能な
Chain。storagecapacity個のChainPointを格納できる配列。capacity保存できる点数。1~128を推奨します。0では記録しません。
戻り値
戻り値はありません。
使用条件と制限
Chain と座標配列は呼び出し側が用意し、使用中は保持します。要素番号の8ビット加算があふれないよう、容量は128以下にしてください。
GB版は有効な chain ポインターが必要です。
使用例
chain_init(&trail,storage,4);result[0]=chain_get_count(&trail);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4点分の保存領域を登録し、点数が0から始まることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/chain.h:19
Attach caller-owned ring storage and start with no recorded points. The storage must remain valid for the lifetime of the chain. Use a valid chain pointer and capacity entries of caller-owned storage. Capacity at most 128 keeps every head+age sum in the byte range used by the reader.
kitaqgb/lib/chain.c:5
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity)
{
chain->points = storage;
chain->capacity = capacity;
chain->count = 0;
chain->head = 0;
}chain_push_head
最新の座標を履歴の先頭に追加します。容量に達すると最も古い点を置き換えます。
書式
void chain_push_head(Chain* chain, s16 x, s16 y);引数
chain対象の初期化済み
Chainへのポインター。x追加する点の符号付きX座標。
y追加する点の符号付きY座標。
戻り値
戻り値はありません。
使用条件と制限
ヌルの履歴または容量0なら何もしません。Chain と座標配列は呼び出し側が用意し、使用中は保持します。要素番号の8ビット加算があふれないよう、容量は128以下にしてください。
使用例
chain_push_head(&trail,8,8);chain_push_head(&trail,16,8);chain_push_head(&trail,24,16);chain_push_head(&trail,32,16);chain_push_head(&trail,40,24);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
容量4に5点を記録します。最新点は(40,24)、残っている最古の点は(16,8)になり、最初の(8,8)は押し出されます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/chain.h:24
Move the ring head backward and store the newest point. Once full, each push replaces the oldest point; zero capacity is a no-op.
kitaqgb/lib/chain.c:22
void chain_push_head(Chain* chain, s16 x, s16 y)
{
if (chain == 0) return;
if (chain->capacity == 0) return;
if (chain->head == 0) chain->head = (u8)(chain->capacity - 1);
else chain->head--;
chain->points[chain->head].x = x;
chain->points[chain->head].y = y;
if (chain->count < chain->capacity) chain->count++;
}danmaku.h — 弾プール・扇状生成・被弾とグレイズ
160×128ピクセル内の最大96発を固定配列で処理する弾幕ライブラリです。1/16ピクセルの位置更新、32方向の扇形生成、ヒット・かすり、4区画のタイル合成を行います。シミュレーションはGBでも動きますが、present_nowはCGB専用の転送です。
danmaku_clear
有効な弾をすべて消し、現在数・今回のヒット/かすり・次の探索位置を初期化します。
書式
void danmaku_clear();戻り値
戻り値はありません。
使用条件と制限
ピーク・生成総数・拒否総数は保持します。画面から弾を消すにはRAMマップを消去して転送してください。
使用例
danmaku_clear();result[6]=dm_count;result[7]=dm_spawned;result[8]=dm_peak;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:35
Deactivate the pool while retaining peak/spawn/rejection counters and the RAM map.
kitaqgb/lib/danmaku.c:28
void danmaku_clear() {
u8 i;
for (i=0; i<DANMAKU_MAX; i++) dm_bullets[i].active=0;
dm_count=0; dm_hit=0; dm_graze=0; dm_cursor=0;
}danmaku_clear_map
32×18タイル分のdm_map 576バイトを0で埋めます。
書式
void danmaku_clear_map();戻り値
戻り値はありません。
使用条件と制限
弾の状態とVRAMは変更しません。毎フレーム clear_map → 装飾の書き込み → step → 転送の順で使います。
使用例
danmaku_clear_map();result[11]=dm_map[0];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:42
32 directions, 0=right, 8=down, 16=left, 24=up. speed in 1/16 px. Clear the 32x18 RAM map before drawing the next frame; no transfer is performed.
kitaqgb/lib/danmaku.c:78
void danmaku_clear_map() {
__asm {
LD_HL_IMM dm_map
LD_B_IMM 72
XOR_A
dm_clm_loop:
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
DEC_B
JR_NZ dm_clm_loop
RET
}
}danmaku_fan
32方向のテーブルから速度を作り、角度をずらしながら弾を連続生成します。
書式
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);引数
x初期Xピクセル0~159。
y初期Yピクセル0~127。
direction開始角。下位5ビットを使い、0=右、8=下、16=左、24=上。
step弾ごとの角度増分。32方向で周回します。
count生成試行数。96に制限します。
speed1/16ピクセル単位の速さ。64(4ピクセル/更新)に制限します。
戻り値
戻り値はありません。
使用条件と制限
満杯でも残りの生成を試行します。sin/cos表は整数近似で、斜め方向の距離は丸めの影響を受けます。
使用例
danmaku_fan(80,64,0,4,8,64);result[9]=dm_count;result[10]=dm_spawned;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:39
Attempt a fan using wrapping 32-step angles; count is capped at 96 and speed at 64.
kitaqgb/lib/danmaku.c:62
void danmaku_fan(u8 x,u8 y,u8 direction,u8 step,u8 count,u8 speed) {
u8 n;
u8 a;
s8 vx;
s8 vy;
if(count>DANMAKU_MAX) count=DANMAKU_MAX;
if(speed>64) speed=64;
a=direction&31;
for(n=0;n<count;n++) {
vx=(s8)(((s16)dm_cos[a]*(s16)speed)>>6);
vy=(s8)(((s16)dm_sin[a]*(s16)speed)>>6);
danmaku_spawn(x,y,vx,vy);
a=(u8)((a+step)&31);
}
}danmaku_present_now
CGBのGDMAでdm_map全576バイトを背景マップ$9800へ同期転送します。
書式
void danmaku_present_now();戻り値
戻り値はありません。
使用条件と制限
CGB専用です。DMGでは呼ばないでください。LCD停止中またはVBlank開始直後に使い、VRAMバンクは0になります。以前のVBKは復元しません。弾用タイル0~15はアプリ側で用意します。
使用例
// CGB general DMA; the LCD is stopped for this complete-map upload.
danmaku_present_now();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:48
Requires CGB and LCD off or the beginning of VBlank. VBK restored to 0. Transfers 18 rows (32-byte stride) to BG map 9800 with 36-block GDMA.
kitaqgb/lib/danmaku.c:301
void danmaku_present_now() {
__asm {
XOR_A
LDH_MEM_A 0x4F
LD_HL_IMM dm_map
LD_A_H
LDH_MEM_A 0x51
LD_A_L
LDH_MEM_A 0x52
LD_A_IMM 0x18
LDH_MEM_A 0x53
XOR_A
LDH_MEM_A 0x54
LD_A_IMM 35
LDH_MEM_A 0x55
RET
}
}danmaku_reset
96発の弾プールを空にし、最大同時数・生成総数・満杯による拒否総数を0へ戻します。
書式
void danmaku_reset();戻り値
戻り値はありません。
使用条件と制限
プレイヤー座標・無敵フラグ・背景RAMマップは変更しません。ゲーム開始時に呼び、これらは別途設定します。
使用例
danmaku_reset();result[0]=dm_count;result[1]=dm_peak;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:33
Reset pool, peak and lifetime counters; player fields and map contents remain caller-owned state.
kitaqgb/lib/danmaku.c:34
void danmaku_reset() {
danmaku_clear(); dm_peak=0; dm_spawned=0; dm_rejected=0;
}danmaku_spawn
空きスロットを巡回検索し、指定した位置と速度の弾を1発作ります。
書式
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);引数
x初期Xピクセル0~159。
y初期Yピクセル0~127。
vx符号付きQ4.4速度−128~127。16が1更新あたり右へ1ピクセルです。
vy符号付きQ4.4速度。正は下方向です。
戻り値
生成できれば1、範囲外または満杯なら0。範囲外の要求はdm_rejectedへ加算しません。
使用条件と制限
位置は内部で1/16ピクセル単位です。生成/拒否のu16総数は65535の次に0へ周回します。メモリの動的確保は行いません。
使用例
result[2]=danmaku_spawn(40,40,16,0);result[3]=danmaku_spawn(160,40,0,0);result[4]=dm_count;result[5]=dm_rejected;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:37
Use pixel origins inside 160x128 and signed sixteenth-pixel velocities; return one if a slot was allocated.
kitaqgb/lib/danmaku.c:39
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy) {
u8 n;
u8 i;
if (x>=160 || y>=128) return 0;
for(n=0;n<DANMAKU_MAX;n++) {
i=dm_cursor;
dm_cursor++;
if(dm_cursor>=DANMAKU_MAX) dm_cursor=0;
if(dm_bullets[i].active==0) {
dm_bullets[i].x=(s16)((u16)x<<4);
dm_bullets[i].y=(s16)((u16)y<<4);
dm_bullets[i].vx=vx; dm_bullets[i].vy=vy;
dm_bullets[i].active=1; dm_bullets[i].grazed=0;
dm_count++; dm_spawned++;
if(dm_count>dm_peak) dm_peak=dm_count;
return 1;
}
}
dm_rejected++;
return 0;
}danmaku_step
全弾を1回移動し、範囲外の弾を削除して、弾のタイル合成とヒット/かすり判定を行います。
書式
void danmaku_step();戻り値
戻り値はありません。
使用条件と制限
描画中心は座標を4ピクセル区画へ丸めた中心です。プレイヤーとの各軸差が8以下で初回かすり、3以下でヒットします。無敵はヒットだけを抑えます。dm_hit/dm_grazeは呼び出しごとに初期化され、かすりは各弾1回です。タイル0~15を4つの区画ビットとして合成し、占有セルの装飾ID16以上は置き換えます。
使用例
dm_player_x=0;dm_player_y=0;dm_invulnerable=0;for(i=0;i<8;i++){danmaku_clear_map();danmaku_step();}result[12]=dm_count;result[13]=dm_hit;result[14]=dm_graze;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB専用の描画例です。中央(80,64)から8方向へ弾を放ち、8回更新して赤い3×3の点8個を放射状に表示します。縦横は32ピクセル進み、斜め方向は整数近似で進みます。プールの消去と生涯生成数の保持も検査します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/danmaku.h:45
Caller fills decoration after clear_map, then calls step to overlay bullets. hit and graze are events for this step. Collision uses rendered centers.
kitaqgb/lib/danmaku.c:99
void danmaku_step() {
// One linear pass. No per-bullet bank calls, multiplication or division.
// The 8-byte packed public layout is x16,y16,vx8,vy8,active,graze.
__asm {
XOR_A
LD_MEM_A dm_hit
LD_MEM_A dm_graze
LD_A_IMM 96
LD_MEM_A dm_remaining
LD_HL_IMM dm_bullets
dm_tick_loop:
PUSH_HL
LD_BC_IMM 6
ADD_HL_BC
LD_A_HL
OR_A
JP_Z dm_tick_next
DEC_HL
LD_A_HL
LD_MEM_A dm_vy
DEC_HL
LD_A_HL
LD_C_A
LD_B_IMM 0
AND_IMM 128
JR_Z dm_tick_xsign
DEC_B
// B:C is the sign-extended X velocity. Add to Q12.4 position; high byte >=10 also rejects negative X.
dm_tick_xsign:
POP_HL
PUSH_HL
LD_A_HL
ADD_C
LDI_HL_A
LD_E_A
LD_A_HL
ADC_B
LDI_HL_A
CP_IMM 10
JP_NC dm_tick_kill
RRCA
RRCA
RRCA
RRCA
LD_D_A
LD_A_E
RRCA
RRCA
RRCA
RRCA
AND_IMM 15
OR_D
LD_MEM_A dm_px
LD_A_MEM dm_vy
LD_C_A
LD_B_IMM 0
AND_IMM 128
JR_Z dm_tick_ysign
DEC_B
// Apply signed Y velocity similarly; high byte >=8 rejects negative Y and positions at or below 128 pixels.
dm_tick_ysign:
LD_A_HL
ADD_C
LDI_HL_A
LD_E_A
LD_A_HL
ADC_B
LDI_HL_A
CP_IMM 8
JP_NC dm_tick_kill
RRCA
RRCA
RRCA
RRCA
LD_D_A
LD_A_E
RRCA
RRCA
RRCA
RRCA
AND_IMM 15
OR_D
LD_MEM_A dm_py
// Signed differences shifted into 0..16 for a wrap-safe range test.
// Rendered centers are (pixel & FC)+2. Test a square of radius eight around the player for graze eligibility.
LD_A_MEM dm_player_x
LD_C_A
LD_A_MEM dm_px
AND_IMM 252
ADD_A_IMM 10
SUB_C
CP_IMM 17
JR_NC dm_tick_plot
LD_B_A
LD_A_MEM dm_player_y
LD_C_A
LD_A_MEM dm_py
AND_IMM 252
ADD_A_IMM 10
SUB_C
CP_IMM 17
JR_NC dm_tick_plot
LD_C_A
INC_HL
INC_HL
INC_HL
LD_A_HL
OR_A
JR_NZ dm_tick_check_hit
LD_A_IMM 1
LD_HL_A
LD_A_MEM dm_graze
INC_A
LD_MEM_A dm_graze
// A bullet grazes at most once during its lifetime. Invulnerability suppresses hits, but not first-graze events.
// The inner hit test uses a square of radius three around the same quantized center.
dm_tick_check_hit:
LD_A_MEM dm_invulnerable
OR_A
JR_NZ dm_tick_plot
LD_A_B
SUB_IMM 5
CP_IMM 7
JR_NC dm_tick_plot
LD_A_C
SUB_IMM 5
CP_IMM 7
JR_NC dm_tick_plot
LD_A_IMM 1
LD_MEM_A dm_hit
// Select one of four quadrant bits from pixel bit 2, then address map[(y>>3)*32+(x>>3)].
// Decoration IDs >=16 are replaced at occupied cells; bullet IDs 0..15 merge with OR.
dm_tick_plot:
LD_A_MEM dm_py
AND_IMM 4
AND_IMM 254
RRCA
LD_B_A
LD_A_MEM dm_px
AND_IMM 4
AND_IMM 254
RRCA
AND_IMM 254
RRCA
OR_B
LD_E_A
LD_D_IMM 0
LD_HL_IMM dm_bits
ADD_HL_DE
LD_A_HL
LD_C_A
LD_A_MEM dm_py
AND_IMM 120
LD_L_A
LD_H_IMM 0
ADD_HL_HL
ADD_HL_HL
LD_A_MEM dm_px
AND_IMM 254
RRCA
AND_IMM 254
RRCA
AND_IMM 254
RRCA
LD_E_A
LD_D_IMM 0
ADD_HL_DE
LD_DE_IMM dm_map
ADD_HL_DE
LD_A_HL
CP_IMM 16
JR_C dm_tick_merge
XOR_A
dm_tick_merge:
OR_C
LD_HL_A
JR dm_tick_next
// Restore the saved record base, clear active at offset six and decrement the live count exactly once.
dm_tick_kill:
POP_HL
PUSH_HL
LD_BC_IMM 6
ADD_HL_BC
XOR_A
LD_HL_A
LD_A_MEM dm_count
DEC_A
LD_MEM_A dm_count
// Restore the saved base even for inactive or culled bullets, then advance by the fixed eight-byte record size.
dm_tick_next:
POP_HL
LD_BC_IMM 8
ADD_HL_BC
LD_A_MEM dm_remaining
DEC_A
LD_MEM_A dm_remaining
JP_NZ dm_tick_loop
RET
}
}debug.h — RAMに値とアサートを記録
名前・値・フレーム番号を固定容量のRAMログに記録します。アサートは情報を残して実行を続ける方式です。
debug_assert_fail
アサート失敗のコードを保持し、ASSERTというログの追加を試みます。
書式
void debug_assert_fail(u16 code);引数
code保持する16ビットのエラー識別コード。
戻り値
戻り値はありません。
使用条件と制限
実行を停止しません。ログが満杯でも最後のアサートコードは更新されます。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_assert_fail(42);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
コード42を記録します。その後ログを満杯にしてコード999を記録し、件数は32のままでも最後のコードは999になることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:33
Record the assertion code and attempt to append an ASSERT entry. This helper does not halt execution, and the code is retained even if the log is full.
kitaqgb/lib/debug.c:56
void debug_assert_fail(u16 code)
{
kq_debug_last_assert = code;
debug_push((const u8*)"ASSERT", code);
}debug_get_last_assert
最後に記録されたアサートコードを、ログの空きに関係なく取得します。
書式
u16 debug_get_last_assert();戻り値
最後の16ビットコード。初期化直後は0です。
使用条件と制限
0は初期値であり、コード0の失敗と区別できません。読み出しても値を消去しません。
使用例
result[22]=debug_get_last_assert();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
マクロで記録した77、満杯後の999、再初期化後の0を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:39
Read the last recorded assertion code, or the zero value installed by initialization.
kitaqgb/lib/debug.c:75
u16 debug_get_last_assert()
{
return kq_debug_last_assert;
}debug_get_trace_count
ログ配列に入っている有効エントリー数を調べます。
書式
u8 debug_get_trace_count();戻り値
0~DEBUG_TRACE_MAX の件数です。
使用条件と制限
ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
result[1]=debug_get_trace_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
通常の10件と、追加し続けた後の上限32件を確認します。満杯になっても件数は折り返しません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:35
Return the number of valid entries in the fixed trace buffer.
kitaqgb/lib/debug.c:63
u8 debug_get_trace_count()
{
return kq_debug_trace_count;
}debug_get_trace_log
記録済みの名前・値・フレームを読み出すため、内部配列への参照を取得します。
書式
const DebugTraceEntry* debug_get_trace_log();戻り値
内部 DebugTraceEntry 配列への読み取り用ポインターです。
使用条件と制限
配列の複製ではありません。有効範囲は debug_get_trace_count() までです。初期化後に残る古いバイト列を有効な記録として扱わないでください。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
entries=debug_get_trace_log();for(i=0;i<10;i++){result[2+i]=entries[i].value;result[12+i]=entries[i].frame;}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
10件の値とフレーム欄を順に読み出し、名前BEGINの先頭文字Bも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:37
Expose the internal trace array for inspection; use the count to bound reads.
kitaqgb/lib/debug.c:69
const DebugTraceEntry* debug_get_trace_log()
{
return kq_debug_trace_log;
}debug_init
デバッグログの有効件数・フレームタグ・最後のアサートコードを初期化します。
書式
void debug_init();戻り値
戻り値はありません。
使用条件と制限
既存エントリーのバイト列は消去しません。初回の記録前に呼び出してください。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_init();result[0]=debug_get_trace_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期件数0を確認し、記録後に再初期化して件数とアサートコードだけが0に戻り、配列の古い値は残ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:22
Reset log length, frame tag and last assertion code; existing entry bytes are not erased.
kitaqgb/lib/debug.c:10
void debug_init()
{
kq_debug_trace_count = 0;
kq_debug_frame = 0;
kq_debug_last_assert = 0;
}debug_mark_frame
処理の区切りを、名前付きのフレームマーカーとして記録します。
書式
void debug_mark_frame(const u8* label);引数
label記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
戻り値
戻り値はありません。
使用条件と制限
ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_mark_frame("BEGIN");呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
BEGINというマーカーを追加します。値は65535(0xFFFF)、フレームは513として記録されます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:26
Append a named frame marker using 0xFFFF as its value.
kitaqgb/lib/debug.c:37
void debug_mark_frame(const u8* label)
{
debug_push(label, 0xFFFF);
}debug_set_frame
これ以降に追加するログに付けるフレーム番号を指定します。
書式
void debug_set_frame(u16 frame);引数
frame以後の記録に付ける16ビットのフレーム番号。
戻り値
戻り値はありません。
使用条件と制限
フレームを待つ処理や自動加算はありません。ゲームのフレームカウンターを明示的に渡します。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_set_frame(513);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
513を設定して10件を記録し、すべてのフレーム欄が513になることを読み出して確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:24
Set the frame tag copied into subsequently recorded entries.
kitaqgb/lib/debug.c:18
void debug_set_frame(u16 frame)
{
kq_debug_frame = frame;
}debug_trace_u16
16ビットのスコアやカウンターを名前付きで記録します。
書式
void debug_trace_u16(const u8* name, u16 value);引数
name記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
value保存する0~65535の値。
戻り値
戻り値はありません。
使用条件と制限
ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_trace_u16("WORD",4660);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
WORDに4660(0x1234)を記録し、上位バイトを含む値を読み戻します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:30
Append a 16-bit value with the current frame tag.
kitaqgb/lib/debug.c:49
void debug_trace_u16(const u8* name, u16 value)
{
debug_push(name, value);
}debug_trace_u8
8ビットの状態値を名前と現在のフレーム番号とともに記録します。
書式
void debug_trace_u8(const u8* name, u8 value);引数
name記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
value保存する0~255の値。ログ内では16ビットにゼロ拡張されます。
戻り値
戻り値はありません。
使用条件と制限
ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_trace_u8("BYTE",255);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
BYTEに255を記録し、ログの16ビット値欄にも255として保存されることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:28
Append an unsigned byte value, widened to the log's 16-bit value field.
kitaqgb/lib/debug.c:43
void debug_trace_u8(const u8* name, u8 value)
{
debug_push(name, (u16)value);
}KITAQGB_ASSERT
条件が偽ならコード1のアサート失敗を記録する、文として使えるマクロです。
書式
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)引数
cond成立していることを確認する条件式。0なら失敗です。
戻り値
戻り値はありません。
使用条件と制限
条件は一度評価します。真なら記録しません。失敗しても実行を続けます。do…while (0) 形式なので if…else の中でも一つの文として使えます。
使用例
KITAQGB_ASSERT(1);KITAQGB_ASSERT(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
真と偽を一回ずつ渡し、追加されるログはコード1の一件だけであることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:45
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)KITAQGB_ASSERT_CODE
条件が偽のときだけ、指定したコードのアサート失敗を記録します。
書式
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)引数
cond成立を確認する条件式。
code失敗した場合に評価し、16ビットへ変換するコードの式。
戻り値
戻り値はありません。
使用条件と制限
条件は一度評価し、コードの式は失敗した場合だけ評価します。記録後も実行を続けます。
使用例
KITAQGB_ASSERT_CODE(1,++side_effect);KITAQGB_ASSERT_CODE(0,77);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
真の条件で ++side_effect を指定しても変数は0のままです。偽の条件ではコード77が記録されます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:46
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)KITAQGB_MARK_FRAME
ラベルを文字列ポインターへ変換し、値0xFFFFのフレームマーカーを追加するマクロです。
書式
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))引数
label記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
戻り値
戻り値はありません。
使用条件と制限
ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
KITAQGB_MARK_FRAME("END");呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ENDというラベルを記録し、値65535とフレーム513を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:44
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))KITAQGB_TRACE
名前付きの値を16ビットへ明示変換してデバッグログへ渡すマクロです。
書式
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))引数
name記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
value16ビットへ変換して保存する式。
戻り値
戻り値はありません。
使用条件と制限
常に有効なマクロで、ビルド設定による自動無効化はありません。KITAQGB_TRACE_U16 と同じ16ビット記録です。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
KITAQGB_TRACE("SCORE",600);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
例の記録値は600です。値をログ配列から読み戻して確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:43
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))KITAQGB_TRACE_U16
名前付きの値を16ビットへ明示変換してデバッグログへ渡すマクロです。
書式
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))引数
name記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
value16ビットへ変換して保存する式。
戻り値
戻り値はありません。
使用条件と制限
常に有効なマクロで、ビルド設定による自動無効化はありません。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
KITAQGB_TRACE_U16("WORD2",500);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
例の記録値は500です。値をログ配列から読み戻して確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:42
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))KITAQGB_TRACE_U8
名前付きの値を8ビットへ明示変換してデバッグログへ渡すマクロです。
書式
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))引数
name記録を識別する終端0の文字列。検査まで読み出せる領域に置きます。
value8ビットへ変換して保存する式。
戻り値
戻り値はありません。
使用条件と制限
常に有効なマクロで、ビルド設定による自動無効化はありません。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
KITAQGB_TRACE_U8("CAST",0x1234);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
例の記録値は52です。0x1234の下位8ビット0x34だけを記録します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/debug.h:41
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))dmg3d.h — DMGの128×120モノクロワイヤーフレーム
モノクロの3D線描画です。モデルの頂点・辺・面と物体の位置・回転・倍率を指定し、初期化→フレーム開始→描画→転送の順で使います。物理計算は別途行います。角度は16段階、モデル倍率は256が等倍です。
Vec3はx/y/z、Edgeは頂点番号a/b、Faceは三角形の頂点番号a/b/cを持ちます。Modelのvertices・edges・facesはそれらの配列、各countは要素数です。edge_facesは辺に隣接する面番号f0/f1で、FACE_NONEは面なしを表します。flagsのMODEL_HIDDEN_LINESは面を用いた辺選択を要求します。面の向きは投影後の符号付き面積で判定するため、頂点の並べ方が重要です。
Objectはmodelへの参照と、位置x/y/z・角度rx/ry/rz・倍率scale_q8・表示可否visibleを保持します。モデルの共有は可能ですが、表をコピーしたりROMバンクを自動で切り替えたりはしません。参照先を描画中に読み取れるように配置します。CGBのObjectには色番号colorもあります。
DMG3D_BeginFrame
次のフレームの描画を開始し、隠線判定の適用を解除します。
書式
void DMG3D_BeginFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
96行設定ではステージとマスクを消去します。120行設定ではピクセルを消去せず、転送時にステージを消費します。未転送の描画や背景キューを破棄する操作ではありません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:17
WIRE3D_DMG_HEIGHT = 120. Disable occlusion filtering only. Stage clearing normally happens during transfer, and Init supplies the first clean stage. This does not discard an unsubmitted frame, clear the mask, or reset pending BG writes/dirty flags.
kitaqgb/lib/wire3d_dmg.c:2641
void Wire3DDMG_BeginFrame()
{
/* The consuming stage transfer clears D000 as it uploads, so BeginFrame only
resets draw state. Wire3DDMG_Init supplies the first clean buffer. */
w3ddmg_occlusion_active = 0;
}DMG3D_DrawIndexedEdges
頂点配列と2バイトずつの辺インデックスから、整数倍率の線モデルを描きます。
書式
void DMG3D_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale);引数
vertices3D頂点の配列。
vertex_count頂点配列の要素数。
edges辺の両端の頂点番号。構造体またはバイト対という宣言の形式に従います。
edge_count辺の本数。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
scale整数の拡大率。0は1として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。頂点24個まで、辺は128組以下にしてください。倍率0は1として扱い、物体回転や面による辺選択は行いません。NULL配列、不正な頂点番号、投影できない端点は無視します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:20
WIRE3D_DMG_HEIGHT = 120. Draw byte-pair edge indices after integer scaling and translation; scale zero means one, and there is no object rotation or face filtering. Project at most 24 vertices. Supply at most 128 edge pairs because byte offsets wrap after 255. Invalid vertex IDs/depth-rejected endpoints are skipped; null arrays are ignored. Keep coordinate products/sums representable as s16.
kitaqgb/lib/wire3d_dmg.c:3824
void Wire3DDMG_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
if (vertices == 0) return;
if (edges == 0) return;
count = vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
if (scale == 0) scale = 1;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)(x + (w3ddmg_i16)(v->x * scale));
vy = (w3ddmg_i16)(y + (w3ddmg_i16)(v->y * scale));
vz = (w3ddmg_i16)(z + (w3ddmg_i16)(v->z * scale));
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < edge_count)
{
w3ddmg_u8 a;
w3ddmg_u8 b;
a = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)(i << 1))];
b = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)((i << 1) + 1))];
if ((a < count) && (b < count) &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)b])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)b]);
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_DrawLine2D
画面座標の両端を含む、ピクセルが上下左右につながる線をステージへ描きます。
書式
void DMG3D_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
端点は表示領域内に指定します。斜線には接続用のピクセルを補います。120行設定でdirty転送を有効にすると外接するタイル範囲を記録します。VRAMへの反映は転送関数で行います。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:21
WIRE3D_DMG_HEIGHT = 120. Mark the bounding tile rectangle when dirty transfer is enabled, then draw a connected staged line. Scene drawing may reject it using five mask samples. Supply 128x120 viewport endpoints; low-level pixel bounds checks do not clip the segment geometrically.
kitaqgb/lib/wire3d_dmg.c:2685
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by)
{
w3ddmg_mark_line_dirty(ax, ay, bx, by);
if (w3ddmg_occlusion_active)
{
w3ddmg_line_stage_masked_c(ax, ay, bx, by);
return;
}
w3ddmg_line_x0 = ax;
w3ddmg_line_y0 = ay;
w3ddmg_line_x1 = bx;
w3ddmg_line_y1 = by;
if (ax < 128 && bx < 128 && ay < WIRE3D_DMG_SCREEN_H && by < WIRE3D_DMG_SCREEN_H)
w3ddmg_line_inside_asm();
else
w3ddmg_line_stage_asm();
}DMG3D_DrawLine3D
世界座標の両端を投影して、1本の線を描きます。
書式
void DMG3D_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);引数
ax世界座標の始点のX成分。
ay世界座標の始点のY成分。
az世界座標の始点のZ成分。
bx世界座標の終点のX成分。
by世界座標の終点のY成分。
bz世界座標の終点のZ成分。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
両端が奥行き範囲内の場合だけ描きます。近面・遠面との交点を計算する処理はありません。投影後の端点は個別に画面端へ制限されます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:22
WIRE3D_DMG_HEIGHT = 120. Project both world endpoints and draw only when both pass the depth range. Do not intersect a crossing segment with near/far planes; projected endpoints are independently clamped to the screen edges.
kitaqgb/lib/wire3d_dmg.c:2808
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz)
{
w3ddmg_u8 sx0;
w3ddmg_u8 sy0;
w3ddmg_u8 sx1;
w3ddmg_u8 sy1;
if (w3ddmg_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3ddmg_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DDMG_DrawLine2D(sx0, sy0, sx1, sy1);
}DMG3D_DrawModel
頂点・辺のモデルを、指定位置・回転で等倍描画します。
書式
void DMG3D_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256でDrawModelScaledを呼びます。面や隣接情報があるモデルの辺選択にも、その関数の条件が適用されます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:23
WIRE3D_DMG_HEIGHT = 120. Draw an unscaled model by delegating to DrawModelScaled with scale 256.
kitaqgb/lib/wire3d_dmg.c:2827
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}DMG3D_DrawModelScaled
モデルを拡大縮小し、Y・X・Zの順に回転、平行移動、投影して辺を描きます。
書式
void DMG3D_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256が等倍、0以下も等倍です。頂点は最大24、面は最大16。96行設定は辺も最大16、120行設定はedge_count個を参照します。NULLのモデル・頂点・辺配列は無視します。奥行き範囲をまたぐ辺は切り詰めず省略します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:24
WIRE3D_DMG_HEIGHT = 120. Scale up to 24 vertices, rotate Y/X/Z, translate and project, then traverse all edge_count entries (a byte count) with optional adjacent-face filtering. Face scratch is capped at 16; edge tables must contain all declared entries. Nonpositive scale means 256. Null model/vertex/edge pointers are ignored. Keep products/translations in signed 16-bit range; depth-crossing edges are dropped.
kitaqgb/lib/wire3d_dmg.c:2972
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_is_edge_visible(model, i, face_count))
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_DrawScene
最大8個の物体をカメラに近い順に描き、手前の面で後方の辺を隠します。
書式
void DMG3D_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);引数
objects位置・回転・倍率・visibleを持つ物体配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
物体中心の奥行きで順序を決め、面の外接矩形と線上の5点で隠れ方を近似します。ピクセル単位の深度バッファではありません。visibleが有効なモデルを描き、配列自体は並べ替えません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:25
WIRE3D_DMG_HEIGHT = 120. Sort at most eight object origins by camera depth, near to far. Draw visible valid models and mark front-face bounding rectangles for later objects. The first sorted slot bypasses mask tests and the last needs no new occluder. This is coarse line rejection, not a depth buffer. Null/empty input is ignored; caller data and the current projection cache must remain valid during traversal.
kitaqgb/lib/wire3d_dmg.c:2723
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count)
{
w3ddmg_u8 scene_count;
w3ddmg_u8 i;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3D_DMG_SCENE_OBJECT_LIMIT) scene_count = WIRE3D_DMG_SCENE_OBJECT_LIMIT;
if (scene_count == 0) return;
i = 0;
while (i < scene_count)
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = i;
w3ddmg_scene_depth[(__safe_index w3ddmg_u8)i] = w3ddmg_scene_object_depth(&objects[(__safe_index w3ddmg_u8)i]);
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3ddmg_u8 j;
j = (w3ddmg_u8)(i + 1);
while (j < scene_count)
{
w3ddmg_u8 oi;
w3ddmg_u8 oj;
oi = w3ddmg_scene_order[(__safe_index w3ddmg_u8)i];
oj = w3ddmg_scene_order[(__safe_index w3ddmg_u8)j];
if (w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oj] < w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oi])
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = oj;
w3ddmg_scene_order[(__safe_index w3ddmg_u8)j] = oi;
}
j = (w3ddmg_u8)(j + 1);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 0;
i = 0;
while (i < scene_count)
{
Wire3DDMG_Object* obj;
obj = &objects[(__safe_index w3ddmg_u8)w3ddmg_scene_order[(__safe_index w3ddmg_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
w3ddmg_occlusion_active = i == 0 ? 0 : 1;
Wire3DDMG_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, obj->rx, obj->ry, obj->rz, obj->scale_q8);
if ((w3ddmg_u8)(i + 1) < scene_count)
{
w3ddmg_mark_model_occluder(obj->model);
}
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_occlusion_active = 0;
}DMG3D_EndFrame
背景予約とステージの描画をVRAMへ反映します。
書式
void DMG3D_EndFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
次のVBlankを待って背景予約を反映します。120行設定では有効な補助転送を先に行い、続いてメインを全体またはdirty転送します。ステージは転送で消費します。STAT待ちにより複数の表示期間にまたがる場合があります。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:28
WIRE3D_DMG_HEIGHT = 120. Wait for the next VBlank and flush BG writes, then optionally consume the auxiliary strips before uploading either dirty tiles or the entire main stage. Auxiliary/main source overlap makes that ordering significant. STAT polling continues across access windows; the update need not fit one VBlank.
kitaqgb/lib/wire3d_dmg.c:3081
void Wire3DDMG_EndFrame()
{
w3ddmg_wait_vblank_start();
w3ddmg_flush_bg_queue();
if (w3ddmg_aux_transfer_enabled)
{
w3ddmg_transfer_stage_aux_asm();
}
if (w3ddmg_dirty_transfer_enabled)
{
w3ddmg_transfer_dirty_stage();
}
else
{
w3ddmg_transfer_stage_asm();
}
}DMG3D_EraseModelFaces
モデルの手前向きの三角形面を投影し、ステージ上の線を面の内側から消します。
書式
void DMG3D_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
隠線フラグと面データが必要です。頂点24・面16まで処理し、投影できない面は省略します。倍率0以下は256です。投影用の共有作業領域を書き換えますが、画面転送は行いません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom DMG3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom DMG3D_Face faces[2]={{0,2,1},{0,3,2}};
DMG3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
DMG3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) DMG3D_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) DMG3D_DrawLine2D(40,i,88,i);
DMG3D_EraseTriangle2D(12,12,28,12,12,28);
DMG3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_faces.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:29
WIRE3D_DMG_HEIGHT = 120. Project up to 24 vertices and erase front-facing triangle interiors from the stage for a model with hidden-line flags and face data. Nonpositive scale means 256 (unity). Up to 16 faces are considered; depth-crossing faces are skipped. This also replaces shared projection caches, but does not update VRAM.
kitaqgb/lib/wire3d_dmg.c:2843
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_EraseSpan2D
指定した1行の両端を含む横区間を消去します。
書式
void DMG3D_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);引数
y消去する行のY座標(ピクセル)。
x0最小側のX端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
Xの左右は自動で並べ替えますが、両端を0~127に収めてください。範囲外のYは無視します。Xのクリッピングは行いません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:30
WIRE3D_DMG_HEIGHT = 120. Order the X endpoints and erase the inclusive staged span. Both endpoints must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
kitaqgb/lib/wire3d_dmg.c:2943
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1)
{
w3ddmg_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
w3ddmg_stage_clear_span(y, x0, x1);
}DMG3D_EraseTriangle2D
画面座標で指定した三角形の内部を、行ごとの塗りつぶしで消去します。
書式
void DMG3D_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
cx頂点CのX座標(表示領域内のピクセル)。
cy頂点CのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
表示領域内の頂点を指定します。面積0の三角形は無視します。マスクや表示済みVRAMは変更しません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom DMG3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom DMG3D_Face faces[2]={{0,2,1},{0,3,2}};
DMG3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
DMG3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) DMG3D_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) DMG3D_DrawLine2D(40,i,88,i);
DMG3D_EraseTriangle2D(12,12,28,12,12,28);
DMG3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_faces.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:31
WIRE3D_DMG_HEIGHT = 120. Erase a triangle from staged pixels using integer scanline intersections. Supply viewport coordinates. Degenerate triangles are ignored; the occlusion mask and VRAM are unchanged.
kitaqgb/lib/wire3d_dmg.c:2933
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy)
{
w3ddmg_clear_triangle(ax, ay, bx, by, cx, cy);
}DMG3D_Init
LCD・タイル・背景マップを初期化し、モノクロのワイヤーフレーム表示領域を用意します。
書式
void DMG3D_Init();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
カメラ、背景書き込みキュー、ステージをリセットし、BGPを0xB4に設定します。描画前にBeginFrameを呼びます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:35
WIRE3D_DMG_HEIGHT = 120. Take over LCD state, clear tile VRAM and the BG map, and install a 16x15 viewport beginning at map tile (3,1). Set scroll (8,0), BGP=0xB4 and BG display. Reset camera, queues, transfer modes, both dirty tables and stage. Reserve 0xD000..0xDFFF and retain its WRAM mapping; auxiliary storage aliases it. Call BeginFrame before drawing to establish occlusion state.
kitaqgb/lib/wire3d_dmg.c:2572
void Wire3DDMG_Init()
{
w3ddmg_u8 row;
w3ddmg_u8 col;
w3ddmg_u8 tid;
w3ddmg_u16 off;
w3ddmg_wait_vblank_start();
w3ddmg_reg_lcdc = 0;
w3ddmg_clear_vram_asm();
w3ddmg_fill_bg_map_asm();
row = 0;
tid = 0x90;
while (row < WIRE3D_DMG_TILE_H)
{
off = (w3ddmg_u16)(0x23 + ((w3ddmg_u16)row << 5));
col = 0;
while (col < WIRE3D_DMG_TILE_W)
{
w3ddmg_bg_map_9800[(w3ddmg_u16)(off + (w3ddmg_u16)col)] = tid;
tid = (w3ddmg_u8)(tid + WIRE3D_DMG_TILE_H);
col = (w3ddmg_u8)(col + 1);
}
row = (w3ddmg_u8)(row + 1);
tid = (w3ddmg_u8)(0x90 + row);
}
w3ddmg_reg_scx = 8;
w3ddmg_reg_scy = 0;
w3ddmg_reg_bgp = 0xB4;
w3ddmg_reg_lcdc = 0x81;
w3ddmg_bgq_count = 0;
w3ddmg_aux_transfer_enabled = 0;
w3ddmg_dirty_transfer_enabled = 0;
Wire3DDMG_SetCamera(0, 0, 0, 0, 0, 0);
w3ddmg_clear_stage_asm();
w3ddmg_clear_stage_aux_asm();
w3ddmg_clear_dirty_tables();
}DMG3D_ProjectPoint
世界座標の点をカメラから見た画面座標へ変換します。
書式
w3ddmg_u8 DMG3D_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);引数
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
sx投影結果のX座標を書き込む、有効なu8領域へのポインタ。
sy投影結果のY座標を書き込む、有効なu8領域へのポインタ。
戻り値
投影成功時は1、深度で拒否した場合は0です。
使用条件と制限
奥行き8~255だけを受理します。範囲外なら0を返し、出力値は保持します。成功時は画面端に制限した座標を書き込みます。座標制限は線の幾何学的クリッピングではありません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:43
WIRE3D_DMG_HEIGHT = 120. Project a world point with the current camera. Return zero for invalid depth without changing outputs; success writes clamped 128x120 coordinates. Both output pointers must be valid. Coordinate differences must fit s16.
kitaqgb/lib/wire3d_dmg.c:549
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy)
{
return w3ddmg_project_world(x, y, z, sx, sy);
}DMG3D_PutBgTile
背景マップの1セルへのタイル番号書き込みを予約します。
書式
void DMG3D_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);引数
x32×32背景マップ内のタイル単位のX座標。
y32×32背景マップ内のタイル単位のY座標。
tile背景セルに置く8ビットのタイル番号。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
座標は32×32マップのタイル単位です。48件まで予約でき、範囲外と超過分は無視します。EndFrameで反映する専用キューであり、一般のvramライブラリのキューとは別です。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:44
WIRE3D_DMG_HEIGHT = 120. Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map coordinates and submissions beyond 48 pending writes are silently ignored. This queue is separate from the general vram library queue.
kitaqgb/lib/wire3d_dmg.c:582
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3ddmg_bgq_count >= WIRE3D_DMG_BG_QUEUE_LIMIT) return;
w3ddmg_bgq_x[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = x;
w3ddmg_bgq_y[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = y;
w3ddmg_bgq_tile[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = tile;
w3ddmg_bgq_count = (w3ddmg_u8)(w3ddmg_bgq_count + 1);
}DMG3D_RotatePoint
1点のX・Y・Zを、指定した回転で直接書き換えます。
書式
void DMG3D_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);引数
xX成分を読み書きする独立したs16領域へのポインタ。
yY成分を読み書きする独立したs16領域へのポインタ。
zZ成分を読み書きする独立したs16領域へのポインタ。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定で使います。16段階の角度でY・X・Zの順に回転し、各軸の回転前に成分を±220へ制限します。いずれかのポインタがNULLなら何もせず、各成分には別々の領域を指定します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:46
WIRE3D_DMG_HEIGHT = 120. Rotate in place in Y/X/Z order using 16 steps per full turn. A null component pointer makes the whole call a no-op; use distinct pointers. Each axis rotation clamps its inputs to +/-220.
kitaqgb/lib/wire3d_dmg.c:3121
void Wire3DDMG_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
if (x == 0) return;
if (y == 0) return;
if (z == 0) return;
w3ddmg_rotate_y(x, z, ry);
w3ddmg_rotate_x(y, z, rx);
w3ddmg_rotate_z(x, y, rz);
}DMG3D_SetAuxTransfer
EndFrameで補助領域も転送するかどうかを設定します。
書式
void DMG3D_SetAuxTransfer(w3ddmg_u8 flag);引数
flag0で転送機能を無効、それ以外で有効にします。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。0で無効、それ以外で有効です。この呼び出しだけでは転送・消去を行いません。補助転送の元データはメインステージと重なります。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Use the auxiliary-transfer gate to consume an overlapping stage strip.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetDirtyTransfer(1); DMG3D_SetAuxTransfer(1);
DMG3D_BeginFrame();
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
// Auxiliary upload consumes this source before the dirty main tile is sent.
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
メインステージの一部を補助転送へ渡し、画面座標(80,24)に8×8の黒いブロックを表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_aux_gate.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_aux_gate.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:50
WIRE3D_DMG_HEIGHT = 120. Store the EndFrame auxiliary-transfer gate; any nonzero value enables it. No data is copied or cleared here. Auxiliary source bytes alias the main stage.
kitaqgb/lib/wire3d_dmg.c:3895
void Wire3DDMG_SetAuxTransfer(w3ddmg_u8 flag)
{
w3ddmg_aux_transfer_enabled = flag;
}DMG3D_SetCamera
後続の投影に使うカメラ位置と姿勢を保存します。
書式
void DMG3D_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll);引数
x世界座標でのカメラのX成分。
y世界座標でのカメラのY成分。
z世界座標でのカメラのZ成分。
pitchX軸の回転。16段階で1周します。
yawY軸の回転。16段階で1周します。
rollZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
角度は16段階で1周します。既に描いた線は移動しません。座標差をs16の範囲に収めてください。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:51
WIRE3D_DMG_HEIGHT = 120. Store camera position and angles for later draws; a full turn is 16 angle steps. No existing drawing is reprojected and no input range validation occurs.
kitaqgb/lib/wire3d_dmg.c:2661
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}DMG3D_SetDirtyTransfer
120行表示のメイン転送を、変更タイル中心の転送へ切り替えます。
書式
void DMG3D_SetDirtyTransfer(w3ddmg_u8 flag);引数
flag0で転送機能を無効、それ以外で有効にします。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
値が同じでも現在・過去のdirty記録とステージを消去します。表示中のVRAMは保持します。古い画像が残る状態で有効化すると未登録のタイルが残り得るため、初期化直後など白紙の画面で設定します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Remove the previous frame and upload only the tracked current/previous tiles.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetDirtyTransfer(1); DMG3D_SetAuxTransfer(0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,24,8); DMG3D_DrawLine2D(24,8,24,24);
DMG3D_DrawLine2D(24,24,8,24); DMG3D_DrawLine2D(8,24,8,8);
DMG3D_EndFrame();
DMG3D_BeginFrame();
DMG3D_DrawLine2D(80,80,104,80); DMG3D_DrawLine2D(104,80,104,104);
DMG3D_DrawLine2D(104,104,80,104); DMG3D_DrawLine2D(80,104,80,80);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
左上の古い枠を消し、画面座標(96,88)~(120,112)に新しい枠だけを表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_dirty.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_dirty.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:52
WIRE3D_DMG_HEIGHT = 120. Store the dirty-transfer gate and clear both dirty histories and the main stage, even if the value is unchanged. VRAM is retained; enabling this over an old image can leave unmarked old tiles, so arrange a clean displayed baseline.
kitaqgb/lib/wire3d_dmg.c:3955
void Wire3DDMG_SetDirtyTransfer(w3ddmg_u8 flag)
{
w3ddmg_dirty_transfer_enabled = flag;
w3ddmg_clear_dirty_tables();
w3ddmg_clear_stage_asm();
}DMG3D_SetPalette
モノクロ背景パレットBGPの4階調の割り当てを直接変更します。
書式
void DMG3D_SetPalette(w3ddmg_u8 bgp);引数
bgp4色の階調を2ビットずつ並べたBGPレジスタ値。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
2ビットずつのハードウェア形式をそのまま渡します。VBlank待ちは行いません。CGBのRGBパレットは別途設定します。サンプルは0xFCで白地に黒線を表示します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:53
WIRE3D_DMG_HEIGHT = 120. Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
kitaqgb/lib/wire3d_dmg.c:597
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp)
{
w3ddmg_reg_bgp = bgp;
}DMG3D_TransferAuxNow
補助用の6本のストリップを直ちに転送し、元データを消去します。
書式
void DMG3D_TransferAuxNow();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。補助転送の有効設定にかかわらず実行し、VBlank開始・dirty記録・背景予約を扱いません。元領域D518+256×列から56バイトずつを読みます。メインステージと重なるため転送順に注意します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:57
WIRE3D_DMG_HEIGHT = 120. Upload and consume auxiliary source strips immediately, independent of the auxiliary gate. No VBlank-start wait or dirty-history/BG-queue update occurs. The source overlaps the main stage and must be coordinated with its upload.
kitaqgb/lib/wire3d_dmg.c:3983
void Wire3DDMG_TransferAuxNow()
{
w3ddmg_transfer_stage_aux_asm();
}DMG3D_TransferMainNow
メインステージ全体を直ちに転送し、読み出したステージを消去します。
書式
void DMG3D_TransferMainNow();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。STATでVRAMアクセスを待ちますが、次のVBlank開始は待ちません。転送設定に従わず、dirty記録や背景予約を変更しません。呼び出し側で表示周期を管理します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/dmg3d.h:58
WIRE3D_DMG_HEIGHT = 120. Upload and consume the full main stage immediately, polling STAT but not waiting for the next VBlank. Ignore transfer gates and leave dirty histories and BG tile queue unchanged; the caller coordinates the frame lifecycle.
kitaqgb/lib/wire3d_dmg.c:3970
void Wire3DDMG_TransferMainNow()
{
w3ddmg_transfer_stage_asm();
}entity.h — 固定配列のオブジェクトプール
entityは、敵、弾、アイテムなどの状態を固定配列で管理するオブジェクトプールです。空きスロットを確保してIDで参照し、不要になったスロットを再利用します。ヒープからの動的確保は行いません。座標や速度の単位、移動、当たり判定、タイマーの進行はゲーム側で決めます。
entity.hを読み込み、entity.cを1回だけコンパイルします。最初にentity_initを呼び、entity_createの戻り値が0xFFでないことを確認してください。ENTITY_MAXの既定値は16です。IDで表せる範囲は1~255件ですが、実際に配置できる数は利用可能なRAMに制約されます。定数を変更するときは全コンパイル単位で同じ値にします。
entity_getが返すポインターはプール内のスロットを指します。解放後に同じIDが別の物体へ再利用されるため、長期間有効な物体識別子として保持しないでください。entity_destroyはスロットを非アクティブにするだけです。スプライトの非表示・解放、その他の素材の後始末は呼び出し側で行います。spriteフィールドも管理用の値であり、自動的にOAMへ反映されません。
entity_update_allとentity_draw_allは、アクティブなスロットをIDの小さい順に巡回してfn(id)を呼びます。更新と描画の実処理はコールバックへ書きます。NULLのコールバックでは何もしません。コールバックから後のIDのスロットを作成・解放すると、同じ巡回に反映されます。更新用の巡回が終わってから描画用の巡回を行う構成にすると、役割を分けやすくなります。
entity_clear_all
全エンティティを解放し、スプライト未割当の印を含む各項目を初期値へ戻します。
書式
void entity_clear_all();戻り値
ありません(void)。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
全スロットの active,type,x,y,vx,vy,state,timer を0、sprite を 0xFF にします。ハードウェアのスプライトを隠したり解放したりはしません。描画資源は別途片付けてください。
使用例
entity_clear_all();
after_clear = entity_count_active();
if (after_clear != 0 || entity_get(reused) != 0) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:43
Release every entity and restore deterministic defaults, including the no-sprite sentinel.
kitaqgb/lib/entity.c:14
void entity_clear_all()
{
u8 i = 0;
while (i < ENTITY_MAX) {
entity_pool[i].active = 0;
entity_pool[i].type = 0;
entity_pool[i].x = 0;
entity_pool[i].y = 0;
entity_pool[i].vx = 0;
entity_pool[i].vy = 0;
entity_pool[i].state = 0;
entity_pool[i].timer = 0;
entity_pool[i].sprite = 0xFF;
i++;
}
}entity_count_active
エンティティの状態を変えずに、使用中スロットの数を返します。
書式
u8 entity_count_active();戻り値
active が0以外のスロット数で、0~ENTITY_MAX です。使用数を表し、最も大きいIDや画面上のスプライト数ではありません。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
使用例
created = entity_count_active();
if (created != 2) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:41
Count occupied slots without changing entity state.
kitaqgb/lib/entity.c:93
u8 entity_count_active()
{
u8 i = 0;
u8 count = 0;
while (i < ENTITY_MAX) {
if (entity_pool[i].active != 0) count++;
i++;
}
return count;
}entity_create
指定した種類・座標でエンティティを確保し、再利用可能なIDを返します。空きがなければ 0xFF です。
書式
u8 entity_create(u8 type, s16 x, s16 y);引数
typeゲーム側で定義する0~255の種類番号です。プールは値を保存するだけで、種類に応じた動作や描画処理は選びません。
x, y符号付き16ビットの座標(
-32768~32767)です。ピクセルやQ8.8などの単位はゲーム側で決めます。プールは単位変換、座標の切り詰め、速度に応じた自動移動を行いません。
戻り値
最も小さい空きスロットのID(0~ENTITY_MAX-1)を返します。空きがなければ 0xFF です。成功時は active=1 とし、種類・座標を保存し、速度・状態・タイマーを0、sprite を 0xFF にします。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
使用例
first = entity_create(1, 2, 12);
second = entity_create(2, 6, 12);
if (first == 0xFF || second == 0xFF) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:29
Returns a reusable slot ID, or 0xFF if no slot is free.
kitaqgb/lib/entity.c:33
u8 entity_create(u8 type, s16 x, s16 y)
{
u8 i = 0;
while (i < ENTITY_MAX) {
if (entity_pool[i].active == 0) {
entity_pool[i].active = 1;
entity_pool[i].type = type;
entity_pool[i].x = x;
entity_pool[i].y = y;
entity_pool[i].vx = 0;
entity_pool[i].vy = 0;
entity_pool[i].state = 0;
entity_pool[i].timer = 0;
entity_pool[i].sprite = 0xFF;
return i;
}
i++;
}
return 0xFF;
}entity_destroy
id のエンティティを解放します。保持データは直ちには消さず、再確保時に初期化します。
書式
void entity_destroy(u8 id);引数
id0~
ENTITY_MAX-1の再利用可能なスロット番号です。範囲外のIDは無効です。無効なIDを解放しても変化せず、取得するとnullを返します。
戻り値
ありません(void)。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
解放時は active だけを0にします。他のフィールドの消去、スプライトや素材の解放は行いません。次の確保で同じID・アドレスが別のエンティティに再利用されることがあります。関連資源の片付けはゲーム側で行ってください。
使用例
entity_destroy(first);
after_free = entity_count_active();
if (entity_get(first) != 0 || after_free != 1) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:32
Release a valid slot without clearing its payload. Reallocation resets the payload; a saved pointer or ID must not be treated as a persistent entity identity.
kitaqgb/lib/entity.c:56
void entity_destroy(u8 id)
{
if (id >= ENTITY_MAX) return;
// Deactivation only releases this pool slot; the caller handles sprite/resource cleanup.
entity_pool[id].active = 0;
}entity_draw_all
使用中のエンティティを巡回し、各IDを描画コールバックに渡します。巡回方法は entity_update_all と同じです。コールバックが描画データを書き込まなければ、スプライトや画素は描画されません。
書式
void entity_draw_all(EntityFn fn);引数
fnvoid fn(u8 id)という書式の関数ポインタです。渡されるのはスロットIDなので、フィールドの参照にはentity_get(id)を使います。nullも指定でき、その場合は呼び出しません。
戻り値
ありません(void)。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
巡回がそのスロットに到達した時点で使用状態を調べます。コールバックで後方のスロットを解放すれば処理を飛ばし、後方を確保すれば同じ巡回で処理することがあります。開始時の対象一覧を固定して処理する方式ではありません。
使用例
void demo_draw(u8 id) {
Entity* entity;
entity = entity_get(id);
if (entity != 0) {
m_put((u8)entity->x, (u8)entity->y, (u8)('A' + entity->type - 1));
draw_calls++;
}
}
// From main(), after creating two entities:
draw_calls = 0;
entity_draw_all(demo_draw);
automatic_draws = draw_calls;
if (automatic_draws != 2) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:39
Use the same active-slot traversal for drawing; the callback supplies all rendering.
kitaqgb/lib/entity.c:87
void entity_draw_all(EntityFn fn)
{
entity_update_all(fn);
}entity_get
有効で使用中のIDならエンティティへのポインタを返し、それ以外はnullを返します。指す領域はプール内で再利用されます。
書式
Entity* entity_get(u8 id);引数
id0~
ENTITY_MAX-1の再利用可能なスロット番号です。範囲外のIDは無効です。無効なIDを解放しても変化せず、取得するとnullを返します。
戻り値
使用中のスロットへの書き換え可能なポインタを返します。IDが無効または未使用ならnullです。領域はプールが所有します。自分で解放したり、削除・再確保をまたぐ永続的な識別子として使ったりしないでください。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
解放時は active だけを0にします。他のフィールドの消去、スプライトや素材の解放は行いません。次の確保で同じID・アドレスが別のエンティティに再利用されることがあります。関連資源の片付けはゲーム側で行ってください。
使用例
current_entity = entity_get(first);
if (current_entity != 0) { current_entity->vx = 5; }
else { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:34
Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.
kitaqgb/lib/entity.c:65
Entity* entity_get(u8 id)
{
if (id >= ENTITY_MAX) return 0;
if (entity_pool[id].active == 0) return 0;
return &entity_pool[(__safe_index u8)id];
}entity_init
エンティティプールの全スロットを初期化します。
書式
void entity_init();戻り値
ありません(void)。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
全スロットの active,type,x,y,vx,vy,state,timer を0、sprite を 0xFF にします。ハードウェアのスプライトを隠したり解放したりはしません。描画資源は別途片付けてください。
使用例
entity_init();
if (entity_count_active() != 0) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:27
Reset every slot before the game starts using the pool.
kitaqgb/lib/entity.c:8
void entity_init()
{
entity_clear_all();
}entity_update_all
IDの小さい順に使用中スロットを調べ、各スロットで fn(id) を1回呼びます。移動・衝突・タイマーなどの処理はコールバック側に書きます。プール自体はその処理を提供しません。コールバックがnullなら何もしません。
書式
void entity_update_all(EntityFn fn);引数
fnvoid fn(u8 id)という書式の関数ポインタです。渡されるのはスロットIDなので、フィールドの参照にはentity_get(id)を使います。nullも指定でき、その場合は呼び出しません。
戻り値
ありません(void)。
使用条件と制限
プールは固定配列で、ヒープからの動的確保は行いません。ENTITY_MAX の既定値は16で、1~255の範囲を全コンパイル単位で同じ値にしてください。IDは0から始まり、0xFF は失敗を表します。
巡回がそのスロットに到達した時点で使用状態を調べます。コールバックで後方のスロットを解放すれば処理を飛ばし、後方を確保すれば同じ巡回で処理することがあります。開始時の対象一覧を固定して処理する方式ではありません。
使用例
void demo_update(u8 id) {
Entity* entity;
entity = entity_get(id);
if (entity != 0) { entity->x = entity->x + 1; update_calls++; }
}
// From main(), after creating two entities:
update_calls = 0;
entity_update_all(demo_update);
automatic_updates = update_calls;
if (automatic_updates != 2) { failures++; }呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完全なプログラムでは、再利用されるプールのスロットがどう変化するかを学びます。数字はラベルと組み合わせて読んでください。2個確保すると CREATED 002、1個目を解放すると AFTER FREE 001、最小の空きIDを再利用すると REUSED ID 000、全解放後は AFTER CLEAR 000 です。解放後の取得がnullになることと、再確保時にフィールドが初期化されることも確認します。FAILED CHECKS 000 は期待値に一致したことを表します。
GB版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm実装とソース
kitaqgb/lib/entity.h:37
Call fn for each slot that is active when the scan reaches it; null is a no-op. Callbacks may change the pool, so newly activated later slots can run in this pass.
kitaqgb/lib/entity.c:74
void entity_update_all(EntityFn fn)
{
u8 i = 0;
if (fn == 0) return;
while (i < ENTITY_MAX) {
if (entity_pool[i].active != 0) {
fn(i);
}
i++;
}
}fixed.h — Q8.8固定小数点と矩形
整数だけで座標や速度の小数部分を扱うQ8.8演算と、矩形・点の当たり判定です。各演算の精度と範囲の制限を確認してください。
fix_div
Q8.8値を、入力を縮小してから除算します。
書式
fix8 fix_div(fix8 a, fix8 b);引数
a演算の左辺となるQ8.8の値。
b演算の右辺となるQ8.8の値。
戻り値
Q8.8の商を返します。b == 0 の場合は0です。
使用条件と制限
式は ((a >> 4) * 256) / (b >> 4) です。b が0以外でも b >> 4 が0になる値(正の生の値1~15)は渡さないでください。積も16ビットに収めてください。
使用例
result[3]=fix_div(768,512);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3.0を2.0で割り、1.5(生の値384)を得ます。明示的なゼロ除算入力では0を返すことも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:32
Scale the reduced-precision quotient back to 8.8 and return zero for b == 0. Callers must also ensure (b >> 4) is nonzero; the explicit check only tests b. The intermediate product must fit the compiler's integer arithmetic.
kitaqgb/lib/fixed.c:26
fix8 fix_div(fix8 a, fix8 b)
{
if (b == 0) return 0;
return (fix8)(((a >> 4) * 256) / (b >> 4));
}fix_from_int
整数の座標や速度を、小数部分を扱えるQ8.8形式に変換します。
書式
fix8 fix_from_int(s16 n);引数
n変換する整数。表現可能な範囲は−128~127です。
戻り値
n × 256 の生の値を返します。
使用条件と制限
fix8 は符号付きQ8.8です。生の値256が1.0を表します。中間値と結果が16ビットに収まる範囲で使用してください。
使用例
result[0]=fix_from_int(-3);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
例では整数−3を変換します。画面の−00768は、Q8.8の生の値−768を表します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:23
Convert an integer to signed 8.8 fixed point by moving it into the high byte. The caller must keep the result within the representable 16-bit range.
kitaqgb/lib/fixed.c:5
fix8 fix_from_int(s16 n)
{
return (fix8)(n << 8);
}fix_lerp
Q8.8の始点から終点へ、8ビットの割合で補間します。
書式
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8);引数
a演算の左辺となるQ8.8の値。
b演算の右辺となるQ8.8の値。
t_q8補間割合。0~255で指定します。
戻り値
近似補間したQ8.8値を返します。
使用条件と制限
差分と割合をそれぞれ4ビット右シフトします。割合は16段階になり、255でも終点に一致するとは限りません。b - a と最終値が16ビットに収まるようにしてください。
使用例
result[4]=fix_lerp(0,1024,128);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
0から4.0への割合128で2.0(512)を得ます。割合255では3.75(960)となり、4.0には達しないことも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:35
Interpolate toward b using a quantized Q0.8 weight. Both shifts discard low bits, so small changes may vanish and a weight of 255 need not reach b.
kitaqgb/lib/fixed.c:34
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8)
{
// The endpoint difference must fit s16 before the reduced-precision multiplication.
fix8 d = (fix8)(b - a);
return (fix8)(a + (fix8)((d >> 4) * (s16)(t_q8 >> 4)));
}fix_mul
二つのQ8.8値を、低コストの近似演算で乗算します。
書式
fix8 fix_mul(fix8 a, fix8 b);引数
a演算の左辺となるQ8.8の値。
b演算の右辺となるQ8.8の値。
戻り値
Q8.8形式の積を返します。
使用条件と制限
各入力を先に4ビット右シフトしてから乗算します。両方の下位4ビットが失われるため、完全な16×16ビット乗算とは精度が異なります。fix8 は符号付きQ8.8です。生の値256が1.0を表します。中間値と結果が16ビットに収まる範囲で使用してください。
使用例
result[2]=fix_mul(384,512);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
1.5と2.0から3.0(生の値768)を得ます。生の値271同士の積は256になることも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:28
Approximate 8.8 multiplication by discarding four low bits from each operand before multiplying. This trades precision for a smaller intermediate product.
kitaqgb/lib/fixed.c:18
fix8 fix_mul(fix8 a, fix8 b)
{
return (fix8)((a >> 4) * (b >> 4));
}fix_to_int
Q8.8の小数部分を捨てて整数座標を取り出します。負数は負の無限大方向へ丸めます。
書式
s16 fix_to_int(fix8 x);引数
x整数に変換するQ8.8の値。
戻り値
符号付き整数を返します。
使用条件と制限
fix8 は符号付きQ8.8です。生の値256が1.0を表します。中間値と結果が16ビットに収まる範囲で使用してください。
使用例
result[1]=fix_to_int(-384);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
−1.5の生の値−384を渡し、−2を得ます。画面では−00002と表示します。−1の生の値からは−1を得る境界例も実行します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:25
Discard the eight fractional bits using a signed right shift.
kitaqgb/lib/fixed.c:11
s16 fix_to_int(fix8 x)
{
return (s16)(x >> 8);
}kq_abs_s16
符号付き16ビット値の絶対値を求めます。
書式
s16 kq_abs_s16(s16 v);引数
v絶対値を求める符号付き16ビット値。
戻り値
非負の絶対値を返します(入力−32768を除く)。
使用条件と制限
−32768の正の絶対値32768は s16 に収まりません。非負の結果が必要な場合はこの入力を除外してください。
使用例
result[0]=kq_abs_s16(-123);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
−123を渡して123を得る例です。座標差の大きさを求める場合などに使えます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:38
Return the magnitude in s16. The most-negative s16 has no positive s16 representation and must be excluded by callers needing a nonnegative result.
kitaqgb/lib/fixed.c:43
s16 kq_abs_s16(s16 v)
{
if (v < 0) return (s16)(0 - v);
return v;
}kq_clamp_s16
座標や速度を、指定した上下限の範囲に収めます。
書式
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi);引数
v制限する符号付き値。
lo含まれる下限値。
hi含まれる上限値。
戻り値
下限未満なら lo、上限超過なら hi、範囲内なら v を返します。
使用条件と制限
lo <= hi を満たす範囲を指定してください。
使用例
result[3]=kq_clamp_s16(25,-10,10);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
25を−10~10に制限して10を得ます。下限側−11→−10と、範囲内3→3も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:44
Clamp to an inclusive interval; callers must provide lo <= hi.
kitaqgb/lib/fixed.c:64
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi)
{
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}kq_max_s16
二つの符号付き16ビット値から大きい方を選びます。
書式
s16 kq_max_s16(s16 a, s16 b);引数
a比較する一つ目の符号付き値。
b比較する二つ目の符号付き値。
戻り値
選択した入力値を返します。
使用条件と制限
符号付きで比較します。同値の場合もその値を返し、入力を書き換えません。
使用例
result[2]=kq_max_s16(-7,4);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
−7と4を比較し、4を得ます。負の結果にはマイナス記号を付けて表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:42
Select the larger signed value.
kitaqgb/lib/fixed.c:57
s16 kq_max_s16(s16 a, s16 b)
{
if (a > b) return a;
return b;
}kq_min_s16
二つの符号付き16ビット値から小さい方を選びます。
書式
s16 kq_min_s16(s16 a, s16 b);引数
a比較する一つ目の符号付き値。
b比較する二つ目の符号付き値。
戻り値
選択した入力値を返します。
使用条件と制限
符号付きで比較します。同値の場合もその値を返し、入力を書き換えません。
使用例
result[1]=kq_min_s16(-7,4);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
−7と4を比較し、-7を得ます。負の結果にはマイナス記号を付けて表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:40
Select the smaller signed value.
kitaqgb/lib/fixed.c:50
s16 kq_min_s16(s16 a, s16 b)
{
if (a < b) return a;
return b;
}kq_point_in_rect
点が矩形の内部にあるかを判定します。左端と上端上の点も内部です。
書式
u8 kq_point_in_rect(s16 x, s16 y, KQRect r);引数
x点のX座標。矩形と同じ座標系を使います。
y点のY座標。
r調べる矩形。構造体を値で渡します。
戻り値
内部なら1、外部なら0です。
使用条件と制限
幅・高さは正数にし、座標と大きさの和を s16 に収めてください。右端・下端は領域に含みません。ゼロ面積を自動的に拒否する処理はありません。
使用例
result[1]=kq_point_in_rect(9,1,a);
for(gy=0;gy<11;gy++){for(gx=0;gx<15;gx++){
inside_a=kq_point_in_rect(gx,gy,a);inside_b=kq_point_in_rect(gx,gy,b);
tile=0;if(inside_a){tile=65;result[3]=result[3]+1;}if(inside_b)tile=66;if(inside_a && inside_b){tile=79;result[4]=result[4]+1;}
m_put(1+gx,2+gy,tile);if(tile!=0)vram_example_color(1+gx,2+gy,1,1,tile==65?1:(tile==66?2:3));
}}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
座標1単位を1タイルとして、A=(1,1)、B=(5,4)、両方8×6の矩形を表示します。Aだけの領域はA、BだけはB、重なり4×3=12マスはOです。Aの右辺x=9は内部に含みません。CGBではAが赤、Bが青、Oが緑です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_geometry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:51
Test a half-open rectangle: left/top edges are included, right/bottom excluded. The coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/fixed.c:85
u8 kq_point_in_rect(s16 x, s16 y, KQRect r)
{
if (x < r.x) return 0;
if (y < r.y) return 0;
if (x >= (s16)(r.x + r.w)) return 0;
if (y >= (s16)(r.y + r.h)) return 0;
return 1;
}kq_rect_intersect
二つの軸に平行な矩形が面積を持って重なるかを判定します。
書式
u8 kq_rect_intersect(KQRect a, KQRect b);引数
a一つ目の矩形。
x, yは左上、w, hは幅・高さです。構造体を値で渡します。b二つ目の矩形。Aと同じ座標系で指定します。
戻り値
重なれば1、離れているか辺が接するだけなら0です。
使用条件と制限
幅・高さは正数にし、座標と大きさの和を s16 に収めてください。右端・下端は領域に含みません。ゼロ面積を自動的に拒否する処理はありません。
使用例
result[0]=kq_rect_intersect(a,b);result[2]=kq_rect_intersect(a,touch);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
座標1単位を1タイルとして、A=(1,1)、B=(5,4)、両方8×6の矩形を表示します。Aだけの領域はA、BだけはB、重なり4×3=12マスはOです。Aの右辺x=9は内部に含みません。CGBではAが赤、Bが青、Oが緑です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_geometry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/fixed.h:48
Test overlap using exclusive right/bottom edges; touching edges do not collide. Require positive dimensions and edge sums that fit s16; zero-sized rectangles are not explicitly rejected and may be reported as overlapping.
kitaqgb/lib/fixed.c:74
u8 kq_rect_intersect(KQRect a, KQRect b)
{
if ((s16)(a.x + a.w) <= b.x) return 0;
if ((s16)(b.x + b.w) <= a.x) return 0;
if ((s16)(a.y + a.h) <= b.y) return 0;
if ((s16)(b.y + b.h) <= a.y) return 0;
return 1;
}input.h — ボタンの現在値・押下・解放・リピート
連続移動は input_down、一度だけの動作は input_pressed、ボタンを離したときの停止は input_released、メニューの繰り返し操作は input_repeat と使い分けます。初期化は一度、更新は入力処理1回につき一度行い、その後は必要なだけ調べます。btn_down・btn_pressed・btn_released・btn_repeat はそれぞれの取得関数の別名です。
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
リピート時間の単位はミリ秒ではなく更新回数です。新たな押下で直ちに出力し、既定の INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 では19回後、以降6回ごとに出力します。各ボタンのカウンタは独立しています。設定を変更する場合は input.c のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
btn_down
btn_downはinput_downを呼ぶ別名です。指定したボタンが現在押されているかを調べます。
mask に含まれるボタンのどれかが、現在押されているかを返します。
書式
u8 btn_down(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (btn_down(BTN_A)!=0) held_ticks++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:48
#define btn_down input_downkitaqgb/lib/input.c:68
u8 input_down(u8 mask)
{
return (u8)((kq_input_keys & mask) != 0);
}btn_pressed
btn_pressedはinput_pressedを呼ぶ別名です。直近の入力更新で新しく押されたボタンを調べます。
直近の入力更新で、mask のボタンが離された状態から押された状態へ変わったかを返します。
書式
u8 btn_pressed(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (btn_pressed(BTN_A)!=0) {
press_count++; seen_press=1;
first_down=btn_down(BTN_A); first_press=btn_pressed(BTN_A);
first_repeat=btn_repeat(BTN_A);
first_current=input_current(); // Only A held: BTN_A, or decimal 16.
first_previous=input_previous(); // No buttons in the preceding sample: 0.
if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
if (first_current!=BTN_A || first_previous!=0) failures++;
if (btn_down(BTN_A|BTN_B)!=1 || btn_down(0)!=0) failures++;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:49
#define btn_pressed input_pressedkitaqgb/lib/input.c:74
u8 input_pressed(u8 mask)
{
return (u8)((kq_input_press_keys & mask) != 0);
}btn_released
btn_releasedはinput_releasedを呼ぶ別名です。直近の入力更新で離されたボタンを調べます。
直近の入力更新で、mask のボタンが押された状態から離された状態へ変わったかを返します。
書式
u8 btn_released(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (btn_released(BTN_A)!=0) {
release_result=btn_released(BTN_A);
last_current=input_current(); last_previous=input_previous();
if (btn_down(BTN_A)!=0 || btn_pressed(BTN_A)!=0 || btn_repeat(BTN_A)!=0) failures++;
break;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:50
#define btn_released input_releasedkitaqgb/lib/input.c:80
u8 input_released(u8 mask)
{
return (u8)((kq_input_release_keys & mask) != 0);
}btn_repeat
btn_repeatはinput_repeatを呼ぶ別名です。押し始め、または長押し中のリピートのタイミングかを調べます。
mask のボタンについて、押し始めまたはキーリピートの発生を返します。
書式
u8 btn_repeat(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
リピート時間の単位はミリ秒ではなく更新回数です。新たな押下で直ちに出力し、既定の INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 では19回後、以降6回ごとに出力します。各ボタンのカウンタは独立しています。設定を変更する場合は input.c のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
使用例
if (btn_repeat(BTN_A)!=0) {
// The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
previous_pulse=held_ticks; repeat_count++;
if (btn_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:51
#define btn_repeat input_repeatkitaqgb/lib/input.c:86
u8 input_repeat(u8 mask)
{
return (u8)((kq_input_repeat_keys & mask) != 0);
}input_current
直近の入力更新で得た、押されている全ボタンのビットマスクを返します。
書式
u8 input_current();戻り値
共通の BTN_* 形式の8ビットマスクです。同時に複数のビットが立つ場合があります。0は該当ボタンなしです。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
first_current=input_current(); // Only A held: BTN_A, or decimal 16.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:44
Return the complete held-button mask from the latest update.
kitaqgb/lib/input.c:92
u8 input_current()
{
return kq_input_keys;
}input_down
mask に含まれるボタンのどれかが、現在押されているかを返します。
書式
u8 input_down(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (input_down(BTN_A)!=0) held_ticks++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:36
Return whether any masked button is currently held.
kitaqgb/lib/input.c:68
u8 input_down(u8 mask)
{
return (u8)((kq_input_keys & mask) != 0);
}input_init
現在・前回・押下・解放・リピートの各マスクを0にし、8個のリピートタイマーを INPUT_REPEAT_DELAY に戻します。コントローラを読まずに入力ライブラリを初期化します。その後の最初の更新で押されているボタンは、新たな押下として扱います。
書式
void input_init();戻り値
ありません(void)。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
リピート時間の単位はミリ秒ではなく更新回数です。新たな押下で直ちに出力し、既定の INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 では19回後、以降6回ごとに出力します。各ボタンのカウンタは独立しています。設定を変更する場合は input.c のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
使用例
input_init();
if (input_current()!=0 || input_previous()!=0) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:31
Clear sampled/edge state and initialize an independent repeat timer for each button.
kitaqgb/lib/input.c:18
void input_init()
{
u8 i;
kq_input_prev_keys = 0;
kq_input_keys = 0;
kq_input_press_keys = 0;
kq_input_release_keys = 0;
kq_input_repeat_keys = 0;
i = 0;
while (i < 8) {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
i++;
}
}input_pressed
直近の入力更新で、mask のボタンが離された状態から押された状態へ変わったかを返します。
書式
u8 input_pressed(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (input_pressed(BTN_A)!=0) {
press_count++; seen_press=1;
first_down=input_down(BTN_A); first_press=input_pressed(BTN_A);
first_repeat=input_repeat(BTN_A);
first_current=input_current(); // Only A held: BTN_A, or decimal 16.
first_previous=input_previous(); // No buttons in the preceding sample: 0.
if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
if (first_current!=BTN_A || first_previous!=0) failures++;
if (input_down(BTN_A|BTN_B)!=1 || input_down(0)!=0) failures++;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:38
Return whether any masked button became pressed in the latest sample.
kitaqgb/lib/input.c:74
u8 input_pressed(u8 mask)
{
return (u8)((kq_input_press_keys & mask) != 0);
}input_previous
直近の入力更新より1回前に保存した、押されていたボタンのビットマスクを返します。
書式
u8 input_previous();戻り値
共通の BTN_* 形式の8ビットマスクです。同時に複数のビットが立つ場合があります。0は該当ボタンなしです。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
first_previous=input_previous(); // No buttons in the preceding sample: 0.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:46
Return the held-button mask that preceded the latest update.
kitaqgb/lib/input.c:98
u8 input_previous()
{
return kq_input_prev_keys;
}input_released
直近の入力更新で、mask のボタンが押された状態から離された状態へ変わったかを返します。
書式
u8 input_released(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
使用例
if (input_released(BTN_A)!=0) {
release_result=input_released(BTN_A);
last_current=input_current(); last_previous=input_previous();
if (input_down(BTN_A)!=0 || input_pressed(BTN_A)!=0 || input_repeat(BTN_A)!=0) failures++;
break;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:40
Return whether any masked button became released in the latest sample.
kitaqgb/lib/input.c:80
u8 input_released(u8 mask)
{
return (u8)((kq_input_release_keys & mask) != 0);
}input_repeat
mask のボタンについて、押し始めまたはキーリピートの発生を返します。
書式
u8 input_repeat(u8 mask);引数
mask一つの
BTN_*、または複数を|で組み合わせたマスクです。選んだボタンのどれかが該当すれば真になり、BTN_A | BTN_Bで両方の同時押しを要求するわけではありません。マスク0は常に0を返します。すべての同時押しは(input_current() & mask) == maskで調べます。
戻り値
選択したボタンのどれかが調べる条件に合えば1、それ以外は0です。戻り値はボタンのビットマスクではありません。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
取得関数は直近の input_update() が保存した値を調べます。入力の再読込、押下イベントの消費、リピートタイマーの更新は行いません。同じ入力処理内で何度調べても結果は同じです。ボタンを押したまま input_update() を2回呼ぶと、2回目には押下イベントが消えます。
リピート時間の単位はミリ秒ではなく更新回数です。新たな押下で直ちに出力し、既定の INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 では19回後、以降6回ごとに出力します。各ボタンのカウンタは独立しています。設定を変更する場合は input.c のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
使用例
if (input_repeat(BTN_A)!=0) {
// The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
previous_pulse=held_ticks; repeat_count++;
if (input_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:42
Return whether any masked button produced a fresh press or scheduled repeat pulse.
kitaqgb/lib/input.c:86
u8 input_repeat(u8 mask)
{
return (u8)((kq_input_repeat_keys & mask) != 0);
}input_update
現在のマスクを前回値として保存し、__readpadex でゲームボーイのボタンを読み、解放されたボタンを求めます。各ボタンの独立したリピートタイマーも1回進めます。ゲームの入力処理1回につき一度呼び、その後に保存された状態を調べてください。
書式
void input_update();戻り値
ありません(void)。
使用条件と制限
共通マスクは BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80 です。ビット1が押下中を表します。GB・FCの input_* はこの形式ですが、FCの生のパッド関数は別のビット順です。
リピート時間の単位はミリ秒ではなく更新回数です。新たな押下で直ちに出力し、既定の INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 では19回後、以降6回ごとに出力します。各ボタンのカウンタは独立しています。設定を変更する場合は input.c のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
使用例
input_update(); // One update per input tick, followed by any number of queries.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
押している状態・押した瞬間・離した瞬間・自動リピートの違いを学ぶプログラムです。Aを40回の更新にわたって押し、離します。FIRST DOWN、FIRST PRESS、FIRST REPEATは1、FIRST CURRENTは16になります。HELD TICKSは40、PRESS COUNTは1、REPEAT COUNTは5です。離すとRELEASEは1、LAST CURRENTは0、LAST PREVIOUSは16になります。FAILED CHECKSは0が正常です。押す長さを変えると回数も変わり、プログラムは実際に観測した長さを基にリピートの間隔を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache実装とソース
kitaqgb/lib/input.h:34
Sample input once per game tick and derive press, release and repeat masks. Repeat delays are measured in calls to this function, not elapsed wall-clock time.
kitaqgb/lib/input.c:35
void input_update()
{
u8 i;
u16 kt;
kq_input_prev_keys = kq_input_keys;
// The intrinsic packs held buttons in the low byte and new presses in the high byte.
kt = __readpadex(kq_input_prev_keys);
kq_input_keys = (u8)kt;
kq_input_press_keys = (u8)(kt >> 8);
kq_input_release_keys = (u8)(kq_input_prev_keys & (u8)(kq_input_keys ^ 0xFF));
kq_input_repeat_keys = kq_input_press_keys;
i = 0;
while (i < 8) {
u8 mask = input_mask_for_index(i);
if ((kq_input_keys & mask) != 0) {
if ((kq_input_press_keys & mask) != 0) {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
} else if (input_repeat_timer[i] != 0) {
input_repeat_timer[i]--;
} else {
kq_input_repeat_keys = (u8)(kq_input_repeat_keys | mask);
input_repeat_timer[i] = INPUT_REPEAT_RATE;
}
} else {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
}
i++;
}
}link.h — シリアル通信と論理パケット
このライブラリは、バイト交換、最大24バイトのパケット、接続先別キューの3層で通信を扱います。まずBYTE MASTER/SLAVEを動かし、次にPACKET、最後にLINK4へ進むと、クロックと受信待ちの関係を確認できます。ハードウェアレジスタ定義、link.c、link_packet.cは同じプログラムに1回ずつ組み込みます。サンプルのgb_link_example.hがこの順序をまとめています。
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
送受信の両側で継続してポーリングします。タイマーはハードウェアのフレーム数ではなく、進展のない呼び出し90回で満了します。ACKを待つための0xFF送信だけでは期限を延長せず、送信は最大3回試みます。再送で同じ内容が届く場合があるため、厳密に1回だけ実行したいコマンドにはアプリ側の識別番号を付けます。
Link4_BeginTransferTo
宛先を選んでから1バイトの交換を開始します。
書式
u8 __stackcall Link4_BeginTransferTo(u8 peer_slot, u8 out);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
out送る8ビット値。
戻り値
選択または開始時の最初のエラー。成功は0です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[6+slot]=Link4_BeginTransferTo(slot,16+slot);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:129
Select the logical peer and start a byte exchange, returning the first error.
kitaqgb/lib/link.c:584
u8 __stackcall Link4_BeginTransferTo(u8 peer_slot, u8 out) {
u8 err = Link4_SelectPeer(peer_slot);
if (err != LINK_ERR_NONE) return err;
return Link_BeginTransfer(out);
}Link4_GetLastRxPeer
最後に完了したバイトを割り当てた相手番号を取得します。
書式
u8 Link4_GetLastRxPeer();戻り値
ホストは選択相手、相手は0。まだ受信していなければ255です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[19]=Link4_GetLastRxPeer();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:121
Return the peer attributed to the last completed byte transfer.
kitaqgb/lib/link.c:540
u8 Link4_GetLastRxPeer() {
return Link4_LastRxPeer;
}Link4_GetLastTxPeer
最後のバイト転送を開始した時点の宛先を取得します。
書式
u8 Link4_GetLastTxPeer();戻り値
記録済みの宛先番号。開始前は255です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[20]=Link4_GetLastTxPeer();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:123
Return the peer attributed when the last byte transfer was started.
kitaqgb/lib/link.c:545
u8 Link4_GetLastTxPeer() {
return Link4_LastTxPeer;
}Link4_GetLocalSlot
自機に設定されたスロット番号を取得します。
書式
u8 Link4_GetLocalSlot();戻り値
ホストは0、相手は1~3です。未設定時も0です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[2]=Link4_GetLocalSlot();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:115
Return the configured zero-based local slot.
kitaqgb/lib/link.c:525
u8 Link4_GetLocalSlot() {
return Link4_LocalSlot;
}Link4_GetMode
論理的な接続の役割を取得します。
書式
u8 Link4_GetMode();戻り値
0=未設定、1=ホスト、2=相手です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[1]=Link4_GetMode();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:113
Return the configured cooperative role; this is not a hardware connection probe.
kitaqgb/lib/link.c:520
u8 Link4_GetMode() {
return Link4_ModeState;
}Link4_GetSelectedPeer
次の交換で使う論理的な宛先を取得します。
書式
u8 Link4_GetSelectedPeer();戻り値
ホストでは選択中の相手、相手では0、未設定では255です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[4]=Link4_GetSelectedPeer();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:119
Return the current logical destination, or the invalid-slot sentinel when unset.
kitaqgb/lib/link.c:535
u8 Link4_GetSelectedPeer() {
return Link4_SelectedPeer;
}Link4_GetSlotCount
設定された総スロット数を取得します。
書式
u8 Link4_GetSlotCount();戻り値
有効な設定は2~4、未設定は0です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
これらは設定や転送履歴の読み取りです。実物の接続検出、全相手への問い合わせ、転送完了待ちは行いません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[3]=Link4_GetSlotCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:117
Return the configured number of logical slots, not a live connection count.
kitaqgb/lib/link.c:530
u8 Link4_GetSlotCount() {
return Link4_SlotCount;
}Link4_HasByteFrom
指定相手の受信リングに未読バイトがあるか調べます。
書式
u8 __stackcall Link4_HasByteFrom(u8 peer_slot);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
戻り値
データがあれば1、空または不正な相手なら0です。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
この問い合わせはハードウェアをポーリングせず、不正な相手でもエラーラッチを変更しません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[9+slot]=Link4_HasByteFrom(slot);value=0;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:131
Inspect a valid peer ring without polling the hardware.
kitaqgb/lib/link.c:591
u8 __stackcall Link4_HasByteFrom(u8 peer_slot) {
if (Link4_IsValidRemoteSlotInternal(peer_slot) == 0) return 0;
if (Link4_RxHead[(__safe_index u8)peer_slot] == Link4_RxTail[(__safe_index u8)peer_slot]) return 0;
return 1;
}Link4_HasPacketFrom
指定相手の完成済みパケットメールボックスを調べます。
書式
u8 __stackcall Link4_HasPacketFrom(u8 peer_slot);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
戻り値
未読があれば1、空または不正な相手なら0です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
この問い合わせはハードウェアをポーリングせず、不正な相手でもエラーラッチを変更しません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[9]=Link4_HasPacketFrom(0);result[10]=Link4_ReadPacketFrom(0,&cmd,&len,data);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4スロットの相手1として、HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42を確認します。CONSUMED=0は相手0のメールボックスを消費した後の値です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:142
Inspect the one-packet mailbox for a valid remote slot without consuming it.
kitaqgb/lib/link_packet.c:484
u8 __stackcall Link4_HasPacketFrom(u8 peer_slot) {
return Link4_PacketPeerReady(peer_slot);
}Link4_InitHost
2~4スロットの論理ホストとして初期化し、最初の相手を1にします。
書式
u8 __stackcall Link4_InitHost(u8 slot_count);引数
slot_countホスト自身を含むスロット数2~4。
戻り値
設定できれば0、slot_countが範囲外なら4です。
使用条件と制限
初期化は受信キュー、パケット、接続先、エラーを破棄し、転送とシリアル割り込みを停止します。送受信中のデータを保存する操作ではありません。
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
設定の妥当性を調べる前に初期化します。失敗しても前の接続設定や未読データは残りません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[0]=Link4_InitHost(4);result[1]=Link4_GetMode();result[2]=Link4_GetLocalSlot();result[3]=Link4_GetSlotCount();result[4]=Link4_GetSelectedPeer();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:108
Cooperative 4-player adapter helpers. This layer models the common host-selected adapter pattern: - Slot 0 is always the host. - The host selects one active peer slot at a time. - Packet mailboxes stay one-deep per peer. - The original Link_* API remains available for normal 2-player projects. Reset as master, validate slot count and select logical peer one. Invalid configuration still discards old link state.
kitaqgb/lib/link.c:489
u8 __stackcall Link4_InitHost(u8 slot_count) {
u8 err;
Link_InitMaster();
err = Link4_ValidateConfig(LINK4_MODE_HOST, LINK4_HOST_SLOT, slot_count);
if (err != LINK_ERR_NONE) {
Link_SetErrorInternal(err);
return err;
}
Link4_ApplyConfig(LINK4_MODE_HOST, LINK4_HOST_SLOT, slot_count);
return LINK_ERR_NONE;
}Link4_InitPeer
指定スロットの外部クロック側として初期化し、相手をホスト0にします。
書式
u8 __stackcall Link4_InitPeer(u8 local_slot, u8 slot_count);引数
local_slot自分の番号1~slot_count−1。
slot_countホストを含む総数2~4。
戻り値
設定できれば0。スロット数や自分の番号が不正なら4です。
使用条件と制限
初期化は受信キュー、パケット、接続先、エラーを破棄し、転送とシリアル割り込みを停止します。送受信中のデータを保存する操作ではありません。
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
設定の妥当性を調べる前に初期化します。失敗しても前の接続設定や未読データは残りません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[0]=Link4_InitPeer(1,4);result[1]=Link4_GetMode();result[2]=Link4_GetLocalSlot();result[3]=Link4_GetSlotCount();result[4]=Link4_GetSelectedPeer();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4スロットの相手1として、HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42を確認します。CONSUMED=0は相手0のメールボックスを消費した後の値です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:111
Reset as slave and validate the local nonzero slot and total slot count. An invalid configuration still discards the previous link state.
kitaqgb/lib/link.c:505
u8 __stackcall Link4_InitPeer(u8 local_slot, u8 slot_count) {
u8 err;
Link_InitSlave();
err = Link4_ValidateConfig(LINK4_MODE_PEER, local_slot, slot_count);
if (err != LINK_ERR_NONE) {
Link_SetErrorInternal(err);
return err;
}
Link4_ApplyConfig(LINK4_MODE_PEER, local_slot, slot_count);
return LINK_ERR_NONE;
}Link4_PollPacket
共通のパケット処理を、現在の接続先に対して1回進めます。
書式
void Link4_PollPacket();戻り値
戻り値はありません。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
送受信の両側で継続してポーリングします。タイマーはハードウェアのフレーム数ではなく、進展のない呼び出し90回で満了します。ACKを待つための0xFF送信だけでは期限を延長せず、送信は最大3回試みます。再送で同じ内容が届く場合があるため、厳密に1回だけ実行したいコマンドにはアプリ側の識別番号を付けます。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
全スロットを順番に選ぶ処理は含まれません。相手を切り替えるのは、送受信が完了して選択可能になってからです。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
for(i=0;i<50;i++){Link4_PollPacket();while(Link4_TryReadByteFrom(slot,0)){}m_wait();}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:140
Advance the shared packet engine; this does not poll every logical peer in turn.
kitaqgb/lib/link_packet.c:479
void Link4_PollPacket() {
Link_PollPacket();
}Link4_ReadPacketFrom
指定相手のパケットをコピーし、そのメールボックスを消費します。
書式
u8 __stackcall Link4_ReadPacketFrom(u8 peer_slot, u8 *cmd, u8 *len, u8 *dst);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
cmd受信コマンドを書き込む1バイト。0なら省略します。
len受信長0~24を書き込む1バイト。0なら省略します。
dst最大24バイトの書き込み先。0なら内容を読み捨てます。
戻り値
取得できれば1、空または不正な相手なら0です。
使用条件と制限
受信先の容量を渡す引数はありません。dstを使う場合は最大長24バイトの書き込み可能な領域を用意します。cmd・len・dstは個別に0を渡して省略できます。空き通知と読み取りを別スレッドや割り込みで競合させないでください。
通常メールボックスがこの相手のパケットを指している場合だけ、通常側も消します。他の相手の未読パケットは保持します。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[9]=Link4_HasPacketFrom(0);result[10]=Link4_ReadPacketFrom(0,&cmd,&len,data);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4スロットの相手1として、HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42を確認します。CONSUMED=0は相手0のメールボックスを消費した後の値です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:146
Consume a peer mailbox, copying to optional outputs. A nonnull destination needs room for the entire payload. Clear the shared mailbox only when it refers to this same peer.
kitaqgb/lib/link_packet.c:491
u8 __stackcall Link4_ReadPacketFrom(u8 peer_slot, u8 *cmd, u8 *len, u8 *dst) {
u8 i;
u8 offset;
if (Link4_PacketPeerReady(peer_slot) == 0) return 0;
if (cmd != 0) *cmd = Link4_ReceivedPacketCmd[(__safe_index u8)peer_slot];
if (len != 0) *len = Link4_ReceivedPacketLen[(__safe_index u8)peer_slot];
if (dst != 0) {
for (i = 0; i < Link4_ReceivedPacketLen[(__safe_index u8)peer_slot]; ++i) {
offset = Link4_PacketOffset(peer_slot, i);
dst[(__safe_index u8)i] = Link4_ReceivedPacketBuf[(__safe_index u8)offset];
}
}
Link4_ReceivedPacketReady[(__safe_index u8)peer_slot] = 0;
if (Link4_LastPacketPeer == peer_slot) {
Link_ReceivedPacketReady = 0;
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
}
return 1;
}Link4_SelectPeer
未完了の交換がないことを確かめ、次の論理的な宛先を変更します。
書式
u8 __stackcall Link4_SelectPeer(u8 peer_slot);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
戻り値
選択できれば0、バイト/パケット/ACKが使用中なら1、未設定や不正な相手なら4です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[25]=Link4_SelectPeer(1);result[26]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:127
Poll completion, then change the logical destination only while byte and packet state are idle. A peer can select only the host. No adapter-routing command is sent here; the transport must provide the matching physical route.
kitaqgb/lib/link.c:552
u8 __stackcall Link4_SelectPeer(u8 peer_slot) {
Link_Poll();
if (Link4_IsEnabledInternal() == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (Link_Busy != 0 || Link_PacketState != LINK_PKT_STATE_IDLE || Link_PendingAck != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
if (Link4_ModeState == LINK4_MODE_HOST) {
if (Link4_IsValidRemoteSlotInternal(peer_slot) == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
Link4_SelectedPeer = peer_slot;
return LINK_ERR_NONE;
}
if (Link4_ModeState == LINK4_MODE_PEER && peer_slot == LINK4_HOST_SLOT) {
Link4_SelectedPeer = LINK4_HOST_SLOT;
return LINK_ERR_NONE;
}
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}Link4_SendPacketTo
宛先を選択し、その相手へのパケットをコピーして予約します。
書式
u8 __stackcall Link4_SendPacketTo(u8 peer_slot, const u8 *data, u8 len, u8 cmd);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
datalenバイトを読み取る元。len=0のときだけ0を渡せます。
len内容の長さ0~24バイト。
cmd受信側アプリが意味を定める8ビットのコマンド。
戻り値
選択または予約時の最初のエラー。受付は0です。
使用条件と制限
Link4はホスト0と、1~3の相手を論理的に管理します。SelectPeerは電気的な配線を切り替えるコマンドを送りません。KOKURAのlink4接続ではコンパイラのメタデータを使って接続先を追従させます。DMG-07実機のプロトコルにはLinkDmg07を使います。
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
送受信の両側で継続してポーリングします。タイマーはハードウェアのフレーム数ではなく、進展のない呼び出し90回で満了します。ACKを待つための0xFF送信だけでは期限を延長せず、送信は最大3回試みます。再送で同じ内容が届く場合があるため、厳密に1回だけ実行したいコマンドにはアプリ側の識別番号を付けます。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
選択に成功してから長さ等を調べるため、内容が不正で予約に失敗しても、宛先だけは変更される場合があります。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[21+slot]=Link4_SendPacketTo(slot,data,3,64+slot);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:138
Select a logical peer, then copy and schedule a packet; return the first error.
kitaqgb/lib/link_packet.c:472
u8 __stackcall Link4_SendPacketTo(u8 peer_slot, const u8 *data, u8 len, u8 cmd) {
u8 err = Link4_SelectPeer(peer_slot);
if (err != LINK_ERR_NONE) return err;
return Link_SendPacket(data, len, cmd);
}Link4_TryReadByteAny
番号の小さい相手から順に調べ、最初に見つかった1バイトを取り出します。
書式
u8 __stackcall Link4_TryReadByteAny(u8 *peer_slot, u8 *out);引数
peer_slot受信元を書き込む1バイト。0なら番号の出力を省略します。
out受信値を書き込む1バイトの領域。0なら読み捨てます。
戻り値
取得できれば1、どこにもなければ0です。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
自分のスロットは飛ばします。番号の小さい相手に未読が続くと大きい相手より先に返るため、公平な巡回が必要なら相手別の取得を使います。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
else {other=255;Link4_TryReadByteAny(&other,&value);result[12+slot]=other;}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:136
Scan remote rings in increasing slot order and return the first available byte. Null output pointers are allowed; lower slots have polling priority.
kitaqgb/lib/link.c:604
u8 __stackcall Link4_TryReadByteAny(u8 *peer_slot, u8 *out) {
u8 slot;
if (Link4_IsEnabledInternal() == 0) return 0;
for (slot = 0; slot < Link4_SlotCount; ++slot) {
if (slot != Link4_LocalSlot) {
if (Link4_PopRxByte(slot, out) != 0) {
if (peer_slot != 0) *peer_slot = slot;
return 1;
}
}
}
return 0;
}Link4_TryReadByteFrom
指定相手の受信リングから1バイトを取り出します。
書式
u8 __stackcall Link4_TryReadByteFrom(u8 peer_slot, u8 *out);引数
peer_slot設定済みの自分以外のスロット。ホストからは1~slot_count−1、相手からは0を指定します。
out受信値を書き込む1バイトの領域。0なら読み捨てます。
戻り値
取り出せれば1。空または不正な相手なら0で、outはそのままです。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
if(slot==1)result[13]=Link4_TryReadByteFrom(slot,&value);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのROMをKOKURAのlink4で接続します。ホストは相手1/2/3から161/162/163を受け取り、LAST PEER=3になります。続いて各相手へコマンド65/66/67と7/42/255を送ります。各相手の画面でも内容を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png実装とソース
kitaqgb/lib/link.h:133
Pop one byte from the selected peer ring; null output discards it.
kitaqgb/lib/link.c:598
u8 __stackcall Link4_TryReadByteFrom(u8 peer_slot, u8 *out) {
return Link4_PopRxByte(peer_slot, out);
}Link_BeginTransfer
1バイトをSBへ置き、1回の全二重交換を開始または待ち受けます。
書式
u8 __stackcall Link_BeginTransfer(u8 out);引数
out相手へ送る8ビット値。受信だけが目的でも値を1つ送ります。
戻り値
開始できれば0、すでに使用中なら1です。
使用条件と制限
ライブラリは1台につき1つの通信状態を共有します。バイト送受信は全二重で、片側が内部クロック、もう片側が外部クロックを使います。通常速度は1ビット512クロック、CGB高速は16クロックです。
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
外部クロックの役割では相手がクロックを送るまで完了しません。使用中の再呼び出しは、送信中のバイトを置き換えません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
lesson_delay(12);result[0]=Link_BeginTransfer(42);value=0;result[1]=Link_WaitByte(8,&value);result[2]=value;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE SLAVEと接続します。42を送り99を受け取り、次に双方で0を交換します。RECEIVED=1、PEER BYTE=99、ZERO READY=1、ZERO BYTE=0が、受信した0と未受信の違いを示します。CGB同士では2回目を高速クロックで行います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:66
Start one full-duplex byte exchange, or return BUSY without replacing an active transfer. Slave mode arms reception and waits for external clocks.
kitaqgb/lib/link.c:362
u8 __stackcall Link_BeginTransfer(u8 out) {
u8 sc = LINK_SC_START;
if (Link_Busy != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
LinkHw_WriteSB(out);
Link_LastTx = out;
Link4_LastTxPeer = Link4_CurrentTransferPeerInternal();
Link_Busy = 1;
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
if (Link_Mode == LINK_MODE_MASTER) {
sc = (u8)(sc | LINK_SC_INTERNAL);
if (Link_FastClock != 0) sc = (u8)(sc | LINK_SC_FAST);
}
LinkHw_WriteSC(sc);
return LINK_ERR_NONE;
}Link_Cancel
実行中または外部クロック待ちのバイト転送を停止します。
書式
void Link_Cancel();戻り値
戻り値はありません。
使用条件と制限
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
SCを0にし、使用中とシリアル要求を消します。受信済みバイト、パケット状態、エラーは消しません。プロトコルを最初からやり直す場合は初期化も行います。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_Cancel();result[12]=Link_IsBusy();result[13]=(SC&128)!=0;Link_ClearError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:71
Stop the hardware transfer under interrupt protection. Queued receive bytes and packet-layer state remain available; this is not a full protocol reset.
kitaqgb/lib/link.c:396
void Link_Cancel() {
u8 tok = __critical_enter();
Link_Busy = 0;
LinkHw_WriteSC(0x00);
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
__critical_leave(tok);
}Link_ClearError
エラー表示だけを0に戻します。
書式
void Link_ClearError();戻り値
戻り値はありません。
使用条件と制限
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
転送、キュー、パケットの状態はそのままです。停止が必要ならLink_Cancel、全体を破棄するなら初期化を使います。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_ClearError();result[8]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:62
Clear the error latch without resetting queues or an active transfer.
kitaqgb/lib/link.c:356
void Link_ClearError() {
Link_LastErrorCode = LINK_ERR_NONE;
}Link_HasByte
通常受信リングに未読バイトがあるか調べます。
書式
u8 Link_HasByte();戻り値
未読があれば1、空なら0です。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
ハードウェアをポーリングしません。Link_Pollまたはシリアルハンドラーで転送完了を先に取り込みます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[1]=Link_HasByte();value=77;result[2]=Link_TryReadByte(&value);result[3]=value;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE MASTERがクロックを供給します。PEER BYTE=42、ZERO READY=1、ZERO BYTE=0になります。その後のEMPTY READも0になるため、空かどうかは準備フラグかTryReadの戻り値で判断します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:58
Read the ordinary receive-ready flag without polling hardware.
kitaqgb/lib/link.c:346
u8 Link_HasByte() {
return Link_RxReady;
}Link_HasPacket
通常の完成済みパケットメールボックスを調べます。
書式
u8 Link_HasPacket();戻り値
未読パケットがあれば1、空なら0です。
使用条件と制限
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
プロトコルを進めず、フラグだけを読みます。送信相手がACKを受け取ったかどうかを示すフラグではありません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[0]=Link_HasPacket();cmd=0;len=0;result[1]=Link_ReadPacket(&cmd,&len,data);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のPACKET MASTERと接続し、COMMAND=49、LENGTH=3、PAYLOAD=7/42/255を確認します。ReadPacket後のCONSUMED=0は、メールボックスが空になったことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png実装とソース
kitaqgb/lib/link.h:94
Inspect the shared completed-packet flag without advancing the protocol.
kitaqgb/lib/link_packet.c:441
u8 Link_HasPacket() {
return Link_ReceivedPacketReady;
}Link_InitCommon
通信状態を初期値へ戻し、外部クロック待ちの役割にします。
書式
void Link_InitCommon();戻り値
戻り値はありません。
使用条件と制限
ライブラリは1台につき1つの通信状態を共有します。バイト送受信は全二重で、片側が内部クロック、もう片側が外部クロックを使います。通常速度は1ビット512クロック、CGB高速は16クロックです。
初期化は受信キュー、パケット、接続先、エラーを破棄し、転送とシリアル割り込みを停止します。送受信中のデータを保存する操作ではありません。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_InitCommon();result[0]=Link_IsBusy();result[1]=Link_HasByte();result[2]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:43
Reset byte queues, packet mailboxes, cooperative-peer state and errors, then disable serial transfers and serial IRQ delivery. Existing data is discarded.
kitaqgb/lib/link.c:243
void Link_InitCommon() {
u8 i;
Link_Mode = LINK_MODE_SLAVE;
Link_UseInterrupt = 0;
Link_FastClock = LINK_CLOCK_NORMAL;
Link_Busy = 0;
Link_LastRx = 0xFF;
Link_LastTx = 0xFF;
Link_RxReady = 0;
Link_LastErrorCode = LINK_ERR_NONE;
Link_PacketState = LINK_PKT_STATE_IDLE;
Link_TxSeq = 0;
Link_RxSeq = 0;
Link4_ModeState = LINK4_MODE_NONE;
Link4_LocalSlot = LINK4_HOST_SLOT;
Link4_SlotCount = 0;
Link4_SelectedPeer = LINK4_INVALID_SLOT;
Link4_LastRxPeer = LINK4_INVALID_SLOT;
Link4_LastTxPeer = LINK4_INVALID_SLOT;
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
Link_RxHead = 0;
Link_RxTail = 0;
Link_TxHead = 0;
Link_TxTail = 0;
Link_CurrentPacketCmd = 0;
Link_CurrentPacketLen = 0;
Link_CurrentPacketChecksum = 0;
Link_PendingAck = 0;
Link_ReceivedPacketReady = 0;
Link_ReceivedPacketCmd = 0;
Link_ReceivedPacketLen = 0;
Link_ReceivedPacketChecksum = 0;
Link_PacketRetryCount = 0;
Link_PacketTimer = 0;
for (i = 0; i < LINK_RX_QUEUE_SIZE; ++i) Link_RxQueue[(__safe_index u8)i] = 0;
for (i = 0; i < LINK_TX_QUEUE_SIZE; ++i) Link_TxQueue[(__safe_index u8)i] = 0;
for (i = 0; i < LINK4_MAX_PLAYERS; ++i) {
Link4_RxHead[(__safe_index u8)i] = 0;
Link4_RxTail[(__safe_index u8)i] = 0;
Link4_ReceivedPacketReady[(__safe_index u8)i] = 0;
Link4_ReceivedPacketCmd[(__safe_index u8)i] = 0;
Link4_ReceivedPacketLen[(__safe_index u8)i] = 0;
}
for (i = 0; i < (u8)(LINK4_MAX_PLAYERS * LINK_RX_QUEUE_SIZE); ++i) {
Link4_RxQueue[(__safe_index u8)i] = 0;
}
for (i = 0; i < LINK_PKT_MAX_PAYLOAD; ++i) {
Link_CurrentPacketBuf[(__safe_index u8)i] = 0;
Link_ReceivedPacketBuf[(__safe_index u8)i] = 0;
}
for (i = 0; i < (u8)(LINK4_MAX_PLAYERS * LINK_PKT_MAX_PAYLOAD); ++i) {
Link4_ReceivedPacketBuf[(__safe_index u8)i] = 0;
}
LinkHw_WriteSB(0xFF);
LinkHw_WriteSC(0x00);
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
Link_SetSerialInterruptEnabled(0);
}Link_InitMaster
状態を初期化し、自機がクロックを供給する役割を選びます。
書式
void Link_InitMaster();戻り値
戻り値はありません。
使用条件と制限
ライブラリは1台につき1つの通信状態を共有します。バイト送受信は全二重で、片側が内部クロック、もう片側が外部クロックを使います。通常速度は1ビット512クロック、CGB高速は16クロックです。
初期化は受信キュー、パケット、接続先、エラーを破棄し、転送とシリアル割り込みを停止します。送受信中のデータを保存する操作ではありません。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_InitMaster();Link_SetUseInterrupt(0);Link_SetFastClock(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE SLAVEと接続します。42を送り99を受け取り、次に双方で0を交換します。RECEIVED=1、PEER BYTE=99、ZERO READY=1、ZERO BYTE=0が、受信した0と未受信の違いを示します。CGB同士では2回目を高速クロックで行います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:45
Reset link state and select the internal-clock role for subsequent transfers.
kitaqgb/lib/link.c:309
void Link_InitMaster() {
Link_InitCommon();
Link_Mode = LINK_MODE_MASTER;
}Link_InitSlave
状態を初期化し、相手から来るクロックで通信する役割を選びます。
書式
void Link_InitSlave();戻り値
戻り値はありません。
使用条件と制限
ライブラリは1台につき1つの通信状態を共有します。バイト送受信は全二重で、片側が内部クロック、もう片側が外部クロックを使います。通常速度は1ビット512クロック、CGB高速は16クロックです。
初期化は受信キュー、パケット、接続先、エラーを破棄し、転送とシリアル割り込みを停止します。送受信中のデータを保存する操作ではありません。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_InitSlave();result[0]=Link_BeginTransfer(99);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE MASTERがクロックを供給します。PEER BYTE=42、ZERO READY=1、ZERO BYTE=0になります。その後のEMPTY READも0になるため、空かどうかは準備フラグかTryReadの戻り値で判断します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:47
Reset link state and select the external-clock role for subsequent transfers.
kitaqgb/lib/link.c:315
void Link_InitSlave() {
Link_InitCommon();
Link_Mode = LINK_MODE_SLAVE;
}Link_IsBusy
完了済みの転送を一度取り込んでから、送受信中か返します。
書式
u8 Link_IsBusy();戻り値
1なら使用中、0ならアイドルです。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
単なる状態読み取りではありません。完了していればSBをキューへ入れ、受信準備フラグやエラーも変わり得ます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[3]=Link_IsBusy();result[4]=Link_HasByte();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE SLAVEと接続します。42を送り99を受け取り、次に双方で0を交換します。RECEIVED=1、PEER BYTE=99、ZERO READY=1、ZERO BYTE=0が、受信した0と未受信の違いを示します。CGB同士では2回目を高速クロックで行います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:56
Poll once before returning busy state, so this query may collect a completed byte.
kitaqgb/lib/link.c:340
u8 Link_IsBusy() {
Link_Poll();
return Link_Busy;
}Link_LastError
最後に記録された通信エラーを消さずに取得します。
書式
u8 Link_LastError();戻り値
現在のエラーコード0~5です。
使用条件と制限
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[2]=Link_LastError();Link_ClearError();result[3]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
コマンド49と3バイト7/42/255を送り、送信直後に元の配列を変更します。QUEUED=0、重複開始はSECOND SEND=1です。この使用中エラーは送信成功後も残り、ClearErrorで0にします。受信側のPAYLOAD 2=42が、内容をコピーしていることの確認になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png実装とソース
kitaqgb/lib/link.h:60
Return the latest latched error without clearing it.
kitaqgb/lib/link.c:351
u8 Link_LastError() {
return Link_LastErrorCode;
}Link_OnSerialIRQ
シリアル要求を確認済みのハンドラーから、完了したバイトを取り込みます。
書式
void Link_OnSerialIRQ();戻り値
戻り値はありません。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
この関数自身はSCの完了ビットを調べません。転送がまだ進行中のタイマー割り込み等から呼ばないでください。例は実際のIF.serial要求をCPU割り込み禁止中に確認して明示呼び出しし、関数本体を検証します。ベクターの設置例ではありません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
lesson_delay(2);result[14]=(IF_REG&8)!=0;Link_OnSerialIRQ();result[15]=Link_HasByte();result[16]=Link_ReadByte();result[17]=(IF_REG&8)!=0;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:74
Acknowledge serial IRQ and finish a software-busy transfer. Call this from the actual serial interrupt handler, not an unrelated interrupt.
kitaqgb/lib/link.c:410
void Link_OnSerialIRQ() {
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
if (Link_Busy == 0) return;
Link_FinishTransfer();
}Link_Poll
SCの開始ビットが消えた転送だけを取り込み、受信キューへ追加します。
書式
void Link_Poll();戻り値
戻り値はありません。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
1回の呼び出しで待ち続けることはありません。ソフトウェア上でアイドルなら何もしません。タイムアウトも設定しません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
for(i=0;i<24;i++){Link_Poll();if(Link_HasByte())break;m_wait();}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE MASTERがクロックを供給します。PEER BYTE=42、ZERO READY=1、ZERO BYTE=0になります。その後のEMPTY READも0になるため、空かどうかは準備フラグかTryReadの戻り値で判断します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:68
Collect a pending transfer only after hardware clears the SC start bit.
kitaqgb/lib/link.c:388
void Link_Poll() {
if (Link_Busy == 0) return;
if ((LinkHw_ReadSC() & LINK_SC_START) != 0) return;
Link_FinishTransfer();
}Link_PollPacket
受信バイトを解析し、次の送信またはACKを開始して、期限と再試行を進めます。
書式
void Link_PollPacket();戻り値
戻り値はありません。
使用条件と制限
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
送受信の両側で継続してポーリングします。タイマーはハードウェアのフレーム数ではなく、進展のない呼び出し90回で満了します。ACKを待つための0xFF送信だけでは期限を延長せず、送信は最大3回試みます。再送で同じ内容が届く場合があるため、厳密に1回だけ実行したいコマンドにはアプリ側の識別番号を付けます。
同じ受信リングを直接のReadByte等でも読むと、パケットの途中を消費してしまいます。通常の生バイト読出しとパケット受信を同時に混ぜないでください。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_InitSlave();for(i=0;i<120;i++){Link_PollPacket();m_wait();}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のPACKET MASTERと接続し、COMMAND=49、LENGTH=3、PAYLOAD=7/42/255を確認します。ReadPacket後のCONSUMED=0は、メールボックスが空になったことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png使用例
Link_InitMaster();result[0]=Link_SendPacket(0,0,7);
// An absent peer cannot acknowledge the packet, despite completed FF exchanges.
for(ticks=0;ticks<300;ticks++){Link_PollPacket();m_wait();}
result[1]=Link_LastError();Link_Cancel();result[2]=Link_IsBusy();Link_ClearError();result[3]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
相手を接続せずにパケットを送ります。内部クロックのバイト転送自体は完了してもACKが来ないため、再試行後にNO ACK ERROR=5になります。Cancel後はCANCEL BUSY=0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_deadline.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_deadline.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_deadline.gb --hardware cgb --run-frames 330 --png .\out\deadline.png実装とソース
kitaqgb/lib/link.h:92
Drain received bytes, advance one outgoing exchange and update the retry timer. Timeout ticks count calls without progress, not hardware frames; call with a controlled cadence and avoid mixing raw reads with packet consumption.
kitaqgb/lib/link_packet.c:400
void Link_PollPacket() {
u8 progress = 0;
u8 previous_state;
u8 value = 0;
Link_Poll();
if (Link_PacketState == LINK_PKT_STATE_ERROR) {
Link_PacketGoIdle();
return;
}
while (Link_TryReadByte(&value) != 0) {
if (Link_PacketConsumeByte(value) != 0) progress = 1;
}
if (Link_PacketState == LINK_PKT_STATE_SEND_ACK) {
if (Link_PacketStartAckTransfer() != 0) progress = 1;
} else if (Link_PacketIsSendState() != 0) {
// Clocking an ACK is not protocol progress: a missing peer must time out.
previous_state = Link_PacketState;
if (Link_PacketStartNextTransfer() != 0 && previous_state != LINK_PKT_STATE_WAIT_ACK) progress = 1;
} else {
if (Link_PacketStartReceiveTransfer() != 0) progress = 1;
}
if (Link_PacketState == LINK_PKT_STATE_IDLE) {
Link_PacketTimer = 0;
return;
}
if (progress != 0) {
Link_PacketTimer = LINK_PKT_TIMEOUT_TICKS;
return;
}
if (Link_PacketTimer != 0) Link_PacketTimer--;
if (Link_PacketTimer == 0) Link_PacketHandleTimeout();
}Link_ReadByte
通常受信リングから1バイトを値として取り出します。
書式
u8 Link_ReadByte();戻り値
受信値を返します。空の場合も0です。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
0を受信したのか空なのか、この戻り値だけでは判断できません。TryReadByteの成功値、または読み取り前のHasByteを使います。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[6]=Link_HasByte();result[7]=Link_ReadByte();result[8]=Link_ReadByte();result[9]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE MASTERがクロックを供給します。PEER BYTE=42、ZERO READY=1、ZERO BYTE=0になります。その後のEMPTY READも0になるため、空かどうかは準備フラグかTryReadの戻り値で判断します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:80
Pop one byte, returning zero when empty. Use Link_TryReadByte when a received zero byte must be distinguished from an empty queue.
kitaqgb/lib/link.c:427
u8 Link_ReadByte() {
u8 value = 0;
if (Link_PopRxByte(&value) != 0) return value;
return 0;
}Link_ReadPacket
完成済みパケットをコピーし、通常メールボックスを空にします。
書式
u8 __stackcall Link_ReadPacket(u8 *cmd, u8 *len, u8 *dst);引数
cmd受信コマンドを書き込む1バイト。0なら省略します。
len受信長0~24を書き込む1バイト。0なら省略します。
dst最大24バイトの書き込み先。0なら内容を読み捨てます。
戻り値
取り出せれば1、空なら0。空では出力を書き換えません。
使用条件と制限
受信先の容量を渡す引数はありません。dstを使う場合は最大長24バイトの書き込み可能な領域を用意します。cmd・len・dstは個別に0を渡して省略できます。空き通知と読み取りを別スレッドや割り込みで競合させないでください。
Link4の場合は、この共有パケットと同じ相手のメールボックスも空になります。他の相手のメールボックスは残ります。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[0]=Link_HasPacket();cmd=0;len=0;result[1]=Link_ReadPacket(&cmd,&len,data);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のPACKET MASTERと接続し、COMMAND=49、LENGTH=3、PAYLOAD=7/42/255を確認します。ReadPacket後のCONSUMED=0は、メールボックスが空になったことを示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png実装とソース
kitaqgb/lib/link.h:98
Consume the shared packet mailbox and its matching peer flag. Any output may be null; a nonnull destination needs room for the received payload because there is no destination-capacity argument.
kitaqgb/lib/link_packet.c:448
u8 __stackcall Link_ReadPacket(u8 *cmd, u8 *len, u8 *dst) {
u8 i;
u8 peer_slot = Link4_LastPacketPeer;
if (Link_ReceivedPacketReady == 0) return 0;
if (cmd != 0) *cmd = Link_ReceivedPacketCmd;
if (len != 0) *len = Link_ReceivedPacketLen;
if (dst != 0) {
for (i = 0; i < Link_ReceivedPacketLen; ++i) {
dst[(__safe_index u8)i] = Link_ReceivedPacketBuf[(__safe_index u8)i];
}
}
Link_ReceivedPacketReady = 0;
if (Link4_IsValidRemoteSlotInternal(peer_slot) != 0) {
Link4_ReceivedPacketReady[(__safe_index u8)peer_slot] = 0;
}
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
return 1;
}Link_SendPacket
送信内容を内部バッファへコピーし、パケット送信を予約します。
書式
u8 __stackcall Link_SendPacket(const u8 *data, u8 len, u8 cmd);引数
datalenバイトを読み取る元。len=0のときだけ0を渡せます。
len内容の長さ0~24バイト。
cmd受信側アプリが意味を定める8ビットのコマンド。
戻り値
受付なら0、送信状態が空いていなければ1、不正な長さやポインターなら4です。
使用条件と制限
エラーコードは0=正常、1=使用中、2=時間切れ、3=受信容量不足、4=設定またはプロトコル不正、5=ACK待ちで切断を示す応答です。開始・設定関数の成功は0、受信取得関数の成功は1なので区別してください。成功しても過去のエラーは消えません。
パケットは0xA5、コマンド、長さ、0~24バイトの内容、XORチェックサムの順で送り、受信確認0x79(ACK)または受信拒否0x1F(NAK)を返します。チェックサムはコマンド・長さ・内容のXORです。各機の通常メールボックスは1パケット、Link4は相手ごとにも1パケットを保持します。
送受信の両側で継続してポーリングします。タイマーはハードウェアのフレーム数ではなく、進展のない呼び出し90回で満了します。ACKを待つための0xFF送信だけでは期限を延長せず、送信は最大3回試みます。再送で同じ内容が届く場合があるため、厳密に1回だけ実行したいコマンドにはアプリ側の識別番号を付けます。
0は送信の受付です。相手による受信やACKの完了を意味しません。送信元配列は受付後に再利用できます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[0]=Link_SendPacket(data,3,0x31);data[1]=0;result[1]=Link_SendPacket(data,3,0x32);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
コマンド49と3バイト7/42/255を送り、送信直後に元の配列を変更します。QUEUED=0、重複開始はSECOND SEND=1です。この使用中エラーは送信成功後も残り、ClearErrorで0にします。受信側のPAYLOAD 2=42が、内容をコピーしていることの確認になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png実装とソース
kitaqgb/lib/link.h:88
Validate payload size and idle packet state, copy payload bytes into owned storage and schedule transmission. Return success means queued, not acknowledged; keep polling to progress the exchange and inspect the error latch.
kitaqgb/lib/link_packet.c:361
u8 __stackcall Link_SendPacket(const u8 *data, u8 len, u8 cmd) {
u8 i;
u8 checksum = (u8)(cmd ^ len);
if (len > LINK_PKT_MAX_PAYLOAD) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (len != 0 && data == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (Link_PacketState != LINK_PKT_STATE_IDLE || Link_PendingAck != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
Link_CurrentPacketCmd = cmd;
Link_CurrentPacketLen = len;
for (i = 0; i < len; ++i) {
Link_CurrentPacketBuf[(__safe_index u8)i] = data[(__safe_index u8)i];
checksum = (u8)(checksum ^ data[(__safe_index u8)i]);
}
Link_CurrentPacketChecksum = checksum;
Link_TxSeq = 0;
Link_RxSeq = 0;
Link_PacketRetryCount = 0;
Link_PacketTimer = LINK_PKT_TIMEOUT_TICKS;
Link_PacketState = LINK_PKT_STATE_SEND_SYNC;
return LINK_ERR_NONE;
}Link_SetFastClock
以後の内部クロック転送でCGB高速ビットを使うか選びます。
書式
void __stackcall Link_SetFastClock(u8 on);引数
on0で通常、0以外で高速を要求します。
戻り値
戻り値はありません。
使用条件と制限
ライブラリは1台につき1つの通信状態を共有します。バイト送受信は全二重で、片側が内部クロック、もう片側が外部クロックを使います。通常速度は1ビット512クロック、CGB高速は16クロックです。
転送中の速度は変更しません。外部クロック側ではこの設定を使いません。CGB対応を調べる機能はなく、サンプルは両側を同じ機種で動かします。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
lesson_delay(12);Link_SetFastClock(__cgb_is_cgb());result[5]=Link_BeginTransfer(0);value=77;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE SLAVEと接続します。42を送り99を受け取り、次に双方で0を交換します。RECEIVED=1、PEER BYTE=99、ZERO READY=1、ZERO BYTE=0が、受信した0と未受信の違いを示します。CGB同士では2回目を高速クロックで行います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:53
Select the fast-clock bit for future master transfers; this does not restart an active transfer or verify that the hardware supports CGB fast mode.
kitaqgb/lib/link.c:334
void __stackcall Link_SetFastClock(u8 on) {
if (on != 0) Link_FastClock = LINK_CLOCK_FAST;
else Link_FastClock = LINK_CLOCK_NORMAL;
}Link_SetUseInterrupt
シリアル割り込みの許可ビットを設定し、保留中の要求を消します。
書式
void __stackcall Link_SetUseInterrupt(u8 on);引数
on0で無効、0以外で有効にします。
戻り値
戻り値はありません。
使用条件と制限
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
割り込みベクター0058hのハンドラーは別途必要です。ライブラリは自動登録しません。サンプルは転送開始前に許可を戻し、実際のCPU割り込み入口は検証していません。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
Link_SetUseInterrupt(1);result[3]=(IE_REG&8)!=0;Link_SetUseInterrupt(0);result[4]=(IE_REG&8)!=0;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:50
Enable or disable serial IRQ delivery and clear its pending flag. Enabling does not install the handler. Leaving its outer critical section enables CPU interrupts.
kitaqgb/lib/link.c:322
void __stackcall Link_SetUseInterrupt(u8 on) {
if (on != 0) {
Link_UseInterrupt = 1;
Link_SetSerialInterruptEnabled(1);
} else {
Link_UseInterrupt = 0;
Link_SetSerialInterruptEnabled(0);
}
}Link_TryReadByte
通常受信リングの先頭を、待たずに1バイト取り出します。
書式
u8 __stackcall Link_TryReadByte(u8 *out);引数
out受信値を書き込む1バイトの領域。0なら読み捨てます。
戻り値
取り出せれば1、空なら0。空の場合outは変更しません。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
内部のクリティカルセクションはソフトウェアの入れ子深さを使います。外側のセクションを抜けるとCPU割り込みが有効になるため、直前のIME状態の保存は前提にしないでください。
Link4では同じ受信バイトを通常リングと相手別リングの両方へ入れます。片方を読んでも他方は減りません。パケットのポーリングは通常リングを消費するので、不要な相手別の生バイトも定期的に読み捨て、満杯を防ぎます。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[1]=Link_HasByte();value=77;result[2]=Link_TryReadByte(&value);result[3]=value;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同梱のBYTE MASTERがクロックを供給します。PEER BYTE=42、ZERO READY=1、ZERO BYTE=0になります。その後のEMPTY READも0になるため、空かどうかは準備フラグかTryReadの戻り値で判断します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png実装とソース
kitaqgb/lib/link.h:77
Pop the ordinary receive queue without waiting or polling hardware; null discards a byte.
kitaqgb/lib/link.c:417
u8 __stackcall Link_TryReadByte(u8 *out) {
return Link_PopRxByte(out);
}Link_WaitByte
直ちにポーリングし、必要ならVBlank単位で期限付き受信待ちをします。
書式
u8 __stackcall Link_WaitByte(u16 timeout_frames, u8 *out);引数
timeout_frames最大のVBlank待ち回数。0でも最初のポーリングと取得を行います。
out必須の1バイト書き込み先。0なら待たず、エラーも変更せず0を返します。
戻り値
取得できれば1。時間切れ、またはout=0なら0です。
使用条件と制限
受信リングは8要素中7バイトを保持できます。満杯では新しいバイトを捨て、エラー3を記録します。受信値0も有効なデータです。
LCDを動作させて使います。時間切れはエラー2を記録し、outを変更しません。転送そのものは停止しないため、終了する場合はCancelします。
掲載画像はKOKURA上の結果です。複数台を使う通信例では、リンクしたエミュレータで送受信値を照合し、その終了状態から画面を取得しています。実機ケーブルや実物の4人用アダプターを使った検証ではありません。
コマンドはkitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親でPowerShellを開いて実行します。接続する全ROMをビルドしてからまとめて起動します。Link4では出力されたシンボル等のメタデータをROMの隣に残してください。初代GBで確認する場合は全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
value=77;result[9]=Link_WaitByte(2,&value);result[10]=value;result[11]=Link_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化、使用中エラー、外部クロック待ち、受信リングの満杯を確かめます。BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7になります。IRQ REQUEST=1を確認してから、割り込み禁止のままハンドラー本体を明示呼び出しします。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png実装とソース
kitaqgb/lib/link.h:83
Require an output pointer, poll immediately, then wait at most timeout_frames VBlanks for a byte. Zero timeout still performs the initial poll and read.
kitaqgb/lib/link.c:435
u8 __stackcall Link_WaitByte(u16 timeout_frames, u8 *out) {
u16 waited = 0;
if (out == 0) return 0;
Link_Poll();
if (Link_TryReadByte(out) != 0) return 1;
while (waited < timeout_frames) {
__wait_vblank();
waited++;
Link_Poll();
if (Link_TryReadByte(out) != 0) return 1;
}
Link_SetErrorInternal(LINK_ERR_TIMEOUT);
return 0;
}link_dmg07.h — DMG-07の外部クロック通信
DMG-07は各台からの1バイトを4台へ配信するアダプターです。このライブラリは、探索、プレイヤー1による開始要求、1パケット遅れの配信、整列した再起動制御を扱います。EXCHANGEで基本の4台通信、NO ADAPTERで時間切れ、OVERFLOWで読み忘れ、RESTARTで回復を学べます。
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
このドライバーがシリアルポートを専有します。通常のLinkやLink4と同時に動かさず、link_hwregs_gb.cとlink_dmg07.cを1回ずつ組み込みます。外部クロックはアダプターが供給し、CGB専用の高速通信には切り替えません。
Pollはフレームの途中でも繰り返し呼び、TickFrameは1フレームにつき1回だけ呼びます。VBlankごとにPollを1回呼ぶだけではバイト間の再設定に間に合いません。共通ヘッダーのd_pollはLYが144以上になった境界を検出してTickFrameを呼びます。両者を割り込みとメイン処理から同時に操作しないでください。
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
LinkDmg07_ClearError
最後のエラー番号を0へ戻します。
書式
void LinkDmg07_ClearError();戻り値
戻り値はありません。
使用条件と制限
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
受信データ、フェーズ、接続情報、時間切れラッチは変えません。これだけで再接続や受信再開を行う関数ではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
LinkDmg07_ClearError();result[21]=LinkDmg07_LastError();result[22]=LinkDmg07_GetErrorCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
最初の有効パケットを読まず、次が届くまで待ちます。SENT SEQ=3、PACKET SEQ=2、ERROR=6となり、取得内容は最新の33/34/35/36です。ClearErrorでCLEAR ERROR=0になりますが、ErrorCountは1のままです。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_overflow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_overflow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_overflow.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_overflow.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_overflow.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_overflow.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:125
Clear only the latest error code, preserving counters and protocol state.
kitaqgb/lib/link_dmg07.c:541
void LinkDmg07_ClearError() {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NONE;
}LinkDmg07_ConsumePingStatus
探索完了通知を1回分取り出してフラグを消します。
書式
u8 LinkDmg07_ConsumePingStatus();戻り値
通知があれば1、なければ0です。
使用条件と制限
通知だけを消し、番号・接続マスク・LastStatusの保存値は残します。繰り返し呼ぶと新しい通知が来るまで0になります。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[3]=LinkDmg07_ConsumePingStatus();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:71
Clear and report the completed-discovery flag; return zero if no new status exists.
kitaqgb/lib/link_dmg07.c:418
u8 LinkDmg07_ConsumePingStatus() {
if (LinkDmg07_PingStatusReady == 0) return 0;
LinkDmg07_PingStatusReady = 0;
return 1;
}LinkDmg07_GetConnectedMask
最後に受け付けた接続プレイヤーの集合を4ビットで読みます。
書式
u8 LinkDmg07_GetConnectedMask();戻り値
ビット0~3に接続状態を返します。未検出は0、4台接続は15です。
使用条件と制限
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
探索中に受け付けた値を保存しています。呼び出すたびに物理配線を調べるものではなく、転送中の表示は最後の通知に基づきます。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[1]=LinkDmg07_GetConnectedMask();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:65
Normalized mask: bit 0 is player 1, ..., bit 3 is player 4.
kitaqgb/lib/link_dmg07.c:403
u8 LinkDmg07_GetConnectedMask() {
return LinkDmg07_ConnectedMask;
}LinkDmg07_GetDisconnectCount
検出済みアダプターの無通信、または探索マスクからのプレイヤー減少を数えます。
書式
u8 LinkDmg07_GetDisconnectCount();戻り値
0~255の累積イベント数です。失われた人数ではありません。
使用条件と制限
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
アダプター未検出のまま期限を超えても増えません。1回の状態変化で複数人が消えても1イベントです。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[8]=LinkDmg07_GetDisconnectCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
アダプターを接続せずに実行します。PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4になります。一度もアダプターを見ていないためDISCONNECTS=0です。空の取得ではEMPTY BUFFER=77を保持し、ClearError後はCLEARED=0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png実装とソース
kitaqgb/lib/link_dmg07.h:118
Return the saturating count of removed-player and previously-seen-adapter timeout events.
kitaqgb/lib/link_dmg07.c:525
u8 LinkDmg07_GetDisconnectCount() {
return LinkDmg07_DisconnectCount;
}LinkDmg07_GetErrorCount
受信プロトコル不正と未読パケットの上書きを記録した回数を読みます。
書式
u8 LinkDmg07_GetErrorCount();戻り値
0~255の累積値です。要求の引数・状態エラーや時間切れはこの数に含めません。
使用条件と制限
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
受信メールボックスは最新の1パケットだけを保持します。未読のまま次が完成すると新しい内容で上書きし、エラー6とErrorCountを記録します。取り出すまでアダプターが待ってくれるわけではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[19]=LinkDmg07_GetErrorCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
最初の有効パケットを読まず、次が届くまで待ちます。SENT SEQ=3、PACKET SEQ=2、ERROR=6となり、取得内容は最新の33/34/35/36です。ClearErrorでCLEAR ERROR=0になりますが、ErrorCountは1のままです。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_overflow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_overflow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_overflow.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_overflow.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_overflow.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_overflow.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:121
Return the saturating protocol/overflow error count. Timeout and request validation paths use separate counters or latches and do not increment this value.
kitaqgb/lib/link_dmg07.c:531
u8 LinkDmg07_GetErrorCount() {
return LinkDmg07_ErrorCount;
}LinkDmg07_GetLastStatus
最後に検証を通ったアダプターの状態バイトを変換せずに読みます。
書式
u8 LinkDmg07_GetLastStatus();戻り値
上位4ビットは接続マスク、下位3ビットは自機番号です。未検出は0です。
使用条件と制限
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
ビット3が1、自機番号が0または5以上、検出後に自機番号が変わる通知は拒否し、エラー5を記録します。拒否された値はLastStatusへ保存しません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[2]=LinkDmg07_GetLastStatus();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:67
Return the last validated raw adapter status byte.
kitaqgb/lib/link_dmg07.c:408
u8 LinkDmg07_GetLastStatus() {
return LinkDmg07_LastStatus;
}LinkDmg07_GetLocalSlot
アダプターの状態通知から分かった自機のプレイヤー番号を読みます。
書式
u8 LinkDmg07_GetLocalSlot();戻り値
検出済みは1~4、未検出または探索へのリセット直後は0です。
使用条件と制限
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
slot=LinkDmg07_GetLocalSlot();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:62
Return the one-based player slot, or LINK_DMG07_NO_SLOT before discovery.
kitaqgb/lib/link_dmg07.c:398
u8 LinkDmg07_GetLocalSlot() {
return LinkDmg07_LocalSlot;
}LinkDmg07_GetPacketSequence
最後にメールボックスへ公開したパケットの番号を読みます。
書式
u8 LinkDmg07_GetPacketSequence();戻り値
8ビットの受信パケット番号です。読み取りで未読フラグは変わりません。
使用条件と制限
番号は8ビットで255の次は0です。パケット公開時にPacketSequence=SentSequence−1となります。その後も送信が始まるとSentSequenceが進むため、後から読む2つの番号が常に差1になるとは限りません。
受信メールボックスは最新の1パケットだけを保持します。未読のまま次が完成すると新しい内容で上書きし、エラー6とErrorCountを記録します。取り出すまでアダプターが待ってくれるわけではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[7]=LinkDmg07_GetPacketSequence();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:109
Return the modulo-256 sequence associated with the most recently published packet.
kitaqgb/lib/link_dmg07.c:510
u8 LinkDmg07_GetPacketSequence() {
return LinkDmg07_PacketSequence;
}LinkDmg07_GetPacketSlot
保存中のパケットから指定プレイヤーの1バイトを読みます。
書式
u8 __stackcall LinkDmg07_GetPacketSlot(u8 player_slot);引数
player_slot物理プレイヤー番号1~4。
戻り値
1~4なら対応する保存値、不正な番号なら0です。
使用条件と制限
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
未読フラグは調べず、消費もしません。初期化直後は0、消費後や探索へのリセット後は保存値を返すことがあるため、新着判定には使いません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[16]=LinkDmg07_GetPacketSlot(4);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:102
Read one cached player byte using a one-based slot. Invalid slots return zero; the function neither requires readiness nor consumes the packet.
kitaqgb/lib/link_dmg07.c:499
u8 __stackcall LinkDmg07_GetPacketSlot(u8 player_slot) {
if ((player_slot == 0) || (player_slot > LINK_DMG07_MAX_PLAYERS)) return 0;
return LinkDmg07_RxPacket[(__safe_index u8)(player_slot - 1)];
}LinkDmg07_GetPhase
探索、開始要求、開始確認、データ転送のうち現在の段階を読みます。
書式
u8 LinkDmg07_GetPhase();戻り値
0=PING、1=REQUEST_AA、2=WAIT_CC、3=TRANSFERです。
使用条件と制限
状態を読むだけでポーリングや待機はしません。要求を受け付けただけではTRANSFERにならず、連続する4バイトの0xCCを受信して初めて転送へ移ります。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[8]=LinkDmg07_GetPhase();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:60
Return the discovery, start-handshake or transfer phase without polling.
kitaqgb/lib/link_dmg07.c:393
u8 LinkDmg07_GetPhase() {
return LinkDmg07_Phase;
}LinkDmg07_GetRestartState
再起動要求の待機・送信状態を読みます。
書式
u8 LinkDmg07_GetRestartState();戻り値
0=なし、1=境界待ち、2=制御送信中です。
使用条件と制限
これは通信フェーズとは別の状態です。値0だけでは接続済みや転送可能とは判断できません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[4]=LinkDmg07_GetRestartState();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4台のうちプレイヤー2が再起動を要求します。その台でPENDING=1とSENT FF=1を記録し、全台でDATA VALID=1、RESET PHASE=0、SLOT AFTER=0、RESET PRIME=0になります。要求直後に通信状態を捨てず、アダプターの通知後に探索へ戻ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_restart.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_restart.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_restart.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_restart.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_restart.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_restart.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:85
Return whether aligned recovery is absent, pending or sending control traffic.
kitaqgb/lib/link_dmg07.c:457
u8 LinkDmg07_GetRestartState() {
return LinkDmg07_RestartState;
}LinkDmg07_GetSentSequence
自機の提出バイトが実際にクロックで送り出された回数を読みます。
書式
u8 LinkDmg07_GetSentSequence();戻り値
8ビットの送信番号です。初回の提出も数えますが、再起動制御の送信は数えません。
使用条件と制限
番号は8ビットで255の次は0です。パケット公開時にPacketSequence=SentSequence−1となります。その後も送信が始まるとSentSequenceが進むため、後から読む2つの番号が常に差1になるとは限りません。
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[6]=LinkDmg07_GetSentSequence();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:107
SentSequence increments when this console's packet byte is clocked out. PacketSequence labels the returned four-byte packet. For a valid packet it is one less than SentSequence, making the DMG-07 one-packet delay explicit.
kitaqgb/lib/link_dmg07.c:505
u8 LinkDmg07_GetSentSequence() {
return LinkDmg07_SentSequence;
}LinkDmg07_GetSilenceFrames
最後のシリアル完了処理から経過したフレーム数を読みます。
書式
u8 LinkDmg07_GetSilenceFrames();戻り値
0~255。完了処理で0へ戻り、TickFrameごとに増えます。
使用条件と制限
Pollはフレームの途中でも繰り返し呼び、TickFrameは1フレームにつき1回だけ呼びます。VBlankごとにPollを1回呼ぶだけではバイト間の再設定に間に合いません。共通ヘッダーのd_pollはLYが144以上になった境界を検出してTickFrameを呼びます。両者を割り込みとメイン処理から同時に操作しないでください。
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[6]=LinkDmg07_GetSilenceFrames();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
アダプターを接続せずに実行します。PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4になります。一度もアダプターを見ていないためDISCONNECTS=0です。空の取得ではEMPTY BUFFER=77を保持し、ClearError後はCLEARED=0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png実装とソース
kitaqgb/lib/link_dmg07.h:114
SilenceFrames is reset by every completed serial byte. TimeoutCount counts distinct serial-silence or AA/CC/restart handshake timeouts. DisconnectCount counts adapter-silence outages and ping-status membership drops. All saturate.
kitaqgb/lib/link_dmg07.c:515
u8 LinkDmg07_GetSilenceFrames() {
return LinkDmg07_SilenceFrames;
}LinkDmg07_GetTimeoutCount
無通信またはハンドシェイクの期限超過を記録した回数を読みます。
書式
u8 LinkDmg07_GetTimeoutCount();戻り値
0~255の累積値です。
使用条件と制限
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
受信が再開するまで同じ無通信状態を毎フレーム加算しません。ClearErrorを呼んでもこの抑制ラッチと累積値は残ります。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[7]=LinkDmg07_GetTimeoutCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
アダプターを接続せずに実行します。PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4になります。一度もアダプターを見ていないためDISCONNECTS=0です。空の取得ではEMPTY BUFFER=77を保持し、ClearError後はCLEARED=0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png実装とソース
kitaqgb/lib/link_dmg07.h:116
Return the saturating lifetime timeout count, preserved across protocol restarts.
kitaqgb/lib/link_dmg07.c:520
u8 LinkDmg07_GetTimeoutCount() {
return LinkDmg07_TimeoutCount;
}LinkDmg07_HasPacket
通常データの完成済み4バイトパケットが未読か調べます。
書式
u8 LinkDmg07_HasPacket();戻り値
未読があれば1、空なら0です。
使用条件と制限
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
受信メールボックスは最新の1パケットだけを保持します。未読のまま次が完成すると新しい内容で上書きし、エラー6とErrorCountを記録します。取り出すまでアダプターが待ってくれるわけではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[15]=LinkDmg07_HasPacket();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:96
Inspect the one-packet receive mailbox without consuming it.
kitaqgb/lib/link_dmg07.c:473
u8 LinkDmg07_HasPacket() {
return LinkDmg07_PacketReady;
}LinkDmg07_HasPingStatus
4バイトの探索交換を終えた通知が未消費かどうかを調べます。
書式
u8 LinkDmg07_HasPingStatus();戻り値
未消費の通知があれば1、なければ0です。
使用条件と制限
フラグを読むだけです。探索中の不正な状態バイトでも交換自体は進むため、このフラグだけで全データの妥当性や転送開始完了を判断せず、番号・マスク・LastErrorも確認します。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[4]=LinkDmg07_HasPingStatus();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:69
Read the completed-discovery status flag without clearing it.
kitaqgb/lib/link_dmg07.c:413
u8 LinkDmg07_HasPingStatus() {
return LinkDmg07_PingStatusReady;
}LinkDmg07_Init
探索状態、受信保存領域、診断値を初期化し、アダプターからのクロックを待ち受けます。
書式
void __stackcall LinkDmg07_Init(u8 rate);引数
rateアダプターへ返す8ビットRATE。0なら既定の0x10を使います。
戻り値
戻り値はありません。
使用条件と制限
このドライバーがシリアルポートを専有します。通常のLinkやLink4と同時に動かさず、link_hwregs_gb.cとlink_dmg07.cを1回ずつ組み込みます。外部クロックはアダプターが供給し、CGB専用の高速通信には切り替えません。
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
SCを0x80、SBを0x88に設定し、IEとIFのシリアルビットだけを消します。ほかの割り込み許可ビットやCPUのIME状態を変更する処理ではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
LinkDmg07_Init(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:50
Initializes polling-only external-clock operation. A zero RATE is replaced with LINK_DMG07_DEFAULT_RATE. This driver disables only the serial IRQ bit.
kitaqgb/lib/link_dmg07.c:282
void __stackcall LinkDmg07_Init(u8 rate) {
u8 i;
LinkDmg07_Rate = rate;
if (LinkDmg07_Rate == 0) LinkDmg07_Rate = LINK_DMG07_DEFAULT_RATE;
LinkDmg07_LocalByte = 0;
LinkDmg07_SentSequence = 0;
LinkDmg07_PacketSequence = 0;
LinkDmg07_RestartState = LINK_DMG07_RESTART_NONE;
LinkDmg07_SilenceFrames = 0;
LinkDmg07_TimeoutCount = 0;
LinkDmg07_DisconnectCount = 0;
LinkDmg07_ErrorCount = 0;
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NONE;
LinkDmg07_SeenAdapter = 0;
LinkDmg07_TimeoutLatched = 0;
for (i = 0; i < LINK_DMG07_PACKET_BYTES; ++i) {
LinkDmg07_RxBuild[(__safe_index u8)i] = 0;
LinkDmg07_RxPacket[(__safe_index u8)i] = 0;
}
LinkDmg07_ResetToPingInternal();
LinkHw_WriteIE((u8)(LinkHw_ReadIE() & LINK_DMG07_IRQ_SERIAL_CLEAR));
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & LINK_DMG07_IRQ_SERIAL_CLEAR));
LinkDmg07_ArmInternal(LINK_DMG07_ACK);
}LinkDmg07_IsPipelinePrimed
初回の未定義配信を捨てて受信準備を終えたか調べます。
書式
u8 LinkDmg07_IsPipelinePrimed();戻り値
初回配信の処理後は1、初期化・探索へのリセット・開始確認時は0です。
使用条件と制限
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
1でも完成済みパケットがあるとは限りません。取り出せるかどうかはHasPacketで調べます。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[9]=LinkDmg07_IsPipelinePrimed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:94
The first incoming adapter data packet is discarded. Thereafter ReadPacket copies four bytes in physical slot order (players 1..4).
kitaqgb/lib/link_dmg07.c:468
u8 LinkDmg07_IsPipelinePrimed() {
return LinkDmg07_PipelinePrimed;
}LinkDmg07_LastError
最後に記録したエラー番号を読みます。
書式
u8 LinkDmg07_LastError();戻り値
0~6のエラー番号です。
使用条件と制限
エラーは0=なし、1=不正なフェーズ、2=プレイヤー1ではない、3=接続準備不足、4=時間切れ、5=プロトコル不正、6=未読パケットの上書きです。要求の成功は0、受信取得の成功は1です。成功した要求が以前のエラーを消すことはありません。
読み取りでは消えません。複数の問題が起きたときの全履歴ではなく、最後のコードだけです。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[9]=LinkDmg07_LastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
アダプターを接続せずに実行します。PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4になります。一度もアダプターを見ていないためDISCONNECTS=0です。空の取得ではEMPTY BUFFER=77を保持し、ClearError後はCLEARED=0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png実装とソース
kitaqgb/lib/link_dmg07.h:123
Return the latest error latch without clearing counters or restarting the protocol.
kitaqgb/lib/link_dmg07.c:536
u8 LinkDmg07_LastError() {
return LinkDmg07_LastErrorCode;
}LinkDmg07_Poll
完了済みのシリアル1バイトを現在のフェーズで処理し、次の応答を設定して再び受信を開始します。
書式
void LinkDmg07_Poll();戻り値
戻り値はありません。
使用条件と制限
このドライバーがシリアルポートを専有します。通常のLinkやLink4と同時に動かさず、link_hwregs_gb.cとlink_dmg07.cを1回ずつ組み込みます。外部クロックはアダプターが供給し、CGB専用の高速通信には切り替えません。
Pollはフレームの途中でも繰り返し呼び、TickFrameは1フレームにつき1回だけ呼びます。VBlankごとにPollを1回呼ぶだけではバイト間の再設定に間に合いません。共通ヘッダーのd_pollはLYが144以上になった境界を検出してTickFrameを呼びます。両者を割り込みとメイン処理から同時に操作しないでください。
SCの開始ビットが立っている間は何もしません。完了を処理するとSilenceFramesと時間切れラッチをリセットします。初期化前には呼ばないでください。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
void d_poll(){LinkDmg07_Poll();if(D_LY>=144){if(d_vblank==0){d_vblank=1;LinkDmg07_TickFrame();}}else d_vblank=0;}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:54
Services at most one completed serial byte and immediately re-arms SC=$80. Call continuously from the main loop, including between VBlanks.
kitaqgb/lib/link_dmg07.c:311
void LinkDmg07_Poll() {
u8 value;
u8 next;
if ((LinkHw_ReadSC() & LINK_DMG07_SC_EXTERNAL_START) != 0) return;
value = LinkHw_ReadSB();
LinkDmg07_SilenceFrames = 0;
LinkDmg07_TimeoutLatched = 0;
LinkDmg07_SeenAdapter = 1;
next = 0;
if (LinkDmg07_Phase == LINK_DMG07_PHASE_PING) {
next = LinkDmg07_ProcessPingInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_REQUEST_AA) {
next = LinkDmg07_ProcessRequestInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_WAIT_CC) {
next = LinkDmg07_ProcessWaitCcInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_TRANSFER) {
next = LinkDmg07_ProcessTransferInternal(value);
} else {
LinkDmg07_SetRuntimeErrorInternal(LINK_DMG07_ERR_PROTOCOL);
LinkDmg07_ResetToPingInternal();
next = LINK_DMG07_ACK;
}
LinkDmg07_ArmInternal(next);
}LinkDmg07_ReadPacket
最新の完成済みパケットを取り出し、未読フラグを消します。
書式
u8 __stackcall LinkDmg07_ReadPacket(u8 *dst);引数
dst4バイトの書き込み可能な領域。0なら内容を読み捨てます。
戻り値
取得できれば1、空なら0です。空ではdstを書き換えません。
使用条件と制限
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
受信メールボックスは最新の1パケットだけを保持します。未読のまま次が完成すると新しい内容で上書きし、エラー6とErrorCountを記録します。取り出すまでアダプターが待ってくれるわけではありません。
読んだ後も保存領域にはデータが残るため、GetPacketSlotで参照できます。空のReadPacketはその保存値を再取得する操作ではありません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[10]=LinkDmg07_ReadPacket(packet);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:99
Consume the latest four-byte packet. A nonnull destination must hold four bytes; null discards the packet. Packet storage remains after the flag clears.
kitaqgb/lib/link_dmg07.c:479
u8 __stackcall LinkDmg07_ReadPacket(u8 *dst) {
if (LinkDmg07_PacketReady == 0) return 0;
if (dst != 0) {
// Keep this as a sequential pointer copy. Besides being smaller for a
// fixed four-byte packet, it avoids making a serial hot-path copy
// depend on general variable-index pointer lowering.
*dst = LinkDmg07_RxPacket[0];
dst++;
*dst = LinkDmg07_RxPacket[1];
dst++;
*dst = LinkDmg07_RxPacket[2];
dst++;
*dst = LinkDmg07_RxPacket[3];
}
LinkDmg07_PacketReady = 0;
return 1;
}LinkDmg07_RequestRestart
転送中のパケット境界から再起動制御を送るよう予約します。
書式
u8 LinkDmg07_RequestRestart();戻り値
TRANSFERなら0、それ以外は1です。予約中・送信中の再要求も0です。
使用条件と制限
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
エラーは0=なし、1=不正なフェーズ、2=プレイヤー1ではない、3=接続準備不足、4=時間切れ、5=プロトコル不正、6=未読パケットの上書きです。要求の成功は0、受信取得の成功は1です。成功した要求が以前のエラーを消すことはありません。
どのプレイヤーでも要求できます。途中のバイト位置を壊さず、境界から0xFFを送り続け、アダプターの全0xFF通知を受けて探索へ戻ります。要求しただけでは自機番号や受信パイプラインを消しません。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[3]=LinkDmg07_RequestRestart();result[4]=LinkDmg07_GetRestartState();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4台のうちプレイヤー2が再起動を要求します。その台でPENDING=1とSENT FF=1を記録し、全台でDATA VALID=1、RESET PHASE=0、SLOT AFTER=0、RESET PRIME=0になります。要求直後に通信状態を捨てず、アダプターの通知後に探索へ戻ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_restart.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_restart.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_restart.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_restart.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_restart.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_restart.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:83
Any physical slot may schedule FF FF FF FF at the next four-byte packet boundary while remaining in TRANSFER phase. The driver keeps sending aligned FF bytes until it sees the adapter's complete all-FF indicator packet, then returns to PING. A transfer-phase silence timeout schedules the same recovery automatically and preserves the current byte position for clocks that resume.
kitaqgb/lib/link_dmg07.c:445
u8 LinkDmg07_RequestRestart() {
if (LinkDmg07_Phase != LINK_DMG07_PHASE_TRANSFER) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_STATE;
return LINK_DMG07_ERR_STATE;
}
if (LinkDmg07_RestartState == LINK_DMG07_RESTART_NONE) {
LinkDmg07_RestartState = LINK_DMG07_RESTART_PENDING;
}
return LINK_DMG07_ERR_NONE;
}LinkDmg07_RequestTransmission
物理プレイヤー1から、次の探索パケット境界で開始要求を送るよう予約します。
書式
u8 LinkDmg07_RequestTransmission();戻り値
予約できれば0。フェーズ不正は1、プレイヤー1でなければ2、自機接続ビットがなければ3です。
使用条件と制限
ライブラリのプレイヤー番号は1~4、未検出は0です。接続マスクのビット0~3がプレイヤー1~4に対応します。KOKURAの--link-sessionに渡すslotは0~3なので、ROM内の番号と区別します。
エラーは0=なし、1=不正なフェーズ、2=プレイヤー1ではない、3=接続準備不足、4=時間切れ、5=プロトコル不正、6=未読パケットの上書きです。要求の成功は0、受信取得の成功は1です。成功した要求が以前のエラーを消すことはありません。
PINGでのみ予約できます。全4台がそろうまで待つ処理は含まず、サンプルはアプリ側でマスク15を待ちます。予約は即時転送ではなく、整列した0xAA列を送ってから0xCC列を待ちます。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
result[5]=LinkDmg07_RequestTransmission();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:76
Only physical player 1 may request AA AA AA AA. The bytes are aligned to the next FE/status/status/status ping packet. All consoles enter transfer mode only after receiving four consecutive CC bytes.
kitaqgb/lib/link_dmg07.c:426
u8 LinkDmg07_RequestTransmission() {
if (LinkDmg07_Phase != LINK_DMG07_PHASE_PING) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_STATE;
return LINK_DMG07_ERR_STATE;
}
if (LinkDmg07_LocalSlot != 1) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NOT_PLAYER1;
return LINK_DMG07_ERR_NOT_PLAYER1;
}
if ((LinkDmg07_ConnectedMask & (u8)0x01) == 0) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NOT_READY;
return LINK_DMG07_ERR_NOT_READY;
}
LinkDmg07_StartRequested = 1;
return LINK_DMG07_ERR_NONE;
}LinkDmg07_SetLocalByte
次に自機データを送るパケット用の1バイトを設定します。
書式
void __stackcall LinkDmg07_SetLocalByte(u8 value);引数
value提出する8ビット値。
戻り値
戻り値はありません。
使用条件と制限
SIZE=1で各台が1バイトを提出し、4バイトの配信はプレイヤー1~4の順です。最初の配信は捨て、次の配信から受け取ります。受信内容は1パケット前の提出値です。4バイトすべてが0xFFの配信は再起動通知として予約されています。
値はパケットのバイト0を設定するときに読み取ります。すでにSBへ設定したバイトは変更しません。最初の提出値は開始要求前に設定します。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
LinkDmg07_SetLocalByte(32+slot);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じROMを4台で動かし、各台が32+プレイヤー番号を送ります。全台でPLAYER 1~4が33/34/35/36になります。最初の有効な配信でSENT SEQ=2、PACKET SEQ=1を記録し、1パケットの遅延を示します。表示はその時点の観測値を固定したものです。ゲームでは受信後もポーリングを続けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png実装とソース
kitaqgb/lib/link_dmg07.h:90
Sets the byte sampled when byte 0 of the next adapter packet is armed. Set it before the current packet completes (and before requesting start for the first submission). During normal transfer, bytes 1..3 are sent as zero.
kitaqgb/lib/link_dmg07.c:463
void __stackcall LinkDmg07_SetLocalByte(u8 value) {
LinkDmg07_LocalByte = value;
}LinkDmg07_TickFrame
無通信時間と開始・再起動ハンドシェイクの時間を1フレーム分進め、期限に達したら回復を要求します。
書式
void LinkDmg07_TickFrame();戻り値
戻り値はありません。
使用条件と制限
Pollはフレームの途中でも繰り返し呼び、TickFrameは1フレームにつき1回だけ呼びます。VBlankごとにPollを1回呼ぶだけではバイト間の再設定に間に合いません。共通ヘッダーのd_pollはLYが144以上になった境界を検出してTickFrameを呼びます。両者を割り込みとメイン処理から同時に操作しないでください。
SilenceFramesと診断カウンターは255で止まります。Initはすべて初期化しますが、通常のプロトコル再起動では診断カウンターを保持します。ClearErrorはエラー番号だけを消します。
既定の期限は12回で、組込み前にLINK_DMG07_TIMEOUT_FRAMESを1~255へ設定できます。同じ無通信状態では1回だけ時間切れを数え、次の受信バイトでラッチを解除します。転送中は位置を保持して境界で再起動し、探索・開始待ちでは探索へ戻ります。
画像はKOKURAのDMG-07モデルで実行した結果です。接続するコンソールは個別に動作し、実際の送受信値を照合しています。実物のアダプターやケーブルの電気的特性・タイミングは未検証です。CGBの数値は青色で表示します。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーへ配置し、親フォルダーでPowerShellを開きます。4台通信では同じROMを4セッションで起動します。初代GB用に試す場合は、全コマンドの--cgb=cgbを--cgb=dmg、--hardware cgbを--hardware dmgへ変更します。
使用例
void d_poll(){LinkDmg07_Poll();if(D_LY>=144){if(d_vblank==0){d_vblank=1;LinkDmg07_TickFrame();}}else d_vblank=0;}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
アダプターを接続せずに実行します。PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4になります。一度もアダプターを見ていないためDISCONNECTS=0です。空の取得ではEMPTY BUFFER=77を保持し、ClearError後はCLEARED=0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png実装とソース
kitaqgb/lib/link_dmg07.h:57
Advances saturating frame-based silence and handshake timeout counters.
kitaqgb/lib/link_dmg07.c:369
void LinkDmg07_TickFrame() {
LinkDmg07_SilenceFrames = LinkDmg07_IncrementSaturatingInternal(LinkDmg07_SilenceFrames);
if ((LinkDmg07_Phase == LINK_DMG07_PHASE_REQUEST_AA) ||
(LinkDmg07_Phase == LINK_DMG07_PHASE_WAIT_CC) ||
(LinkDmg07_RestartState == LINK_DMG07_RESTART_SENDING)) {
LinkDmg07_HandshakeFrames = LinkDmg07_IncrementSaturatingInternal(LinkDmg07_HandshakeFrames);
} else {
LinkDmg07_HandshakeFrames = 0;
}
if ((LinkDmg07_SilenceFrames >= LINK_DMG07_TIMEOUT_FRAMES) &&
(LinkDmg07_TimeoutLatched == 0)) {
LinkDmg07_TimeoutInternal(1);
return;
}
if ((LinkDmg07_HandshakeFrames >= LINK_DMG07_TIMEOUT_FRAMES) &&
(LinkDmg07_TimeoutLatched == 0)) {
LinkDmg07_TimeoutInternal(0);
}
}physics2d.h — 2Dの運動と衝突
矩形bodyの積分・AABB接触と、個別に使える面応答を提供します。中心と半サイズで矩形を表し、active=0は処理対象外、inv_mass_q8<=0は固定物体です。KQRectは中心形式ではなく左上+幅・高さです。bodyの反発係数は矩形ペアのソルバーでは使わず、面応答ではKQSurface2Dの係数を使います。
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
kq2d_body_apply_friction
両軸の速度を符号付き8ビット係数/256で縮小します。位置や有効状態は調べません。
書式
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
friction_q8残す速度の倍率です。この関数ではs8へ変換され、128~255は負の係数になる点に注意してください。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
0~127は0~127/256の倍率です。128~255はs8への変換で負になり、速度を反転させます。0~255の正の減衰が必要なら、各速度にkq2d_scale_q8を使ってください。積の端数は0方向へ切り捨てます。NULLは無視します。
使用例
kq2d_body_apply_friction(&temp,64);
result[11]=temp.vx; result[12]=temp.vy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:66
Scale both velocities through the signed 8-bit coefficient intrinsic. Despite the unsigned parameter, values 128..255 become negative coefficients; this is not a full-range unsigned Q8 damping helper. Null is ignored.
kitaqgb/lib/physics2d.c:654
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8)
{
if (body == 0) return;
body->vx = (s16)__smul16x8(body->vx, (s8)friction_q8);
body->vy = (s16)__smul16x8(body->vy, (s8)friction_q8);
}kq2d_body_apply_gravity
有効な動的bodyの速度に重力を足し、各軸を±max_speedに制限します。位置は更新しません。
書式
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
gravity_xこの呼び出しでX速度へ加える加速度。
gravity_yこの呼び出しでY速度へ加える加速度。
max_speed速度の上限。軸ごとの制限か合成速度の制限かは関数ごとに異なります。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
NULL・active=0・inv_mass_q8<=0のbodyは無視します。速度上限には0以上を指定します。
使用例
kq2d_body_apply_gravity(&temp,5,-10,32);
result[9]=temp.vx; result[10]=temp.vy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:62
Add gravity and clamp each velocity axis separately for an active dynamic body. Null, inactive and static bodies are ignored. Supply a nonnegative limit and avoid overflow in the additions before clamping; position is unchanged.
kitaqgb/lib/physics2d.c:630
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed)
{
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
kq2d_asm_value = body->vx;
kq2d_asm_operand = gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = max_speed;
kq2d_clamp_abs_s16_asm();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = max_speed;
kq2d_clamp_abs_s16_asm();
body->vy = kq2d_asm_value;
}kq2d_body_init
中心と半サイズを設定し、速度0・逆質量256・有効状態の矩形bodyを作ります。
書式
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
x中心または点のX座標。サンプルではピクセル単位です。
y中心または点のY座標。サンプルでは下向きが正です。
half_xX方向の半サイズ。箱の全幅はこの値の2倍です。
half_yY方向の半サイズ。箱の全高はこの値の2倍です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
restitution_q8は0に設定します。bodyがNULLなら何もしません。半サイズは正の値を指定してください。
使用例
kq2d_body_init(&temp,10,20,8,8);
result[3]=temp.inv_mass_q8; result[4]=temp.active;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:54
Initialize an active unit-inverse-mass body at the supplied center and half extents, with zero velocity and reserved restitution set to zero. Null is ignored.
kitaqgb/lib/physics2d.c:597
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y)
{
if (body == 0) return;
body->x = x;
body->y = y;
body->vx = 0;
body->vy = 0;
body->half_x = half_x;
body->half_y = half_y;
body->inv_mass_q8 = 256;
body->restitution_q8 = 0;
body->active = 1;
}kq2d_body_limit_speed
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
速度ベクトルの長さを保守的に見積もり、両軸を同じ倍率で縮小して合成速度を制限します。
書式
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
max_speed速度の上限。軸ごとの制限か合成速度の制限かは関数ごとに異なります。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
各速度の絶対値と正のmax_speedは16383以下にしてください。上限0以下は速度を0にします。NULLは無視しますが、activeや質量は無視して変更します。整数量子化により上限より小さくなり、ごく小さい倍率では0になります。
使用例
kq2d_body_limit_speed(&body,250);
result[2]=body.vx; result[3]=body.vy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い物体の位置は変わりません。緑の速度線は左の(8,16)から右の(2,−8)へ変わり、青い面に対する反発と接線摩擦を表します。図の原点は画面(8,48)・(88,48)、物体中心は図内(24,40)です。接近速度は16、交差時刻の例は192/256です。速度制限の例では(300,400)が(147,196)になります。DMGでは全て黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:92
Conservative circular bound, with a shared Q8 scale for both components. Quantizes toward zero; very small limits relative to input may yield zero. Components and positive max_speed must be <= 16383 in magnitude.
kitaqgb/lib/physics2d.c:260
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed)
{
u16 x;
u16 y;
u16 swap;
u16 ratio;
u16 length;
if (body == 0) return;
if (max_speed <= 0) { body->vx = 0; body->vy = 0; return; }
x = (u16)body->vx;
y = (u16)body->vy;
if (body->vx < 0) x = (u16)(0 - body->vx);
if (body->vy < 0) y = (u16)(0 - body->vy);
if (x + y <= (u16)max_speed) return;
if (x < y) { swap = x; x = y; y = swap; }
// major + minor/2 bounds hypot from above for 0 <= minor <= major.
if (x + (y >> 1) + 1 <= (u16)max_speed) return;
// Round the minor/major ratio upward for the 65-entry upper-bound table.
// The extra unit covers truncation when converting the estimate back to speed.
ratio = (kq2d__fraction_q8(y, x) + 4) >> 2;
if (ratio > 64) ratio = 64;
length = x + (u16)kq2d_scale_q8((s16)x, (s16)kq2d_hypot_extra[(__safe_index u8)ratio]) + 1;
if (length <= (u16)max_speed) return;
ratio = kq2d__fraction_q8((u16)max_speed, length);
body->vx = kq2d_scale_q8(body->vx, (s16)ratio);
body->vy = kq2d_scale_q8(body->vy, (s16)ratio);
}kq2d_body_resolve_surface
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
移動する面との相対速度を使い、法線方向の反発・最低反発速度と接線摩擦を速度へ適用します。
書式
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
surface面の法線・移動速度・反発・摩擦を指定したKQSurface2Dへのポインタ。
戻り値
面へ向かう法線速度の正の大きさを返します。NULLまたは離れる動きでは0です。
使用条件と制限
法線は256を長さ1とするQ8単位ベクトルです。相対速度の各成分は±8191以内、thresholdとkickは0以上、係数は0~255にします。閾値を厳密に超えた時だけ反発係数を使い、kickを最低反発速度として適用します。NULLや離れる動きは無変更です。接触検出・めり込み解消は別途必要で、activeや質量の判定はしません。
使用例
result[6]=kq2d_body_resolve_surface(&body,&surface);
result[7]=body.vx; result[8]=body.vy; result[9]=body.y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い物体の位置は変わりません。緑の速度線は左の(8,16)から右の(2,−8)へ変わり、青い面に対する反発と接線摩擦を表します。図の原点は画面(8,48)・(88,48)、物体中心は図内(24,40)です。接近速度は16、交差時刻の例は192/256です。速度制限の例では(300,400)が(147,196)になります。DMGでは全て黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:95
Kinematic surface response; returns incoming normal speed, or zero if separating. Caller resolves penetration separately. Relative components must fit +/-8191.
kitaqgb/lib/physics2d.c:314
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface)
{
s16 vx;
s16 vy;
s16 speed;
s16 tangent;
s16 bounce = 0;
s16 impulse;
s16 friction;
if (body == 0 || surface == 0) return 0;
vx = (s16)(body->vx - surface->vx);
vy = (s16)(body->vy - surface->vy);
speed = (s16)(kq2d_scale_q8(vx,surface->nx_q8) + kq2d_scale_q8(vy,surface->ny_q8));
if (speed >= 0) return 0;
speed = (s16)(0 - speed);
if (speed > surface->bounce_threshold) bounce = kq2d_scale_q8(speed,(s16)surface->restitution_q8);
if (bounce < surface->kick) bounce = surface->kick;
impulse = (s16)(speed + bounce);
// A resting contact is a tangent constraint, not a tiny rounded bounce.
if (bounce == 0) {
tangent = (s16)(kq2d__rest_component(vy,surface->nx_q8) - kq2d__rest_component(vx,surface->ny_q8));
friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
if (tangent > friction) tangent = (s16)(tangent - friction);
else if (tangent < (s16)(0 - friction)) tangent = (s16)(tangent + friction);
else tangent = 0;
body->vx = (s16)(surface->vx - kq2d__rest_component(tangent,surface->ny_q8));
body->vy = (s16)(surface->vy + kq2d__rest_component(tangent,surface->nx_q8));
return speed;
}
body->vx = (s16)(body->vx + kq2d_scale_q8(impulse,surface->nx_q8));
body->vy = (s16)(body->vy + kq2d_scale_q8(impulse,surface->ny_q8));
if (surface->friction_q8 != 0) {
tangent = (s16)(kq2d_scale_q8(vy,surface->nx_q8) - kq2d_scale_q8(vx,surface->ny_q8));
friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
if (tangent > friction) tangent = friction;
if (tangent < (s16)(0 - friction)) tangent = (s16)(0 - friction);
body->vx = (s16)(body->vx + kq2d_scale_q8(tangent,surface->ny_q8));
body->vy = (s16)(body->vy - kq2d_scale_q8(tangent,surface->nx_q8));
}
return speed;
}kq2d_body_set_pos
bodyの中心座標だけを置き換えます。速度・大きさ・有効状態を変更しません。
書式
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
x中心または点のX座標。サンプルではピクセル単位です。
y中心または点のY座標。サンプルでは下向きが正です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
NULLは無視します。無効bodyや固定bodyも、NULL以外なら変更します。
使用例
kq2d_body_set_pos(&temp,-12,20);
result[5]=temp.x; result[6]=temp.y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:56
Replace the center without altering velocity, extents or activity. Null is ignored.
kitaqgb/lib/physics2d.c:612
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y)
{
if (body == 0) return;
body->x = x;
body->y = y;
}kq2d_body_set_velocity
bodyの速度を直接設定します。上限への制限や位置更新は行いません。
書式
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
vxX方向の1刻み当たりの変位。位置と同じ単位です。
vyY方向の1刻み当たりの変位。位置と同じ単位です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
NULLは無視します。無効bodyや固定bodyも、NULL以外なら変更します。
使用例
kq2d_body_set_velocity(&temp,40,-30);
result[7]=temp.vx; result[8]=temp.vy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:58
Replace velocity without clamping or checking activity/mass. Null is ignored.
kitaqgb/lib/physics2d.c:620
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy)
{
if (body == 0) return;
body->vx = vx;
body->vy = vy;
}kq2d_integrate_body
1個の動的bodyへワールドの重力と軸速度制限を適用し、その速度を位置へ足します。
書式
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
NULL・active=0・inv_mass_q8<=0のbodyは無視します。速度上限には0以上を指定します。
この呼び出し単独では他のbodyとの衝突を解決しません。worldがNULLの場合も何もしません。
使用例
kq2d_integrate_body(&world,&temp);
result[18]=temp.x; result[19]=temp.y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:47
For one active dynamic body, add gravity, clamp each velocity axis and then add velocity to position. Null inputs are ignored; no contacts are solved. Use a nonnegative speed limit and keep additions in signed 16-bit range.
kitaqgb/lib/physics2d.c:664
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body)
{
if (world == 0) return;
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
kq2d_asm_value = body->vx;
kq2d_asm_operand = world->gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = world->gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
body->vy = kq2d_asm_value;
kq2d_asm_value = body->x;
kq2d_asm_operand = body->vx;
kq2d_add_s16_asm();
body->x = kq2d_asm_value;
kq2d_asm_value = body->y;
kq2d_asm_operand = body->vy;
kq2d_add_s16_asm();
body->y = kq2d_asm_value;
}kq2d_overlap_aabb
中心と半サイズで表した2個の矩形が、X・Y両軸で厳密に重なるか調べます。
書式
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b);引数
a有効なbodyへのポインタ。照会はactive・質量・flagsを無視します。
b有効なbodyへのポインタ。照会はactive・質量・flagsを無視します。
戻り値
内部が重なれば1、接触のみまたは離れていれば0です。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
使用例
result[20]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);
bodies[1].x=40; result[21]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:51
Test strict overlap of two centered boxes; touching edges return false. Both pointers must be valid. Active/mass flags are ignored, and coordinate differences and extent sums must fit the signed 16-bit calculations.
kitaqgb/lib/physics2d.c:579
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b)
{
s16 dx = (s16)(b->x - a->x);
s16 dy = (s16)(b->y - a->y);
s16 adx = kq2d__abs_s16(dx);
s16 ady = kq2d__abs_s16(dy);
s16 sx = (s16)(a->half_x + b->half_x);
s16 sy = (s16)(a->half_y + b->half_y);
if (adx >= sx) return 0;
if (ady >= sy) return 0;
return 1;
}kq2d_point_in_rect
点が矩形の左端・上端を含み、右端・下端を含まない範囲内にあるか調べます。
書式
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r);引数
x中心または点のX座標。サンプルではピクセル単位です。
y中心または点のY座標。サンプルでは下向きが正です。
r左上座標x/yと幅w・高さhを持つKQRectの値。
戻り値
範囲内なら1、範囲外なら0です。
使用条件と制限
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
使用例
result[16]=kq2d_point_in_rect(0,0,r1);
result[17]=kq2d_point_in_rect(16,0,r1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:72
Delegate the half-open point test: include left/top and exclude right/bottom. Coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/physics2d.c:705
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r)
{
return kq_point_in_rect(x, y, r);
}kq2d_rect_intersect
左上座標・幅・高さで表した2個の矩形について、内部の重なりを調べます。
書式
u8 kq2d_rect_intersect(KQRect a, KQRect b);引数
a左上座標と幅・高さを持つKQRectの値。幅と高さには正の値を指定します。
b左上座標と幅・高さを持つKQRectの値。幅と高さには正の値を指定します。
戻り値
内部が重なれば1、接触のみまたは離れていれば0です。
使用条件と制限
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
0以下の幅や高さを除外するチェックはありません。bodyの中心・半サイズをそのままKQRectへ入れないでください。
使用例
result[14]=kq2d_rect_intersect(r1,r2);
r2.x=16; result[15]=kq2d_rect_intersect(r1,r2);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:69
Delegate strict rectangle overlap to the fixed-point utility; edge-only contact is false. Coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/physics2d.c:698
u8 kq2d_rect_intersect(KQRect a, KQRect b)
{
return kq_rect_intersect(a, b);
}kq2d_scale_q8
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
符号付き係数/256を掛け、正負どちらも0方向へ切り捨てます。
書式
s16 kq2d_scale_q8(s16 value, s16 coefficient);引数
value倍率を掛ける符号付き16ビットの値。
coefficient256を1.0とする符号付き係数(−256~256)。
戻り値
0方向に切り捨てた符号付き16ビットの結果を返します。
使用条件と制限
係数は−256~256だけを指定してください。±256では倍率が±1になります。value=−32768とcoefficient=−256の組み合わせの数学的な結果32768はs16に収まりません。
使用例
result[0]=kq2d_scale_q8(-257,128);
result[1]=kq2d_scale_q8(300,-256);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い物体の位置は変わりません。緑の速度線は左の(8,16)から右の(2,−8)へ変わり、青い面に対する反発と接線摩擦を表します。図の原点は画面(8,48)・(88,48)、物体中心は図内(24,40)です。接近速度は16、交差時刻の例は192/256です。速度制限の例では(300,400)が(147,196)になります。DMGでは全て黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:88
Truncates toward zero, including negative values; coefficient is -256..256.
kitaqgb/lib/physics2d.c:8
s16 kq2d_scale_q8(s16 value, s16 coefficient)
{
// Eight-bit shift/add product in HL:C, with sign applied after truncation.
__asm {
LD_HL_IMM value
LD_A_HL
LD_E_A
INC_HL
LD_A_HL
LD_D_A
OR_E
JP_Z kqscale_zero
LD_HL_IMM coefficient
LD_A_HL
LD_C_A
INC_HL
LD_A_HL
LD_B_A
LD_A_C
OR_A
JR_NZ kqscale_fraction
LD_A_B
OR_A
JP_Z kqscale_zero
LD_H_D
LD_L_E
AND_IMM 128
RET_Z
JP kqscale_negate
kqscale_fraction:
LD_A_D
XOR_B
AND_IMM 128
PUSH_AF
LD_A_D
AND_IMM 128
JR_Z kqscale_value_positive
XOR_A
SUB_E
LD_E_A
LD_A_IMM 0
SBC_D
LD_D_A
kqscale_value_positive:
LD_A_B
AND_IMM 128
JR_Z kqscale_coefficient_positive
XOR_A
SUB_C
LD_C_A
kqscale_coefficient_positive:
LD_HL_IMM 0
// Unroll eight bits: avoid a loop branch on every fixed-point product.
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_0
ADD_HL_DE
kqscale_shift_0:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_1
ADD_HL_DE
kqscale_shift_1:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_2
ADD_HL_DE
kqscale_shift_2:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_3
ADD_HL_DE
kqscale_shift_3:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_4
ADD_HL_DE
kqscale_shift_4:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_5
ADD_HL_DE
kqscale_shift_5:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_6
ADD_HL_DE
kqscale_shift_6:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_7
ADD_HL_DE
kqscale_shift_7:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
POP_AF
OR_A
RET_Z
kqscale_negate:
XOR_A
SUB_L
LD_L_A
LD_A_IMM 0
SBC_H
LD_H_A
RET
kqscale_zero:
LD_HL_IMM 0
RET
}
}kq2d_step
有効な動的bodyを積分した後、配列内の有効なbody同士の接触をまとめて解決します。
書式
void kq2d_step(KQWorld2D* world);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
接触補正は侵入の浅い軸を選び、同値ならY軸を選びます。逆質量で位置補正を分けますが、動く2物体の軸方向速度は単純平均、固定物との衝突では0になります。restitution_q8はこのソルバーでは使いません。高速なすり抜けを防ぐ連続衝突判定ではありません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
worldまたは配列がNULLなら何もしません。solver_iterations=0でも接触処理は1回行います。
使用例
kq2d_step(&world);
result[22]=bodies[0].x; result[23]=bodies[0].y;
result[24]=bodies[0].vx; result[25]=bodies[0].vy;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:43
Advance active dynamic bodies, then visit each active unordered pair for the configured contact passes (zero means one). Static bodies still participate as obstacles. The world borrows its array and shares non-reentrant ASM scratch. This is discrete integration; fast bodies can pass through thin obstacles.
kitaqgb/lib/physics2d.c:794
void kq2d_step(KQWorld2D* world)
{
if (world == 0) return;
if (world->bodies == 0) return;
KQBody2D* bodies = world->bodies;
u8 count = world->body_count;
// Integrate dynamic bodies.
u8 i;
for (i = 0; i < count; i++)
{
KQBody2D* b = &bodies[(__safe_index u8)i];
if (b->active == 0) continue;
if (b->inv_mass_q8 <= 0) continue;
kq2d_asm_value = b->vx;
kq2d_asm_operand = world->gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
b->vx = kq2d_asm_value;
kq2d_asm_value = b->vy;
kq2d_asm_operand = world->gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
b->vy = kq2d_asm_value;
kq2d_asm_value = b->x;
kq2d_asm_operand = b->vx;
kq2d_add_s16_asm();
b->x = kq2d_asm_value;
kq2d_asm_value = b->y;
kq2d_asm_operand = b->vy;
kq2d_add_s16_asm();
b->y = kq2d_asm_value;
}
// Solve contacts with a small fixed iteration count.
u8 iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
u8 it;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQBody2D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody2D* b = &bodies[(__safe_index u8)j];
if (b->active == 0) continue;
kq2d__resolve_pair(a, b);
}
}
}
}kq2d_surface_toi_q8
開始・終了の符号付き距離から、線形運動で面を横切る刻み内時刻を見積もります。
書式
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap);引数
start_gap刻み開始時の面までの符号付き距離。正は面の外側です。
end_gap刻み終了時の面までの符号付き距離。負は面を通過した側です。
戻り値
0~256のQ8時刻を返します。192は刻みの75%です。
使用条件と制限
距離は±8191以内にしてください。開始距離0以下は0、終了距離0以上は256です。初期重なりと時刻0、交差なしとちょうど終点での接触は同じ戻り値になります。位置は変更しません。
使用例
result[4]=kq2d_surface_toi_q8(6,-2);
result[5]=kq2d_surface_toi_q8(6,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い物体の位置は変わりません。緑の速度線は左の(8,16)から右の(2,−8)へ変わり、青い面に対する反発と接線摩擦を表します。図の原点は画面(8,48)・(88,48)、物体中心は図内(24,40)です。接近速度は16、交差時刻の例は192/256です。速度制限の例では(300,400)が(147,196)になります。DMGでは全て黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:98
Linear crossing time in Q8 (0..256); positive separation is outside. Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.
kitaqgb/lib/physics2d.c:291
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap)
{
if (start_gap <= 0) return 0;
if (end_gap >= 0) return 256;
return kq2d__fraction_q8((u16)start_gap, (u16)(start_gap - end_gap));
}kq2d_world_init
2Dワールドへ配列を関連付け、重力(0,32)、接触反復4回、軸速度上限512を設定します。
書式
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
bodies呼び出し側が保持するbody配列の先頭。body_count個の要素を用意します。
body_count配列中のbody数(0~255)。無効な要素も個数に含めます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
使用例
kq2d_world_init(&world,bodies,2);
result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左が衝突前、右が1刻み後です。赤い16×16の箱は中心(24,24)・速度(4,16)から中心(28,36)へ進み、青い床の上でY速度が0になります。各図の原点は画面(8,48)と(88,48)です。DMGではすべて黒で描きます。数値検証では初期値、設定、積分、矩形・点判定、符号付き倍率も確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d.h:38
Retain the caller-owned body array without initializing its elements. Set gravity to (0,32), four contact iterations and a per-axis speed limit of 512. A null world is ignored; the array must outlive subsequent world operations.
kitaqgb/lib/physics2d.c:564
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 32;
world->solver_iterations = 4;
world->max_speed = 512;
}physics2d_circle.h — 円同士の物理計算
円の半径、逆質量、反発、接線摩擦、世界の減衰と境界を扱います。bodyは呼び出し側で全フィールドを設定します。bodyのfriction_q8と壁のwall_friction_q8は「除く接線運動」、linear_damping_q8は「残す速度」の割合です。0~255の係数は256で割って使うため255も正確な1.0ではありません。
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
円の距離と法線は整数近似です。成分を255以下へ縮小しても、二乗和が65535を超える場合があります。中心差・半径和・逆質量和を有効範囲に収め、重み計算のinv_mass_q8×255も32767以下にしてください(サンプルは64)。
kq2dc_overlap_circle
縮小した中心距離と半径和を使い、2個の円の内部が重なるか近似判定します。
書式
u8 kq2dc_overlap_circle(const KQCircleBody2D* a, const KQCircleBody2D* b);引数
a判定する1個目の円への有効なポインタ。
b判定する2個目の円への有効なポインタ。
戻り値
内部が重なれば1、接触のみまたは離れていれば0です。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
円の距離と法線は整数近似です。成分を255以下へ縮小しても、二乗和が65535を超える場合があります。中心差・半径和・逆質量和を有効範囲に収め、重み計算のinv_mass_q8×255も32767以下にしてください(サンプルは64)。
有効な2ポインタと正の半径が必要です。active・質量は無視します。縮小・整数平方根により、幾何学的に厳密な距離判定とは一致しない場合があります。
使用例
result[4]=kq2dc_overlap_circle(&balls[0],&balls[1]);
balls[1].x=18; result[5]=kq2dc_overlap_circle(&balls[0],&balls[1]);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
半径6の赤い円が、左の中心(24,24)から右の(54,24)へ進みます。図内の境界は(4,4)~(60,60)です。右壁で中心が54に制限され、X速度は32から−15へ変わります。壁反発128/256に続き、減衰255/256が掛かります。図の原点は画面(8,48)・(88,48)。DMGでは円と枠を黒で表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d_circle.h:55
Compare a reduced integer center distance with the reduced radius sum. Valid pointers and positive radii are required; activity and mass are ignored. This approximate test requires representable differences, radius sums and squared-distance sum. Touching at the computed distance returns false.
kitaqgb/lib/physics2d_circle.c:397
u8 kq2dc_overlap_circle(const KQCircleBody2D* a, const KQCircleBody2D* b)
{
s16 dx;
s16 dy;
s16 sdx;
s16 sdy;
u8 shift;
u16 adx;
u16 ady;
u16 dist_sq;
u16 dist_scaled;
s16 radius_sum;
u16 radius_sum_scaled;
dx = (s16)(b->x - a->x);
dy = (s16)(b->y - a->y);
sdx = dx;
sdy = dy;
shift = kq2dc__reduce_vector(&sdx, &sdy);
adx = (u16)kq2dc__abs_s16(sdx);
ady = (u16)kq2dc__abs_s16(sdy);
dist_sq = (u16)((adx * adx) + (ady * ady));
dist_scaled = kq2dc__isqrt_u16(dist_sq);
radius_sum = (s16)(a->radius + b->radius);
radius_sum_scaled = kq2dc__scale_down_positive(radius_sum, shift);
if (dist_scaled >= radius_sum_scaled) return 0;
return 1;
}kq2dc_set_bounds
円の行動範囲を保存して境界処理を有効にします。現在の位置は動かしません。
書式
void kq2dc_set_bounds(KQCircleWorld2D* world, s16 min_x, s16 min_y, s16 max_x, s16 max_y);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
min_x境界の左端。円の中心の最小値はmin_x+radiusです。
min_y境界の上端。円の中心の最小値はmin_y+radiusです。
max_x境界の右端。円の中心の最大値はmax_x−radiusです。
max_y境界の下端。円の中心の最大値はmax_y−radiusです。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
NULL worldは無視します。上下・左右の順序や円が収まるかは検証しないため、幅・高さを円の直径以上にしてください。実際の位置補正はstepで行います。
使用例
kq2dc_set_bounds(&world,4,4,60,60);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
半径6の赤い円が、左の中心(24,24)から右の(54,24)へ進みます。図内の境界は(4,4)~(60,60)です。右壁で中心が54に制限され、X速度は32から−15へ変わります。壁反発128/256に続き、減衰255/256が掛かります。図の原点は画面(8,48)・(88,48)。DMGでは円と枠を黒で表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d_circle.h:45
Store and enable bounds without validating their order or fitting bodies immediately. Supply extents large enough for body diameters. Null is ignored.
kitaqgb/lib/physics2d_circle.c:382
void kq2dc_set_bounds(KQCircleWorld2D* world, s16 min_x, s16 min_y, s16 max_x, s16 max_y)
{
if (world == 0) return;
world->use_bounds = 1;
world->min_x = min_x;
world->min_y = min_y;
world->max_x = max_x;
world->max_y = max_y;
}kq2dc_step
円を積分し、境界・円同士の接触を解決してから線形減衰を掛けます。
書式
void kq2dc_step(KQCircleWorld2D* world);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
円の距離と法線は整数近似です。成分を255以下へ縮小しても、二乗和が65535を超える場合があります。中心差・半径和・逆質量和を有効範囲に収め、重み計算のinv_mass_q8×255も32767以下にしてください(サンプルは64)。
最後に速度の−1~1を0へ揃えます。減衰255でも完全維持ではありません。静止した円同士の重なりは解消されない場合があります。固定円も有効な境界に押し戻されます。反復0は1回、NULL world/配列は無視します。速度更新で接触の訪問順が変わり得る、離散的で量子化されたソルバーです。
使用例
kq2dc_step(&world);
result[6]=balls[0].x; result[7]=balls[0].y; result[8]=balls[0].vx;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
半径6の赤い円が、左の中心(24,24)から右の(54,24)へ進みます。図内の境界は(4,4)~(60,60)です。右壁で中心が54に制限され、X速度は32から−15へ変わります。壁反発128/256に続き、減衰255/256が掛かります。図の原点は画面(8,48)・(88,48)。DMGでは円と枠を黒で表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d_circle.h:50
Integrate active dynamic bodies, apply bounds, solve contacts, then damp velocity and snap components in -1..1 to zero. Zero solver iterations means one. Pairs with both velocities zero are skipped, so resting overlaps can remain. Bounds can reposition static bodies; null world/array inputs are ignored.
kitaqgb/lib/physics2d_circle.c:433
void kq2dc_step(KQCircleWorld2D* world)
{
KQCircleBody2D* bodies;
u8 count;
u8 i;
u8 iterations;
u8 it;
if (world == 0) return;
if (world->bodies == 0) return;
bodies = world->bodies;
count = world->body_count;
for (i = 0; i < count; i++)
{
KQCircleBody2D* body = &bodies[(__safe_index u8)i];
if (body->active == 0) continue;
if (body->inv_mass_q8 <= 0)
{
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
continue;
}
body->vx = (s16)(body->vx + world->gravity_x);
body->vy = (s16)(body->vy + world->gravity_y);
body->vx = kq2dc__clamp_abs(body->vx, world->max_speed);
body->vy = kq2dc__clamp_abs(body->vy, world->max_speed);
body->x = (s16)(body->x + body->vx);
body->y = (s16)(body->y + body->vy);
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
}
iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQCircleBody2D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
if (a->vx == 0 && a->vy == 0) continue;
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, a);
{
u8 j;
for (j = 0; j < count; j++)
{
KQCircleBody2D* b = &bodies[(__safe_index u8)j];
s16 radius_sum;
if (j == i) continue;
if (b->active == 0) continue;
// Avoid revisiting an earlier moving body, but still allow a moving body
// to resolve against a stationary slot with a lower index. Velocities are live
// solver state, so contact ordering can change which later pairs are visited.
if (j < i && (b->vx != 0 || b->vy != 0)) continue;
radius_sum = (s16)(a->radius + b->radius);
if (kq2dc__abs_s16((s16)(b->x - a->x)) >= radius_sum) continue;
if (kq2dc__abs_s16((s16)(b->y - a->y)) >= radius_sum) continue;
kq2dc__resolve_pair(a, b);
}
}
}
}
for (i = 0; i < count; i++)
{
KQCircleBody2D* body = &bodies[(__safe_index u8)i];
if (body->active == 0) continue;
if (body->inv_mass_q8 <= 0) continue;
body->vx = kq2dc__mul_q8(body->vx, world->linear_damping_q8);
body->vy = kq2dc__mul_q8(body->vy, world->linear_damping_q8);
if (body->vx >= -1 && body->vx <= 1) body->vx = 0;
if (body->vy >= -1 && body->vy <= 1) body->vy = 0;
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
}
}kq2dc_world_init
円のワールドへ配列を関連付け、重力0・反復4・軸速度上限768・減衰252/256を設定します。
書式
void kq2dc_world_init(KQCircleWorld2D* world, KQCircleBody2D* bodies, u8 body_count);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
bodies呼び出し側が保持するbody配列の先頭。body_count個の要素を用意します。
body_count配列中のbody数(0~255)。無効な要素も個数に含めます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
境界は無効で初期範囲は(0,0)~(159,143)、壁反発は240、壁摩擦は16です。円の初期化関数はないため、各bodyの全フィールドを呼び出し側で設定します。NULL worldは無視します。
使用例
kq2dc_world_init(&world,balls,1);
result[0]=world.gravity_y; result[1]=world.solver_iterations;
result[2]=world.max_speed; result[3]=world.linear_damping_q8;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
半径6の赤い円が、左の中心(24,24)から右の(54,24)へ進みます。図内の境界は(4,4)~(60,60)です。右壁で中心が54に制限され、X速度は32から−15へ変わります。壁反発128/256に続き、減衰255/256が掛かります。図の原点は画面(8,48)・(88,48)。DMGでは円と枠を黒で表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics2d_circle.h:42
Retain a caller-owned array without initializing bodies. Set zero gravity, four contact passes, per-axis limit 768 and damping 252/256; bounds start disabled. A null world is ignored. Keep the array alive for subsequent steps.
kitaqgb/lib/physics2d_circle.c:360
void kq2dc_world_init(KQCircleWorld2D* world, KQCircleBody2D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 0;
world->solver_iterations = 4;
world->max_speed = 768;
world->linear_damping_q8 = 252;
world->use_bounds = 0;
world->min_x = 0;
world->min_y = 0;
world->max_x = 159;
world->max_y = 143;
world->wall_restitution_q8 = 240;
world->wall_friction_q8 = 16;
}physics3d.h — 3DのAABB物理計算
回転しない3Dの箱を中心と各軸の半サイズで表す物理ライブラリです。質量・逆質量は接触の重み、restitution_q8は反発係数、ax/ay/azは持続する加速度です。表示は別の処理で行います。サンプルは同じ物体を正面と上面に描き、Z方向の動きと壁の反発を分けて示します。
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
last_impact_speedには軸方向の接近速度の最大値が記録されます。動的な有効bodyは積分時に0へ戻り、固定・無効bodyは値を保持します。break_speedやKQ3D_BODY_BROKENを見て自動的に壊す処理はありません。破壊判定・flags・activeの更新はゲーム側で行います。
kq3d_body_init
3Dの中心・半サイズ・質量を設定し、速度・加速度・衝撃値・flagsを0にして有効化します。
書式
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
x中心または点のX座標。サンプルではピクセル単位です。
y中心または点のY座標。サンプルでは下向きが正です。
z中心のZ座標。サンプルでは上面図の縦軸に表示します。
half_xX方向の半サイズ。箱の全幅はこの値の2倍です。
half_yY方向の半サイズ。箱の全高はこの値の2倍です。
half_zZ方向の半サイズ。奥行きはこの値の2倍です。
mass_q8256を質量1.0とする値。0以下は固定物体です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
反発係数は224、break_speedは0です。質量設定はkq3d_body_set_massと同じ規則です。NULLは無視し、半サイズの妥当性は呼び出し側が保証します。
使用例
kq3d_body_init(&temp,10,20,30,8,8,8,256);
result[3]=temp.inv_mass_q8; result[4]=temp.restitution_q8; result[5]=temp.active;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:50
Initialize an active centered box with zero velocity/acceleration, restitution 224, cleared impact/flags and mass set through the weight helper. The caller supplies valid half extents. Null body is ignored.
kitaqgb/lib/physics3d.c:330
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8)
{
if (body == 0) return;
body->x = x;
body->y = y;
body->z = z;
body->vx = 0;
body->vy = 0;
body->vz = 0;
body->ax = 0;
body->ay = 0;
body->az = 0;
body->half_x = half_x;
body->half_y = half_y;
body->half_z = half_z;
body->restitution_q8 = 224;
body->break_speed = 0;
body->last_impact_speed = 0;
body->active = 1;
body->flags = 0;
kq3d_body_set_mass(body, mass_q8);
}kq3d_body_set_mass
要求した質量を保存し、接触補正・反発に使う逆質量の重みを計算します。
書式
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
mass_q8256を質量1.0とする値。0以下は固定物体です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
0以下は逆質量0の固定物体です。正なら32767/max(mass,64)を整数除算し、最低1とします。保存するmassは元の値のままで、正確なQ8逆数ではありません。mass=256ではinv_mass=127です。速度やactiveは変更せず、NULLは無視します。
使用例
kq3d_body_set_mass(&temp,32); result[6]=temp.mass_q8; result[7]=temp.inv_mass_q8;
kq3d_body_set_mass(&temp,0); result[8]=temp.inv_mass_q8;
kq3d_body_set_mass(&temp,256);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:55
Store the requested mass; nonpositive mass makes the body static. For positive mass compute inverse weight as max(1,32767/max(mass,64)). The stored mass retains the original input; this is a relative solver weight, not an exact Q8 reciprocal. Null body is ignored.
kitaqgb/lib/physics3d.c:311
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8)
{
if (body == 0) return;
body->mass_q8 = mass_q8;
if (mass_q8 <= 0)
{
body->inv_mass_q8 = 0;
return;
}
if (mass_q8 < 64) mass_q8 = 64;
body->inv_mass_q8 = (s16)(32767 / mass_q8);
if (body->inv_mass_q8 <= 0) body->inv_mass_q8 = 1;
}kq3d_dot_q8_8
3軸の符号付き積をそれぞれ256で割り、0方向へ切り捨ててから足し合わせます。
書式
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az);引数
x対応する係数を掛ける符号付き16ビット成分。位置に限らず速度にも使えます。
y対応する係数を掛ける符号付き16ビット成分。位置に限らず速度にも使えます。
z対応する係数を掛ける符号付き16ビット成分。位置に限らず速度にも使えます。
ax対応軸の符号付き8ビット係数(−128~127)。倍率は係数/256です。
ay対応軸の符号付き8ビット係数(−128~127)。倍率は係数/256です。
az対応軸の符号付き8ビット係数(−128~127)。倍率は係数/256です。
戻り値
量子化した3項の和をs16で返します。
使用条件と制限
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
各項を先に0方向へ切り捨てるため、積の総和を最後に1回だけシフトする内積とは端数が異なります。係数に1.0=256は格納できません。最終和は飽和しません。
使用例
result[13]=kq3d_dot_q8_8(256,128,0,64,-64,127);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:74
Sum three signed products, shifting each by eight bits before addition. Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.
kitaqgb/lib/physics3d.c:576
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az)
{
return __sdot3_q8_8(x, y, z, ax, ay, az);
}kq3d_integrate_body
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
1個の有効な動的bodyの衝撃値を消去し、重力とbody固有の加速度を足して、軸速度制限後に位置を進めます。
書式
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
last_impact_speedには軸方向の接近速度の最大値が記録されます。動的な有効bodyは積分時に0へ戻り、固定・無効bodyは値を保持します。break_speedやKQ3D_BODY_BROKENを見て自動的に壊す処理はありません。破壊判定・flags・activeの更新はゲーム側で行います。
ax・ay・azは呼び出し後も保持され、次の積分でも加算します。この関数単独では衝突を解決しません。NULL、無効、固定bodyは変更しません。
使用例
kq3d_integrate_body(&world,&temp);
result[9]=temp.x; result[10]=temp.y; result[11]=temp.z; result[12]=temp.last_impact_speed;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:66
Reset impact for one active dynamic body, add gravity and persistent body acceleration, clamp velocity per axis and advance position. Static/inactive bodies retain impact state. Null inputs are ignored; no contacts are solved. Use a nonnegative speed limit and avoid overflow before clamping.
kitaqgb/lib/physics3d.c:436
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body)
{
if (world == 0) return;
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
body->last_impact_speed = 0;
kq3d_asm_value = body->vx;
kq3d_asm_operand = world->gravity_x;
kq3d_add_s16_asm();
kq3d_asm_operand = body->ax;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vx = kq3d_asm_value;
kq3d_asm_value = body->vy;
kq3d_asm_operand = world->gravity_y;
kq3d_add_s16_asm();
kq3d_asm_operand = body->ay;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vy = kq3d_asm_value;
kq3d_asm_value = body->vz;
kq3d_asm_operand = world->gravity_z;
kq3d_add_s16_asm();
kq3d_asm_operand = body->az;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vz = kq3d_asm_value;
kq3d_asm_value = body->x;
kq3d_asm_operand = body->vx;
kq3d_add_s16_asm();
body->x = kq3d_asm_value;
kq3d_asm_value = body->y;
kq3d_asm_operand = body->vy;
kq3d_add_s16_asm();
body->y = kq3d_asm_value;
kq3d_asm_value = body->z;
kq3d_asm_operand = body->vz;
kq3d_add_s16_asm();
body->z = kq3d_asm_value;
}kq3d_overlap_aabb
中心と半サイズで表した2個の箱が、X・Y・Zの全軸で厳密に重なるか調べます。
書式
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b);引数
a有効なbodyへのポインタ。照会はactive・質量・flagsを無視します。
b有効なbodyへのポインタ。照会はactive・質量・flagsを無視します。
戻り値
内部が重なれば1、接触のみまたは離れていれば0です。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
使用例
result[14]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);
bodies[1].z=32; result[15]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:70
Test strict overlap on all three axes; face-only contact returns false. Valid pointers are required and flags are ignored. Center differences and extent sums must fit the signed 16-bit magnitude calculations.
kitaqgb/lib/physics3d.c:357
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b)
{
s16 dx = kq3d__abs_s16((s16)(b->x - a->x));
s16 dy = kq3d__abs_s16((s16)(b->y - a->y));
s16 dz = kq3d__abs_s16((s16)(b->z - a->z));
s16 sx = (s16)(a->half_x + b->half_x);
s16 sy = (s16)(a->half_y + b->half_y);
s16 sz = (s16)(a->half_z + b->half_z);
if (dx >= sx) return 0;
if (dy >= sy) return 0;
if (dz >= sz) return 0;
return 1;
}kq3d_step
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
ワールド内の有効な動的bodyを積分し、有効な全bodyの接触補正・反発・衝撃値の記録を行います。
書式
void kq3d_step(KQWorld3D* world);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
侵入量が最小の軸で接触を解決し、同値ならX、Y、Zの順を優先します。接近中だけ、両者の反発係数の平均(0~256に制限)と逆質量で反発を計算します。高速なすり抜け、回転、角運動量、摩擦は扱いません。
last_impact_speedには軸方向の接近速度の最大値が記録されます。動的な有効bodyは積分時に0へ戻り、固定・無効bodyは値を保持します。break_speedやKQ3D_BODY_BROKENを見て自動的に壊す処理はありません。破壊判定・flags・activeの更新はゲーム側で行います。
worldまたは配列がNULLなら何もしません。solver_iterations=0でも接触処理は1回行います。
反発の中間式impact×(256+restitution)や補正量×逆質量もs16です。完全反発256ではimpactを63以下にするなど、積が32767を超えない設定にしてください。
使用例
kq3d_step(&world);
result[16]=bodies[0].x; result[17]=bodies[0].y; result[18]=bodies[0].z;
result[19]=bodies[0].vx; result[20]=bodies[0].vz;
result[21]=bodies[0].last_impact_speed; result[22]=bodies[0].flags;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:61
Integrate active dynamic bodies and reset their impact values, then solve each active unordered pair for the configured passes (zero means one). Static bodies retain previous impact maxima until the game clears them. Acceleration persists; flags and break thresholds remain game-owned. This discrete solver shares non-reentrant ASM scratch and can miss fast crossings.
kitaqgb/lib/physics3d.c:492
void kq3d_step(KQWorld3D* world)
{
if (world == 0) return;
if (world->bodies == 0) return;
KQBody3D* bodies = world->bodies;
u8 count = world->body_count;
u8 i;
for (i = 0; i < count; i++)
{
KQBody3D* b = &bodies[(__safe_index u8)i];
if (b->active == 0) continue;
if (b->inv_mass_q8 <= 0) continue;
b->last_impact_speed = 0;
kq3d_asm_value = b->vx;
kq3d_asm_operand = world->gravity_x;
kq3d_add_s16_asm();
kq3d_asm_operand = b->ax;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vx = kq3d_asm_value;
kq3d_asm_value = b->vy;
kq3d_asm_operand = world->gravity_y;
kq3d_add_s16_asm();
kq3d_asm_operand = b->ay;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vy = kq3d_asm_value;
kq3d_asm_value = b->vz;
kq3d_asm_operand = world->gravity_z;
kq3d_add_s16_asm();
kq3d_asm_operand = b->az;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vz = kq3d_asm_value;
kq3d_asm_value = b->x;
kq3d_asm_operand = b->vx;
kq3d_add_s16_asm();
b->x = kq3d_asm_value;
kq3d_asm_value = b->y;
kq3d_asm_operand = b->vy;
kq3d_add_s16_asm();
b->y = kq3d_asm_value;
kq3d_asm_value = b->z;
kq3d_asm_operand = b->vz;
kq3d_add_s16_asm();
b->z = kq3d_asm_value;
}
u8 iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
u8 it;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQBody3D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody3D* b = &bodies[(__safe_index u8)j];
if (b->active == 0) continue;
kq3d__resolve_pair(a, b);
}
}
}
}kq3d_world_init
3Dワールドへ配列を関連付け、重力(0,24,0)・反復4・軸速度上限512を設定します。
書式
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
bodies呼び出し側が保持するbody配列の先頭。body_count個の要素を用意します。
body_count配列中のbody数(0~255)。無効な要素も個数に含めます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
physics3d.cはROMバンク2に配置されます。サンプルはMBC5・128KB ROMで、本体を先に読み込み、mainをバンク0に配置しています。呼び出し中に外部処理でROMバンクを切り替えないでください。
使用例
kq3d_world_init(&world,bodies,2);
result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左はX/Y正面図、右はX/Z上面図です。緑の枠が初期位置(24,24,16)、赤い箱が1刻み後の(36,24,24)、青い領域が固定障害物です。X速度16は−14へ反発し、Z速度8は保持されます。衝撃値は16ですが、break_speed=1でもflagsは0のままです。図の原点は画面(8,48)・(88,48)。DMGでは枠・箱・障害物が黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm実装とソース
kitaqgb/lib/physics3d.h:46
Borrow the body array without initializing it. Set gravity (0,24,0), four contact passes and a per-axis speed limit of 512. Null world is ignored; the caller retains ownership and must keep the array alive.
kitaqgb/lib/physics3d.c:294
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 24;
world->gravity_z = 0;
world->solver_iterations = 4;
world->max_speed = 512;
}raster.h — 帯・行単位のスクロール表
Rasterライブラリには、最大8個の境界を登録するバンド方式と、全144走査線のXを動かすLineX方式があります。バンド方式は固定HUD・多重スクロール・ウィンドウ切り替え・CGB色帯に向きます。LineX方式は波打つタイトルや画面演出に使います。両方式を同時には動かさず、必要な方式を選んでください。
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
1ラインずつ波打つ背景とタイトルの登場
背景の横スクロール値を画面の各行で変えると、縦の柱や文字が波の形に曲がります。転送・ワープの場面、ボスの出現、タイトルの登場などに使える表現です。下の動画は、実行結果の8フレーム分の画像を並べたプレビューです。ROMでは波の位相が毎フレーム進みます。
Raster_Push系は最大8本の帯を切り替える機能です。1ラインずつ変形させる演出にはRaster_LineXRunFrameを使います。スプライトとウィンドウは変形しません。処理中は可視144ラインにわたってCPUを専有し、割り込みを止めるため、ゲームや音声の更新は呼び出し間に行います。

タイトル例は強い波で右から入り、小さな揺れを経て静止します。同じフォルダーのraster_wave.cを共有するため、両方のソースファイルを一緒に使ってください。ビルド方法と各関数の使い方は下の項目に掲載しています。
Rasterライブラリには、最大8個の境界を登録するバンド方式と、全144走査線のXを動かすLineX方式があります。バンド方式は固定HUD・多重スクロール・ウィンドウ切り替え・CGB色帯に向きます。LineX方式は波打つタイトルや画面演出に使います。両方式を同時には動かさず、必要な方式を選んでください。
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
Raster_BuildHudBottom
上側をスクロールするプレイ領域、下側を固定HUDとする2バンドを作ります。LY=0はカメラ座標、hud_y以降は背景(0,0)です。
書式
u8 Raster_BuildHudBottom(u8 hud_y, u8 camera_x, u8 camera_y);引数
hud_y分割する行1~143です。0や144以上はBAD_RANGEとなります。
camera_x背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
camera_y背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
範囲が正しければ既存の分割を停止して表を消し、新しいバンドを登録します。範囲が不正なら表と個数をそのままにしてBAD_RANGEを保存します。成功後もRaster_Commitが必要です。
使用例
Raster_BuildHudBottom(96,8,8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
96行目から下を固定します。上の2個の矩形はカメラ(8,8)により(24,8)と(24,72)、下の矩形は(32,112)です。下端のFIXED BOTTOM HUDも固定します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_hud_bottom.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:73
Build a scrolling upper playfield and fixed bottom HUD after validating the split.
kitaqgb/lib/raster.c:183
u8 Raster_BuildHudBottom(u8 hud_y, u8 camera_x, u8 camera_y)
{
u8 r;
if (hud_y == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (hud_y >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, camera_x, camera_y);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(hud_y, 0, 0);
}Raster_BuildHudTop
上側を固定したHUD用に、LY=0で背景(0,0)、hud_heightでカメラ座標となる2バンドを作ります。ウィンドウではなく背景スクロールの分割です。
書式
u8 Raster_BuildHudTop(u8 hud_height, u8 camera_x, u8 camera_y);引数
hud_height分割する行1~143です。0や144以上はBAD_RANGEとなります。
camera_x背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
camera_y背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
範囲が正しければ既存の分割を停止して表を消し、新しいバンドを登録します。範囲が不正なら表と個数をそのままにしてBAD_RANGEを保存します。成功後もRaster_Commitが必要です。
使用例
Raster_BuildHudTop(32,8,8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上端32行を固定します。上の矩形は(32,16)のままで、下の2個はカメラ(8,8)により(24,72)と(24,104)へ移ります。上端のFIXED TOP HUDは動きません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_hud_top.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:71
Validate the HUD height, then replace pending splits with a fixed top band and scrolling playfield. The new table still requires an explicit commit.
kitaqgb/lib/raster.c:169
u8 Raster_BuildHudTop(u8 hud_height, u8 camera_x, u8 camera_y)
{
u8 r;
if (hud_height == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (hud_height >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, 0, 0);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(hud_height, camera_x, camera_y);
}Raster_BuildParallax2
上下2段の背景を別々のXスクロール値で描く表を作ります。LY=0はfar_scx、split_y以降はnear_scxとなり、Yは共通です。
書式
u8 Raster_BuildParallax2(u8 split_y, u8 far_scx, u8 near_scx, u8 scy);引数
split_y分割する行1~143です。0や144以上はBAD_RANGEとなります。
far_scx背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
near_scx背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scy背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
範囲が正しければ既存の分割を停止して表を消し、新しいバンドを登録します。範囲が不正なら表と個数をそのままにしてBAD_RANGEを保存します。成功後もRaster_Commitが必要です。
使用例
Raster_BuildParallax2(64,0,16,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
64行目でXを0から16へ切り替えます。上の矩形は(32,16)、下の2個は(16,80)と(16,112)となり、下側だけ16ピクセル左へ動きます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_parallax2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:76
Build two horizontal-scroll bands sharing one vertical scroll value.
kitaqgb/lib/raster.c:197
u8 Raster_BuildParallax2(u8 split_y, u8 far_scx, u8 near_scx, u8 scy)
{
u8 r;
if (split_y == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (split_y >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, far_scx, scy);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(split_y, near_scx, scy);
}Raster_BuildParallax3
LY=0・y1・y2の3段に対して、それぞれscx0・scx1・scx2を登録します。遠景・中景・近景の横移動を分け、Yは共通にできます。
書式
u8 Raster_BuildParallax3(u8 y1, u8 y2, u8 scx0, u8 scx1, u8 scx2, u8 scy);引数
y1分割行です。1 ≤ y1 < y2 ≤ 143を満たしてください。
y2分割行です。1 ≤ y1 < y2 ≤ 143を満たしてください。
scx0背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scx1背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scx2背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scy背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
範囲が正しければ既存の分割を停止して表を消し、新しいバンドを登録します。範囲が不正なら表と個数をそのままにしてBAD_RANGEを保存します。成功後もRaster_Commitが必要です。
使用例
Raster_BuildParallax3(64,104,0,8,16,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
64行目と104行目でXを0→8→16へ切り替えます。矩形は(32,16)、(24,80)、(16,112)へ段階的に左へずれ、3段の差が見えます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_parallax3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:79
Build three horizontal-scroll bands at ordered visible split lines. Invalid parameters leave the previous pending table intact and set the error status.
kitaqgb/lib/raster.c:212
u8 Raster_BuildParallax3(u8 y1, u8 y2, u8 scx0, u8 scx1, u8 scx2, u8 scy)
{
u8 r;
if (y1 == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (y2 >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (y2 <= y1) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, scx0, scy);
if (r != KQ_RASTER_OK) return r;
r = Raster_Push(y1, scx1, scy);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(y2, scx2, scy);
}Raster_Clear
実行中の分割をただちに停止し、登録個数・行順序・エラーをリセットします。同じ場面で新しい表を組み直す前に使います。表のデータ領域全体を0埋めする操作ではありません。
書式
void Raster_Clear();戻り値
戻り値はありません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
分割停止はSCX・SCY・WX・WYを0へ戻す操作ではありません。通常の背景・ウィンドウ設定にはScroll_SetBgなどを使います。IEのVBlank/STATビットやIMEを元の状態に戻す関数でもありません。
使用例
Raster_Clear();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:39
Stop splits immediately and clear bookkeeping. Build and commit a new table to restart.
kitaqgb/lib/raster.c:65
void Raster_Clear()
{
Scroll_SplitReset();
Raster_CountValue = 0;
Raster_LastLyValue = 0;
Raster_HasLastLy = 0;
Raster_LastErrorValue = KQ_RASTER_OK;
}Raster_Commit
構築した共有表を有効にします。先にPushが失敗していても、それまでに登録できた表を適用します。呼び出し側で各登録の成否を確認してください。
書式
u8 Raster_Commit();戻り値
保存中の最終エラーコードを返します。0だからといって、それ以前のすべての登録が成功した証明にはなりません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
空でない表ではIEのVBlank/STATビットを立て、EIで割り込みを有効にします。他のIEビットは保持します。空の表では分割を停止し、割り込み全体の設定は復元しません。
使用例
result[2]=Raster_Commit();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:67
Publish pending splits and return the stored status. This commits even if an earlier append failed; callers should inspect errors while building the table.
kitaqgb/lib/raster.c:161
u8 Raster_Commit()
{
Scroll_SplitCommit();
return Raster_LastErrorValue;
}Raster_Disable
Clearの後で空の表をCommitし、ラスターバンドを無効にします。スクロール分割のない場面へ移る際の明示的な停止操作です。
書式
void Raster_Disable();戻り値
戻り値はありません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
分割停止はSCX・SCY・WX・WYを0へ戻す操作ではありません。通常の背景・ウィンドウ設定にはScroll_SetBgなどを使います。IEのVBlank/STATビットやIMEを元の状態に戻す関数でもありません。
使用例
Raster_Disable();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:41
Commit an empty split table so no raster bands remain active.
kitaqgb/lib/raster.c:75
void Raster_Disable()
{
Raster_Clear();
Scroll_SplitCommit();
}Raster_GetCount
Rasterラッパーで登録した現在のバンド数を読みます。Clear/Disable後は0、Commitだけでは個数を消しません。
書式
u8 Raster_GetCount();戻り値
登録済みの個数0~8です。空き数が必要なら8から引きます。読み取りで状態は変わりません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
result[0]=Raster_GetCount();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:44
Read the number of bands currently recorded by this wrapper.
kitaqgb/lib/raster.c:82
u8 Raster_GetCount()
{
return Raster_CountValue;
}Raster_GetLastError
最後に保存されたRaster状態コードを読みます。読み取りでエラーは消えません。過去に発生したすべてのエラーを蓄積する機能ではありません。
書式
u8 Raster_GetLastError();戻り値
保存中の状態コード0~4です。Commitはこの値を返しますが、それまでの失敗を自動で取り消したり表を破棄したりしません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
result[1]=Raster_GetLastError();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:46
Read the last stored status without clearing it.
kitaqgb/lib/raster.c:88
u8 Raster_GetLastError()
{
return Raster_LastErrorValue;
}Raster_Init
共有バンド表を空にして分割を停止し、個数・最終LYの有効フラグ・エラーを初期化します。背景やウィンドウの座標を初期化するScroll_Initとは役割が異なります。
書式
void Raster_Init();戻り値
戻り値はありません。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
分割停止はSCX・SCY・WX・WYを0へ戻す操作ではありません。通常の背景・ウィンドウ設定にはScroll_SetBgなどを使います。IEのVBlank/STATビットやIMEを元の状態に戻す関数でもありません。
使用例
Raster_Init();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:37
Stop split scheduling, empty the shared table and clear error/count state.
kitaqgb/lib/raster.c:59
void Raster_Init()
{
Raster_Clear();
}Raster_LineXGetOffset
指定ラインで使うSCXを計算します。状態や画面は変更せず、位相も進めません。
書式
u8 Raster_LineXGetOffset(const RasterLineX* effect, u8 ly);引数
effect初期化済みのRasterLineXへのポインターです。呼び出し中は有効な領域を保持してください。
ly波形を調べるライン番号(0~255)です。可視画面のラインは0~143です。範囲外エラーは返さず、波形の周期で折り返します。
戻り値
基準横位置とその行の波形変位を加えた8ビットのSCXです。サンプルの位相8・行0では17になります。
使用条件と制限
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
wave_lookup = Raster_LineXGetOffset(&wave, 0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦の柱の縁と文字が、横方向に±24ピクセル曲がります。64ラインで波が一周し、波の位相は1フレームごとに1ライン分進みます。CGBでは青い柱と文字になります。画面全体が同じ量だけ動くのではなく、各行の横位置が異なることを見てください。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png実装とソース
kitaqgb/lib/raster.h:91
Look up the selected periodic profile at LY + phase and add base X modulo 256. An unknown profile returns the unmodified base X.
kitaqgb/lib/raster.c:264
u8 Raster_LineXGetOffset(const RasterLineX* effect, u8 ly)
{
u8 index;
if (effect->profile == KQ_RASTER_LINE_X_TITLE) {
index = (u8)((ly + effect->phase) & 15);
return (u8)(effect->base_x + Raster_LineXXTitle[(__safe_index u8)index]);
}
if (effect->profile == KQ_RASTER_LINE_TRAVEL_GATE) {
index = (u8)((ly + effect->phase) & 63);
return (u8)(effect->base_x + Raster_LineXTravelGate[(__safe_index u8)index]);
}
return effect->base_x;
}Raster_LineXInit
波形の種類と基準横位置を設定し、位相0・毎フレーム1段階の速度で初期化します。描画やスクロールレジスターの書き込みは行いません。
書式
void Raster_LineXInit(RasterLineX* effect, u8 profile, u8 base_x);引数
effect書き込み可能なRasterLineXへのポインターです。事前の初期化は不要です。
profile0(FLAT)は変位なし、1(X_TITLE)は16ライン周期で−4~+3ピクセル、2(TRAVEL_GATE)は64ライン周期で±24ピクセルです。それ以外は変位なしになります。
base_x波形変位に加える背景の横位置(0~255ピクセル)です。
戻り値
戻り値はありません。
使用条件と制限
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
Raster_LineXInit(&wave, KQ_RASTER_LINE_TRAVEL_GATE, 0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦の柱の縁と文字が、横方向に±24ピクセル曲がります。64ラインで波が一周し、波の位相は1フレームごとに1ライン分進みます。CGBでは青い柱と文字になります。画面全体が同じ量だけ動くのではなく、各行の横位置が異なることを見てください。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png実装とソース
kitaqgb/lib/raster.h:82
Initialize a scanline-wave state with phase zero and one phase step per frame.
kitaqgb/lib/raster.c:229
void Raster_LineXInit(RasterLineX* effect, u8 profile, u8 base_x)
{
effect->profile = profile;
effect->base_x = base_x;
effect->phase = 0;
effect->phase_step = 1;
effect->frame_divider = 1;
effect->frame_counter = 0;
}Raster_LineXRunFrame
VBlankから次の画面の144ラインを描画し、各ラインの背景横位置を更新します。描画後に分周カウンターを進め、指定間隔で位相を更新します。
書式
void Raster_LineXRunFrame(RasterLineX* effect);引数
effect初期化済みのRasterLineXへのポインターです。呼び出し中は有効な領域を保持してください。
戻り値
戻り値はありません。
使用条件と制限
LCDがオフなら、状態を変えず直ちに戻ります。LCDがオンの場合は144行分のSCX更新が済むまで待ちます。割り込みの許可状態を復元し、最後のSCXをScrollヘルパーの追跡値へ反映します。戻った時点がVBlank内である保証はありません。呼び出し直後にVRAMへ直接書き込まず、VBlankを待つか、書き込みタイミングを管理する転送関数を使ってください。
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
Raster_LineXRunFrame(&wave);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦の柱の縁と文字が、横方向に±24ピクセル曲がります。64ラインで波が一周し、波の位相は1フレームごとに1ライン分進みます。CGBでは青い柱と文字になります。画面全体が同じ量だけ動くのではなく、各行の横位置が異なることを見てください。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png実装とソース
kitaqgb/lib/raster.h:96
Render all 144 visible lines with one SCX write per line, then advance phase. This foreground renderer masks interrupts during the visible frame. Do game and audio work between calls; do not combine it with a STAT raster driver. Returns without changing the effect when the LCD is off.
kitaqgb/lib/raster.c:298
void Raster_LineXRunFrame(RasterLineX* effect)
{
u8 interrupt_state;
if ((Raster_LineXLcdc & 0x80) == 0) return;
Raster_LineXTable = Raster_LineXFlat;
Raster_LineXMask = 15;
if (effect->profile == KQ_RASTER_LINE_X_TITLE) {
Raster_LineXTable = Raster_LineXXTitle;
}
if (effect->profile == KQ_RASTER_LINE_TRAVEL_GATE) {
Raster_LineXTable = Raster_LineXTravelGate;
Raster_LineXMask = 63;
}
Raster_LineXPhase = effect->phase;
Raster_LineXBase = effect->base_x;
interrupt_state = __critical_enter();
__asm {
PUSH_AF
PUSH_BC
PUSH_DE
PUSH_HL
// Use the current VBlank when the preceding frame ended there.
// Waiting for another VBlank edge would leave every second frame flat.
raster_linex_vblank:
LDH_A_MEM 0x44
CP_IMM 144
JR_C raster_linex_vblank
LD_B_IMM 0
LD_A_MEM Raster_LineXPhase
LD_C_A
LD_A_MEM Raster_LineXMask
LD_D_A
LD_A_MEM Raster_LineXBase
LD_E_A
raster_linex_line:
LD_A_B
ADD_C
AND_D
LD_L_A
LD_H_IMM 0
PUSH_DE
LD_A_MEM Raster_LineXTable
LD_E_A
LD_A_MEM Raster_LineXTable+1
LD_D_A
ADD_HL_DE
POP_DE
LD_A_HL
ADD_E
PUSH_AF
raster_linex_wait:
LDH_A_MEM 0x44
CP_B
JR_NZ raster_linex_wait
POP_AF
LDH_MEM_A 0x43
INC_B
LD_A_B
CP_IMM 144
JR_NZ raster_linex_line
POP_HL
POP_DE
POP_BC
POP_AF
}
// Synchronize the scroll helper's tracked state with the last hardware write.
__scroll_bg_x_set(Raster_LineXScx);
__critical_leave(interrupt_state);
effect->frame_counter++;
if (effect->frame_counter >= effect->frame_divider) {
effect->frame_counter = 0;
effect->phase = (u8)(effect->phase + effect->phase_step);
}
}Raster_LineXSetPhase
波の現在位置を直接指定します。基準横位置、速度、分周カウンターは変えません。
書式
void Raster_LineXSetPhase(RasterLineX* effect, u8 phase);引数
effect初期化済みのRasterLineXへのポインターです。呼び出し中は有効な領域を保持してください。
phase位相の8ビット値です。実際の波形は種類に応じて16または64で折り返します。
戻り値
戻り値はありません。
使用条件と制限
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
Raster_LineXSetPhase(&wave, 8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦の柱の縁と文字が、横方向に±24ピクセル曲がります。64ラインで波が一周し、波の位相は1フレームごとに1ライン分進みます。CGBでは青い柱と文字になります。画面全体が同じ量だけ動くのではなく、各行の横位置が異なることを見てください。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png実装とソース
kitaqgb/lib/raster.h:88
Set the byte phase directly without resetting the frame-divider counter.
kitaqgb/lib/raster.c:257
void Raster_LineXSetPhase(RasterLineX* effect, u8 phase)
{
effect->phase = phase;
}Raster_LineXSetProfile
波形の種類を変更し、位相とフレーム分周カウンターを0に戻します。基準横位置と速度は保持します。
書式
void Raster_LineXSetProfile(RasterLineX* effect, u8 profile);引数
effect初期化済みのRasterLineXへのポインターです。呼び出し中は有効な領域を保持してください。
profile0(FLAT)は変位なし、1(X_TITLE)は16ライン周期で−4~+3ピクセル、2(TRAVEL_GATE)は64ライン周期で±24ピクセルです。それ以外は変位なしになります。
戻り値
戻り値はありません。
使用条件と制限
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
Raster_LineXSetProfile(&wave, KQ_RASTER_LINE_X_TITLE);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ORBIT GUARDという独自のタイトルが、強い波を伴って右から入ります。48回描画した後は小さな揺れに切り替わり、96回以降は静止します。160回で演出を繰り返します。掲載画像は登場中、小さな揺れ、静止の順です。






ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_title_entry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_title_entry.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_title_entry.gb --hardware cgb --run-frames 120 --png .\out\raster_title_entry.png実装とソース
kitaqgb/lib/raster.h:84
Change the wave profile and restart its phase/divider counter.
kitaqgb/lib/raster.c:240
void Raster_LineXSetProfile(RasterLineX* effect, u8 profile)
{
effect->profile = profile;
effect->phase = 0;
effect->frame_counter = 0;
}Raster_LineXSetSpeed
位相を進める量と、その更新間隔を設定します。分周カウンターは0へ戻りますが、現在の位相は保持します。
書式
void Raster_LineXSetSpeed(RasterLineX* effect, u8 phase_step, u8 frame_divider);引数
effect初期化済みのRasterLineXへのポインターです。呼び出し中は有効な領域を保持してください。
phase_step1回の更新で加える位相(0~255)です。0は波形を停止させます。255は8ビットの折り返しにより1段階ずつ逆方向へ進みます。
frame_divider何回のRunFrame呼び出しで位相を更新するか(1~255)です。0は1として扱います。LCDがオフで描画しなかった呼び出しは数えません。
戻り値
戻り値はありません。
使用条件と制限
SCXは8ビット値で、背景は256ピクセル幅で折り返します。負の変位は256を法とする値になります。画面外からスクロールしてくる部分も含め、背景マップを用意してください。
対象は背景レイヤーの横スクロールです。スプライトやウィンドウは変形しません。RunFrameは可視144ラインの間CPUを専有し、割り込みを止めます。ゲーム更新や音声更新は呼び出し間に行い、STAT割り込みによる別のラスター処理と併用しないでください。
掲載画像はKOKURA上のDMG/CGB実行結果です。縦の柱と文字を含む160×144画素を各ラインの期待座標と照合しています。実機でのタイミングは未確認です。
kitaqgb・kokura・kitaq-docsを同じ親フォルダーに置き、その親フォルダーでPowerShellを開いてビルドします。ROMはDMG/CGB両対応です。KOKURAの--hardwareにdmgまたはcgbを指定して確認できます。
使用例
Raster_LineXSetSpeed(&wave, 1, 1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦の柱の縁と文字が、横方向に±24ピクセル曲がります。64ラインで波が一周し、波の位相は1フレームごとに1ライン分進みます。CGBでは青い柱と文字になります。画面全体が同じ量だけ動くのではなく、各行の横位置が異なることを見てください。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png実装とソース
kitaqgb/lib/raster.h:86
Configure the phase increment and frame interval; clamp a zero interval to one.
kitaqgb/lib/raster.c:248
void Raster_LineXSetSpeed(RasterLineX* effect, u8 phase_step, u8 frame_divider)
{
effect->phase_step = phase_step;
if (frame_divider == 0) frame_divider = 1;
effect->frame_divider = frame_divider;
effect->frame_counter = 0;
}Raster_Push
LYで背景SCX/SCYを切り替えるバンドを1個追加します。ウィンドウや色は変更しない、背景スクロール専用の登録関数です。
書式
u8 Raster_Push(u8 ly, u8 scx, u8 scy);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
scx背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scy背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
Raster_Push(0,0,0);Raster_Push(64,8,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:50
Append a validated background-scroll band and advance the monotonic-LY bookkeeping. A successful append clears the previous error.
kitaqgb/lib/raster.c:95
u8 Raster_Push(u8 ly, u8 scx, u8 scy)
{
u8 r;
r = Raster_CheckPush(ly);
if (r != KQ_RASTER_OK) return r;
Scroll_SplitPush(ly, scx, scy);
Raster_LastLyValue = ly;
Raster_HasLastLy = 1;
Raster_CountValue++;
Raster_LastErrorValue = KQ_RASTER_OK;
return KQ_RASTER_OK;
}Raster_PushBgColor0
CGBの背景パレット0の色0を走査途中で切り替えるバンドを追加します。背景全体のパレット選択や他の3色を変更する操作ではありません。DMGでは登録できてもカラー効果はありません。
書式
u8 Raster_PushBgColor0(u8 ly, u16 rgb15);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
rgb15RGB15です。赤はビット0~4、緑は5~9、青は10~14です。0x001Fが赤、0x03E0が緑、0x7C00が青です。入力値の範囲検査は行いません。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
色更新はBCPS/BCPDを使い、BCPSの元のインデックスは保持しません。前景処理からパレットを書く際もインデックスを明示してください。転送できるLCDモードを待つので、予約行と実際の色境界には遅れがあります。
使用例
Raster_PushBgColor0(0,0x7C00);Raster_PushBgColor0(64,0x001F);Raster_PushBgColor0(112,0x03E0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGB通常速度の検証画像では、上から行0~65が青、66~113が赤、114~143が緑です。予約LYは0・64・112で、割り込み処理とパレット待ちによる遅延が画面境界に含まれます。DMG版のカラー表示はありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_color.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:63
Pack a 15-bit color into the extended band's two scroll-byte fields and select the color-zero operation, which interprets those fields as color data.
kitaqgb/lib/raster.c:154
u8 Raster_PushBgColor0(u8 ly, u16 rgb15)
{
return Raster_PushEx(ly, (u8)rgb15, (u8)(rgb15 >> 8), 0, 0, KQ_RASTER_BG_COLOR0);
}Raster_PushBgWindowScreen
1個のバンドで背景スクロールとウィンドウ座標を両方更新します。BGとWINを自動で追加し、ウィンドウXを画面座標からWXへ変換します。
書式
u8 Raster_PushBgWindowScreen(u8 ly, u8 scx, u8 scy, u8 screen_x, u8 screen_y, u8 flags);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
scx背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scy背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
screen_xウィンドウ左端の画面X座標です。内部で7を足してWXへ変換します。通常は0~159を使います。249以上は8ビット加算が折り返すため、画面外へ隠す目的で指定しないでください。
screen_yウィンドウのWYです。画面上端からのピクセル数で、横方向と違い7の加算はありません。WYが144以上なら可視領域に開始行がありません。
flags通常はWIN_SHOWまたはWIN_HIDEを指定します。ラッパーがWINを追加するので、座標更新だけなら0でも使えます。その他の対応ビットも通過するため、意図しないBGや色操作は指定しないでください。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
Raster_PushBgWindowScreen(0,8,0,24,48,KQ_RASTER_WIN_SHOW);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景X=8により赤い背景矩形が(24,16)へ移ります。ウィンドウ左上は(24,48)で、青い矩形は(32,56)のままです。背景とウィンドウの座標系が独立していることが分かります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bg_window.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:60
Queue both background and window updates, converting window screen X to WX.
kitaqgb/lib/raster.c:145
u8 Raster_PushBgWindowScreen(u8 ly, u8 scx, u8 scy, u8 screen_x, u8 screen_y, u8 flags)
{
flags = (u8)(flags | KQ_RASTER_BG | KQ_RASTER_WIN);
return Raster_PushEx(ly, scx, scy, (u8)(screen_x + 7), screen_y, flags);
}Raster_PushEx
背景・ウィンドウ・表示状態・色のうち、flagsで指定した操作を行う拡張バンドを追加します。通常の背景だけならRaster_Pushを使うと引数が少なくなります。
書式
u8 Raster_PushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
scx背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
scy背景のスクロール値0~255です。SCX/SCYは画面の位置ではなく背景マップを読み始める座標です。背景は256ピクセル単位で折り返します。
wxハードウェアのWX値です。通常の画面Xに7を加えた値を渡します。画面左端はWX=7です。値の範囲検査や補正は行いません。
wyウィンドウのWYです。画面上端からのピクセル数で、横方向と違い7の加算はありません。WYが144以上なら可視領域に開始行がありません。
flagsBG=1で背景、WIN=2でWX/WY、WIN_SHOW=4で表示、WIN_HIDE=8で非表示、BG_COLOR0=16でCGB背景色0を更新します。未知の上位ビットは捨てます。SHOWとHIDEを同時指定するとSHOWが最後に適用されます。色操作はSCX/SCY欄を色に使うため、通常のBGスクロールと混ぜないでください。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
Raster_PushEx(104,16,0,7,0,KQ_RASTER_BG|KQ_RASTER_WIN_HIDE);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
赤い矩形(DMGでは黒)が上から(32,16)、(24,80)、(16,112)へ並びます。幅24・高さ16で、下の段ほど左へずれます。表の個数3、エラー0、Commitの戻り値0を内部で記録しています。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:52
Append a validated extended band after masking flags to supported operations.
kitaqgb/lib/raster.c:112
u8 Raster_PushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags)
{
u8 r;
r = Raster_CheckPush(ly);
if (r != KQ_RASTER_OK) return r;
// Masking removes unknown bits but does not reject conflicting supported operations.
flags = (u8)(flags & KQ_RASTER_FLAG_MASK);
Scroll_SplitPushEx(ly, scx, scy, wx, wy, flags);
Raster_LastLyValue = ly;
Raster_HasLastLy = 1;
Raster_CountValue++;
Raster_LastErrorValue = KQ_RASTER_OK;
return KQ_RASTER_OK;
}Raster_PushWindowRaw
ハードウェアWX/WYをそのまま使うウィンドウバンドを追加します。WINビットを自動で追加し、背景用の2バイトには0を渡します。
書式
u8 Raster_PushWindowRaw(u8 ly, u8 wx, u8 wy, u8 flags);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
wxハードウェアのWX値です。通常の画面Xに7を加えた値を渡します。画面左端はWX=7です。値の範囲検査や補正は行いません。
wyウィンドウのWYです。画面上端からのピクセル数で、横方向と違い7の加算はありません。WYが144以上なら可視領域に開始行がありません。
flags通常はWIN_SHOWまたはWIN_HIDEを指定します。ラッパーがWINを追加するので、座標更新だけなら0でも使えます。その他の対応ビットも通過するため、意図しないBGや色操作は指定しないでください。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
Raster_PushWindowRaw(0,31,48,KQ_RASTER_WIN_SHOW);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
WX=31、WY=48により、ウィンドウ左上は画面(24,48)です。青い16×16矩形はその中の(8,8)、つまり画面(32,56)に見えます。上の赤い背景矩形は(32,16)です。DMGではどちらも黒になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_window_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:55
Enable window updates and pass hardware WX/WY values without coordinate conversion.
kitaqgb/lib/raster.c:131
u8 Raster_PushWindowRaw(u8 ly, u8 wx, u8 wy, u8 flags)
{
flags = (u8)(flags | KQ_RASTER_WIN);
return Raster_PushEx(ly, 0, 0, wx, wy, flags);
}Raster_PushWindowScreen
画面座標でウィンドウバンドを追加します。Xに7を加えてからRaw版へ渡すため、画面XとWXを取り違えずに配置できます。
書式
u8 Raster_PushWindowScreen(u8 ly, u8 screen_x, u8 screen_y, u8 flags);引数
ly予約する可視行0~143です。同じ表では前のLYより大きくしてください。先頭のLY=0はフレーム開始用としてVBlank処理で適用されます。
screen_xウィンドウ左端の画面X座標です。内部で7を足してWXへ変換します。通常は0~159を使います。249以上は8ビット加算が折り返すため、画面外へ隠す目的で指定しないでください。
screen_yウィンドウのWYです。画面上端からのピクセル数で、横方向と違い7の加算はありません。WYが144以上なら可視領域に開始行がありません。
flags通常はWIN_SHOWまたはWIN_HIDEを指定します。ラッパーがWINを追加するので、座標更新だけなら0でも使えます。その他の対応ビットも通過するため、意図しないBGや色操作は指定しないでください。
戻り値
成功ならKQ_RASTER_OK(0)、失敗なら状態コード1~4を返します。詳細は各関数の制約とRaster_GetLastErrorを参照してください。
使用条件と制限
raster.hを読み込み、raster.cとscroll.cをコンパイルします。最大8バンドの共有表を使います。Raster_*で構築している間は、直接Scroll_Split*や__scroll_split_*を呼ばないでください。別経路の操作はRaster側の個数・行番号と一致しなくなります。
表を作り直すときはVBlankなど描画への影響を管理できる区間でClear→登録→Commitを行います。Clearは実行中の分割も停止し、独立した二重バッファへの編集ではありません。バンドのLYは割り込みの予約行で、実際のレジスター書き込みには処理時間がかかります。毎走査線の波形描画には別のRaster_LineX*系を使い、同時には動かしません。
状態コードは0=OK、1=FULL、2=BAD_LY、3=UNSORTED、4=BAD_RANGEです。Push系はLY範囲、容量、行順序の順に検査します。失敗した登録は表を増やしません。次の成功した登録やClearでエラーは0になるため、各戻り値をその場で確認してください。
使用例
Raster_PushWindowScreen(0,24,48,KQ_RASTER_WIN_SHOW);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
画面座標(24,48)を直接指定しても、青いウィンドウ矩形はRaw版と同じ画面(32,56)に表示されます。Xへの7の加算はライブラリが行います。上の赤い背景矩形は(32,16)です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_window_screen.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/raster.h:58
Convert screen X to hardware WX by adding seven, then queue a window band. The byte addition wraps if callers supply an excessive X coordinate.
kitaqgb/lib/raster.c:139
u8 Raster_PushWindowScreen(u8 ly, u8 screen_x, u8 screen_y, u8 flags)
{
return Raster_PushWindowRaw(ly, (u8)(screen_x + 7), screen_y, flags);
}rpg.h — 乱数、文字、マップ、セーブ、RPG/ADV/SLG
絶対位置を指定する文字表示関数は背景マップへ直接描きます。text_windowは枠付き領域を開き、text_clear_rectは指定範囲を埋め、text_print_xyは制御コードを解釈せずタイル列を書きます。text_print_u8、text_print_u16、text_print_s16は必要なマス数で10進数を表示します。桁数の異なる値に置き換える場合は、表示欄全体を確保して消去します。
rpg.hで宣言された乱数ライブラリ関数の実装はlib/rng.cにあります。コンパイラのGB生成器を共有し、rng_seedが初期化、rng8/rng_next8が1バイト、rng16/rng_next16が2バイトを消費します。rand_range/rng_range・rng_chance・weighted_choiceは、取得した値をゲーム内の選択に使いやすく変換します。ライブラリ関数を呼ぶ場合はrng.cをプログラムと一緒にコンパイルしてください。テストの再現にはシードと呼び出し順を固定します。剰余を使う選択は厳密な確率を保証しません。
RLE展開は個数・値のバイト対と、個数0の終端を使います。rle_decodeは通常のポインターからRAMへ、rle_decode_farはROMバンク付きのポインターからRAMへ展開します。__rle_decode_vramはバンク付きの入力を、LCDの状態を考慮してVRAMへ直接展開します。RAM用の実装はlib/rle.c、直接VRAM用はコンパイラが生成します。いずれも出力バイト数を返し、有効な列と十分な展開先領域を呼び出し側で用意する必要があります。
rpg.hで宣言されたゲーム状態の保存関数は、lib/flags.cに実装されています。三つのflag関数は独立した2048個の真偽ビットを、quest_stateとquest_set_stateはゲーム側で段階を決める64個のバイトを操作します。利用時はflags.cをコンパイルし、番号をこの範囲に収めてください。電源を切った後も進行状況を残すには、別途セーブ処理が必要です。
ウィンドウ内の会話表示は、操作対象の長方形・カーソル・文字速度を1組共有します。text_openで領域を設定し、text_closeで消去し、text_set_speedで文字ごとのVBlank待ちを指定します。text_printとtext_print_farは制御列を解釈し、text_choiceは選択が終わるまで待つ縦並びの一覧です。タイルと領域を用意し、処理が戻るまで待つ呼び出しをゲーム内でどう扱うかを設計します。
RPGのマップ表示・メタタイル・スクリプト・SRAM保存・経路探索を提供します。通常マップは8×8、メタタイルは16×16ピクセルのセルです。マップ読み込みと描画、移動マスク、攻撃マスクは別の処理で、画像の読み込みやイベント実行はゲーム側が組み合わせます。
探索は共有の約4KiB作業領域を使い、再入できません。マップは1024セル以下、探索する距離は254以下にします。0xFFは未到達を表すため、255歩の経路は扱えません。出力配列は現在のマップの幅×高さバイトを用意します。
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
選択メニューは、決定まで待つ関数と、ゲームループから入力・描画を個別に呼ぶ関数を用意しています。所持品メニューは名前と数量を表示します。
camera_center
通常マップの対象セルを20×18タイルの画面中央へ寄せ、マップ端でスクロールを止めます。
書式
void camera_center(u8 x, u8 y);引数
xセルのX座標。
yセルのY座標。
戻り値
戻り値はありません。
使用条件と制限
原点はmax(x−10,0), max(y−9,0)をマップ端で制限し、8倍した下位8ビットをSCX/SCYへ書きます。メタタイルモードでは反映しません。新しい行や列をVRAMへ転送する機能はありません。
使用例
camera_center(5,4);result[9]=*((u8*)0xFF43);result[10]=*((u8*)0xFF42);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:297
Center an ordinary-map tile coordinate within a 20-by-18 viewport and clamp the scroll origin to its edges. This adjusts scroll registers only; it does not stream newly exposed map rows into VRAM.
kitaqgb/lib/map.c:105
void camera_center(u8 x, u8 y)
{
u8 tile_x = 0;
u8 tile_y = 0;
u8 max_x = 0;
u8 max_y = 0;
map_camera_x = x;
map_camera_y = y;
if (map_loaded == 0) return;
if (map_current.width > 20) max_x = (u8)(map_current.width - 20);
if (map_current.height > 18) max_y = (u8)(map_current.height - 18);
if (x > 10) tile_x = (u8)(x - 10);
if (y > 9) tile_y = (u8)(y - 9);
if (tile_x > max_x) tile_x = max_x;
if (tile_y > max_y) tile_y = max_y;
__scroll_bg_set((u8)(tile_x << 3), (u8)(tile_y << 3));
}far_read16
バンク付きポインタから下位バイト先行の16ビット値を読みます。
書式
#define far_read16(p) __farpeek16((p).bank, (p).ptr)引数
pbankとptrを持つfar_ptr_t。ptrはCPUから見えるアドレスです。
戻り値
u16の値。
使用条件と制限
マクロはpのbankとptrを別々に参照します。副作用のある式をpへ渡さないでください。ROM読み取り中のバンク切り替えは組み込み関数が行って復元します。割り込みから同じバンク操作を重ねないでください。
使用例
result[1]=far_read16(far);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
別ROMバンクの34 12 AB(16進)を読みます。1バイトは52、下位バイト先行のワードは4660、コピーした末尾は171と表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:172
far_read8
バンク付きポインタから1バイトを読みます。
書式
#define far_read8(p) __farpeek8((p).bank, (p).ptr)引数
pbankとptrを持つfar_ptr_t。ptrはCPUから見えるアドレスです。
戻り値
u8の値。
使用条件と制限
マクロはpのbankとptrを別々に参照します。副作用のある式をpへ渡さないでください。ROM読み取り中のバンク切り替えは組み込み関数が行って復元します。割り込みから同じバンク操作を重ねないでください。
使用例
far.bank=1;far.ptr=bank_bytes;result[0]=far_read8(far);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
別ROMバンクの34 12 AB(16進)を読みます。1バイトは52、下位バイト先行のワードは4660、コピーした末尾は171と表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:171
far_read_block
バンク付きポインタから連続したバイト列をRAMへコピーします。
書式
#define far_read_block(dst, p, len) __farmemcpy((dst), (p).bank, (p).ptr, (len))引数
dstlenバイトを書き込めるRAM先頭。
pbankとptrを持つfar_ptr_t。ptrはCPUから見えるアドレスです。
lenコピーするバイト数。元のバンク内に収めます。
戻り値
戻り値はありません。
使用条件と制限
__farmemcpyへのマクロです。ROMバンク境界を自動でまたぐ機能はなく、重なる領域の移動にも使いません。
使用例
far_read_block(copy,far,3);result[2]=copy[2];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
別ROMバンクの34 12 AB(16進)を読みます。1バイトは52、下位バイト先行のワードは4660、コピーした末尾は171と表示します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:173
flag_clear
隣接するフラグを消さずに、指定したゲーム内フラグを0にします。条件の成立を取り消すときや、フラグを明示的に初期化するときに使います。すでに0のフラグを消しても0のままです。
書式
void flag_clear(u16 id);引数
id0〜2047のフラグ番号をu16で渡します。バイト位置はid / 8、そのバイト内のビット位置はid % 8で、最下位ビットから数えます。引数の型がu16でも、保存領域は2048フラグで固定です。
戻り値
ありません(void)。
使用条件と制限
関数内では番号の範囲を検査しません。フラグ番号は2048未満、クエスト番号は64未満にしてください。範囲外では無関係なRAMを読んだり書き換えたりするおそれがあります。サンプルでは起動時の内容に依存せず、明示的に初期化しています。
flags.cはRAM内にフラグ用256バイトと、別のクエスト用64バイトを持ちます。これらの関数はカートリッジのセーブRAMにアクセスせず、電源を切った後の保存も行いません。進行状況を残すには、ゲームのセーブ処理でフラグとクエストの値を保存し、読み込み時に復元してください。
rpg.hをインクルードし、lib/flags.cをゲームと一緒にコンパイルします。配列はそのソースファイル内で管理されるため、フラグ・クエストのAPIを通じて読み書きします。保存領域は全呼び出し元で共有されるので、メインループと割り込みから書き込む場合の調整は呼び出し側で行ってください。
使用例
flag_clear(9);flag_clear(9); // Mark it closed; clearing twice still leaves 0.
cleared=flag_get(9);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
設定済みの扉フラグ9を解除するサンプルです。解除を2回呼んでもDOOR CLOSEDは000です。先に読み取っておいたDOOR OPENの001も表示し、二つの時点で取得した値を比較できます。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは000になる必要があります。実行画像が示すのはRAM内の状態操作で、ゲームのセーブを実演するものではありません。
DMGのエミュレータでは、0〜2047の全フラグ番号、設定・解除の繰り返し、隣接項目、および64クエストそれぞれに対する全256状態値を確認しました。最後にフラグ256バイトのパターンとクエスト64バイトを期待データと照合しています。DMG・CGBのサンプル画面は240フレーム後に確認しました。実機とセーブの永続化は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:372
Clear a flag bit; id must be below 2048.
kitaqgb/lib/flags.c:21
void flag_clear(u16 id)
{
__bit_clear(flags_bits, id);
}flag_get
ライブラリ内のRAMビット配列から、ゲーム内フラグを一つ読み取ります。扉が開いているか、アイテムを取得済みかなど、ゲーム側で意味を決めた出来事の有無を調べるときに使います。読み取っても値は変わりません。
書式
u8 flag_get(u16 id);引数
id0〜2047のフラグ番号をu16で渡します。バイト位置はid / 8、そのバイト内のビット位置はid % 8で、最下位ビットから数えます。引数の型がu16でも、保存領域は2048フラグで固定です。
戻り値
フラグが0なら0、設定されていれば1に正規化したu8の真偽値です。このGBライブラリは、結果を正規化するGBの__bit_test組み込み関数を呼びます。
使用条件と制限
関数内では番号の範囲を検査しません。フラグ番号は2048未満、クエスト番号は64未満にしてください。範囲外では無関係なRAMを読んだり書き換えたりするおそれがあります。サンプルでは起動時の内容に依存せず、明示的に初期化しています。
flags.cはRAM内にフラグ用256バイトと、別のクエスト用64バイトを持ちます。これらの関数はカートリッジのセーブRAMにアクセスせず、電源を切った後の保存も行いません。進行状況を残すには、ゲームのセーブ処理でフラグとクエストの値を保存し、読み込み時に復元してください。
rpg.hをインクルードし、lib/flags.cをゲームと一緒にコンパイルします。配列はそのソースファイル内で管理されるため、フラグ・クエストのAPIを通じて読み書きします。保存領域は全呼び出し元で共有されるので、メインループと割り込みから書き込む場合の調整は呼び出し側で行ってください。
使用例
before=flag_get(9); // Door-open flag: 0 after explicit initialization.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
フラグ9を扉の状態に割り当てるサンプルです。明示的に消した直後のBEFOREは000です。その後で設定・解除するとDOOR OPENは001、DOOR CLOSEDは000になり、保存された条件を読み取れることが分かります。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは000になる必要があります。実行画像が示すのはRAM内の状態操作で、ゲームのセーブを実演するものではありません。
DMGのエミュレータでは、0〜2047の全フラグ番号、設定・解除の繰り返し、隣接項目、および64クエストそれぞれに対する全256状態値を確認しました。最後にフラグ256バイトのパターンとクエスト64バイトを期待データと照合しています。DMG・CGBのサンプル画面は240フレーム後に確認しました。実機とセーブの永続化は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:368
Read a bit from the flag array. Callers must keep id below 2048.
kitaqgb/lib/flags.c:9
u8 flag_get(u16 id)
{
return __bit_test(flags_bits, id);
}flag_set
同じバイトにあるほかの7フラグを保ちながら、指定したゲーム内フラグを1にします。すでに1のフラグを設定しても1のままです。扉を開いたことなど、一度発生した出来事を記録するときに使います。
書式
void flag_set(u16 id);引数
id0〜2047のフラグ番号をu16で渡します。バイト位置はid / 8、そのバイト内のビット位置はid % 8で、最下位ビットから数えます。引数の型がu16でも、保存領域は2048フラグで固定です。
戻り値
ありません(void)。
使用条件と制限
関数内では番号の範囲を検査しません。フラグ番号は2048未満、クエスト番号は64未満にしてください。範囲外では無関係なRAMを読んだり書き換えたりするおそれがあります。サンプルでは起動時の内容に依存せず、明示的に初期化しています。
flags.cはRAM内にフラグ用256バイトと、別のクエスト用64バイトを持ちます。これらの関数はカートリッジのセーブRAMにアクセスせず、電源を切った後の保存も行いません。進行状況を残すには、ゲームのセーブ処理でフラグとクエストの値を保存し、読み込み時に復元してください。
rpg.hをインクルードし、lib/flags.cをゲームと一緒にコンパイルします。配列はそのソースファイル内で管理されるため、フラグ・クエストのAPIを通じて読み書きします。保存領域は全呼び出し元で共有されるので、メインループと割り込みから書き込む場合の調整は呼び出し側で行ってください。
使用例
flag_set(9); // Mark the door as open; setting an already set flag is harmless.
flag_set(9);
opened=flag_get(9);neighbor=flag_get(8); // 1 and 0: adjacent flag is unchanged.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
扉のフラグ9を2回設定するサンプルです。DOOR OPENは001となり、隣のフラグ8はNEIGHBOR 000のままです。二つは同じバイトにあるため、隣の値を見て、一つの設定でほかを変えていないことを確かめられます。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは000になる必要があります。実行画像が示すのはRAM内の状態操作で、ゲームのセーブを実演するものではありません。
DMGのエミュレータでは、0〜2047の全フラグ番号、設定・解除の繰り返し、隣接項目、および64クエストそれぞれに対する全256状態値を確認しました。最後にフラグ256バイトのパターンとクエスト64バイトを期待データと照合しています。DMG・CGBのサンプル画面は240フレーム後に確認しました。実機とセーブの永続化は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:370
Set a flag bit without disturbing adjacent flags; id must be below 2048.
kitaqgb/lib/flags.c:15
void flag_set(u16 id)
{
__bit_set(flags_bits, id);
}map_current_height
現在のマップの高さを取得します。
書式
u8 map_current_height();戻り値
セル単位の高さ。未設定なら0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[1]=map_current_height();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:391
Return the active map height in its own cell units, or zero when no map is loaded.
kitaqgb/lib/map.c:25
u8 map_current_height()
{
if (map_meta_loaded != 0) return map_meta_current.height;
if (map_loaded == 0) return 0;
return map_current.height;
}map_current_width
現在のマップの幅を取得します。
書式
u8 map_current_width();戻り値
セル単位の幅。未設定なら0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[0]=map_current_width();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:389
Return the active map width in its own cell units, or zero when no map is loaded.
kitaqgb/lib/map.c:17
u8 map_current_width()
{
if (map_meta_loaded != 0) return map_meta_current.width;
if (map_loaded == 0) return 0;
return map_current.width;
}map_draw_visible
指定したメタタイル原点から10×9セルを背景へ描き、再描画用のカメラ位置を記憶します。
書式
void map_draw_visible(u8 camera_tx, u8 camera_ty);引数
camera_tx左上セルのX。加算が8ビットで周回しないよう246以下にします。
camera_ty上端セルのY。247以下にします。
戻り値
戻り値はありません。
使用条件と制限
範囲外セルはメタタイルID 0で描きます。定義0を必ず用意してください。背景の20×18タイル以外とCGB属性は変更しません。
使用例
map_draw_visible(1,0);result[9]=*((u8*)0x9800);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:304
Draw a 10-by-9 metatile viewport as 20-by-18 individual background tiles. Out-of-range cells use definition zero; palette attributes are not written. Remember the camera and clear the dirty flag after the full redraw.
kitaqgb/lib/map.c:212
void map_draw_visible(u8 camera_tx, u8 camera_ty)
{
u8 my;
if (map_meta_loaded == 0) return;
map_camera_x = camera_tx;
map_camera_y = camera_ty;
my = 0;
while (my < 9) {
u8 mx = 0;
while (mx < 10) {
u8 tx = (u8)(camera_tx + mx);
u8 ty = (u8)(camera_ty + my);
u8 sx = (u8)(mx << 1);
u8 sy = (u8)(my << 1);
metatile_t meta;
u8 id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
__settile_xy(sx, sy, meta.tile_tl);
__settile_xy((u8)(sx + 1), sy, meta.tile_tr);
__settile_xy(sx, (u8)(sy + 1), meta.tile_bl);
__settile_xy((u8)(sx + 1), (u8)(sy + 1), meta.tile_br);
mx++;
}
my++;
}
map_dirty = 0;
}map_flush_dirty
再描画が予約されていれば、保存済みカメラ位置から表示範囲を描き直して予約を解除します。
書式
void map_flush_dirty();戻り値
戻り値はありません。
使用条件と制限
dirtyでなければ何もしません。描画対象は10×9メタタイル全体で、変更セルだけではありません。
使用例
map_flush_dirty();result[11]=*((u8*)0x9800);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:321
Redraw the remembered metatile viewport only when the shared dirty flag is set.
kitaqgb/lib/map.c:254
void map_flush_dirty()
{
if (map_dirty == 0) return;
map_draw_visible(map_camera_x, map_camera_y);
}map_get_attr
セルの定義にあるpaletteメタデータを読みます。
書式
u8 map_get_attr(u8 tx, u8 ty);引数
txメタタイルのX座標。
tyメタタイルのY座標。
戻り値
paletteの1バイト。範囲外は定義0の値、メタタイル未設定は0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[3]=map_get_attr(0,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:310
Return the selected definition's palette byte. An out-of-range coordinate uses the metatile-zero fallback; this function does not write CGB attributes.
kitaqgb/lib/map.c:172
u8 map_get_attr(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return 0;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return meta.palette;
}map_get_event
メタタイルのイベントIDを読みます。通常マップではイベントの有無を調べます。
書式
u8 map_get_event(u8 tx, u8 ty);引数
txメタタイルのX座標。
tyメタタイルのY座標。
戻り値
メタタイルはeventの値、通常マップは0/1。未設定・範囲外は0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[7]=map_get_event(0,0);result[8]=map_get_event(3,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:316
Return a metatile event ID, or the ordinary-map Boolean trigger result. Missing/out-of-range metatile cells have no event.
kitaqgb/lib/map.c:197
u8 map_get_event(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return map_trigger_at(tx, ty);
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 0;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return meta.event;
}map_get_metatile
セルのメタタイルIDを読みます。
書式
u8 map_get_metatile(u8 tx, u8 ty);引数
txメタタイルのX座標。
tyメタタイルのY座標。
戻り値
IDを返します。範囲外・未設定は0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[1]=map_get_metatile(0,0);result[2]=map_get_metatile(3,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:307
Read a row-major metatile ID, returning ID zero for inactive or out-of-range lookups. Callers must provide a usable definition for metatile zero.
kitaqgb/lib/map.c:159
u8 map_get_metatile(u8 tx, u8 ty)
{
u16 index;
if (map_meta_loaded == 0) return 0;
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 0;
index = __map_index(tx, ty, map_meta_current.width);
return __farpeek8(map_meta_data_bank(), map_meta_current.metatile_ids + index);
}map_is_blocked
移動先セルの衝突情報を調べます。メタタイルモードでは定義のcollisionを使います。
書式
u8 map_is_blocked(u8 x, u8 y);引数
xセルのX座標。
yセルのY座標。
戻り値
障害物・範囲外・マップ未設定は1、通行可能なら0。通常マップの衝突配列がNULLなら範囲内は0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[4]=map_is_blocked(2,2);result[5]=map_is_blocked(6,2);result[6]=map_is_blocked(1,2);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:290
Use metatile collision when that mode is active; otherwise read one packed collision bit. Missing maps/out-of-range cells block movement, but a valid ordinary map without a collision array is entirely passable.
kitaqgb/lib/map.c:68
u8 map_is_blocked(u8 x, u8 y)
{
u16 index;
u8 value;
if (map_meta_loaded != 0) return map_is_solid(x, y);
if (map_loaded == 0) return 1;
if (x >= map_current.width || y >= map_current.height) return 1;
if (map_current.collision_bits == 0) return 0;
index = __map_index(x, y, map_current.width);
// Collision uses least-significant bit first within each row-major group of eight cells.
value = __farpeek8(map_bank, map_current.collision_bits + (index >> 3));
if ((value & (u8)(1 << (index & 7))) != 0) return 1;
return 0;
}map_is_solid
セルが壁かを調べ、通常マップでは衝突ビットへ処理を委ねます。
書式
u8 map_is_solid(u8 tx, u8 ty);引数
txメタタイルのX座標。
tyメタタイルのY座標。
戻り値
collisionが非0または範囲外なら1、通行可能なら0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[4]=map_is_solid(0,0);result[5]=map_is_solid(1,0);result[6]=map_is_solid(3,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:313
Treat out-of-range metatile cells as solid; otherwise inspect the definition collision flag. In ordinary-map mode, delegate to its packed collision bits.
kitaqgb/lib/map.c:184
u8 map_is_solid(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return map_is_blocked(tx, ty);
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 1;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return (u8)(meta.collision != 0);
}map_load
通常マップの記述子を読み込み、左上の最大20×18タイルを現在の背景マップへ描きます。
書式
void map_load(u8 bank, const map_t* map);引数
bank記述子・衝突ビット・イベント配列を置くROMバンク。
map9バイトのpacked map_t。tilesは幅×高さのタイル番号、collision_bitsは下位ビットからのビット列、eventsはセルごとの1バイトです。タイル配列はbank_tilesを使います。
戻り値
戻り値はありません。
使用条件と制限
有効なマップは全体で1つです。記述子はコピーしますが、配列は必要な間ずっと読み取り可能にします。画像のタイルデータは別途VRAMへ読み込んでください。
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
小さいマップの外側は消去しません。bank_tiles=0は通常マップではバンク0を表し、記述子のバンクの継承指定ではありません。
使用例
map_load(0,maps);result[2]=map_current_width();result[3]=map_current_height();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:286
Load the nine-byte ordinary-map descriptor and draw up to 20 by 18 tiles from its top-left corner. Drawing does not upload tile graphics or clear background cells beyond smaller map dimensions.
kitaqgb/lib/map.c:35
void map_load(u8 bank, const map_t* map)
{
u8 row;
u8 h;
u8 w;
// The literal copy size depends on the packed map_t layout; changing that layout requires updating this copy.
__farmemcpy(&map_current, bank, map, 9);
map_bank = bank;
map_loaded = 1;
map_meta_loaded = 0;
w = map_current.width;
h = map_current.height;
if (w > 20) w = 20;
if (h > 18) h = 18;
row = 0;
while (row < h) {
u8 x = 0;
u16 base_index = __map_index(0, row, map_current.width);
while (x < w) {
map_row[x] = __farpeek8(map_current.bank_tiles, map_current.tiles + base_index + x);
x++;
}
__settile_row(0, row, map_row, w);
row++;
}
}map_load_metatile
メタタイルマップを選択し、カメラ(0,0)から10×9セルを20×18タイルへ展開します。
書式
void map_load_metatile(u8 bank, const metatile_map_t* map);引数
bank記述子を置くROMバンク。
mapmetatile_map_tへのポインタ。幅・高さは16×16セル数。ID配列と7バイトのmetatile_t定義表を参照します。
戻り値
戻り値はありません。
使用条件と制限
有効なマップは全体で1つです。記述子はコピーしますが、配列は必要な間ずっと読み取り可能にします。画像のタイルデータは別途VRAMへ読み込んでください。
bank_tilesが0なら記述子のバンクを使い、0以外ならID配列・定義表の両方にそのバンクを使います。定義は左上・右上・左下・右下のタイル、collision、palette、eventの順です。paletteは描画時に自動適用しません。
使用例
map_load_metatile(0,&level);result[0]=*((u8*)0x9800);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:300
Select metatile mode, copy its descriptor and draw the initial viewport with the camera at cell (0,0).
kitaqgb/lib/map.c:145
void map_load_metatile(u8 bank, const metatile_map_t* map)
{
map_meta_bank = bank;
__farmemcpy(&map_meta_current, bank, map, sizeof(metatile_map_t));
map_meta_loaded = 1;
map_loaded = 0;
map_dirty = 0;
map_camera_x = 0;
map_camera_y = 0;
map_draw_visible(0, 0);
}map_mark_dirty
有効なメタタイル座標を指定して、画面全体の再描画を予約します。
書式
void map_mark_dirty(u8 tx, u8 ty);引数
txメタタイルのX座標。
tyメタタイルのY座標。
戻り値
戻り値はありません。
使用条件と制限
1セルだけの更新キューではなく、共有のdirtyフラグを立てます。範囲外は無視します。IDや定義そのものは変更せず、呼び出し側が先に書き換えます。
使用例
ids[1]=1;map_mark_dirty(1,0);result[10]=*((u8*)0x9800);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
メタタイルIDをRAMで変え、dirtyを付けてflushする例です。カメラ(1,0)で左上に16×16の緑の正方形が現れます。パレットのメタデータはサンプル側がCGB属性へ反映しています。DMGでは黒い正方形です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:319
Mark the viewport for a full redraw when a valid metatile cell changes. The coordinate is validated but no per-cell dirty list is retained.
kitaqgb/lib/map.c:246
void map_mark_dirty(u8 tx, u8 ty)
{
if (map_meta_loaded == 0) return;
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return;
map_dirty = 1;
}map_trigger_at
セルにイベントが設定されているかを調べます。イベントの実行は行いません。
書式
u8 map_trigger_at(u8 x, u8 y);引数
xセルのX座標。
yセルのY座標。
戻り値
設定ありで1、それ以外は0。通常マップのイベント値やメタタイルのイベントIDそのものは返しません。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
使用例
result[7]=map_trigger_at(5,4);result[8]=map_trigger_at(6,4);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:293
Report whether an event exists at the cell. Ordinary maps expose a Boolean byte-presence test; metatile maps reduce their event ID to a Boolean here.
kitaqgb/lib/map.c:87
u8 map_trigger_at(u8 x, u8 y)
{
u16 index;
u8 value;
if (map_meta_loaded != 0) return (u8)(map_get_event(x, y) != 0);
if (map_loaded == 0) return 0;
if (x >= map_current.width || y >= map_current.height) return 0;
if (map_current.events == 0) return 0;
index = __map_index(x, y, map_current.width);
value = __farpeek8(map_bank, ((const u8*)map_current.events) + index);
return (u8)((value != 0) ? 1 : 0);
}path_find_bfs
上下左右の最短経路を求め、開始点からの移動方向列へ変換します。
書式
u8 path_find_bfs(u8 sx, u8 sy, u8 gx, u8 gy, u8* out_path, u8 max_len);引数
sx開始セルX。範囲内を指定します。
sy開始セルY。
gx目標セルX。開始セルと異なる壁は目標にできません。
gy目標セルY。
out_pathmax_lenバイト以上の配列。0=上、1=右、2=下、3=左。
max_len許容する出力長。短すぎる場合は切り詰めずに失敗します。
戻り値
経路長を返します。未到達・不正座標・max_len超過・開始点と目標が同じ場合は0です。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
探索は共有の約4KiB作業領域を使い、再入できません。マップは1024セル以下、探索する距離は254以下にします。0xFFは未到達を表すため、255歩の経路は扱えません。出力配列は現在のマップの幅×高さバイトを用意します。
max_lenは探索距離の上限ではありません。失敗時に出力を新しい経路として使わないでください。
使用例
result[49]=path_find_bfs(0,2,5,2,route,30);x=0;y=2;for(i=0;i<result[49];i++){if(route[i]==0)y--;if(route[i]==1)x++;if(route[i]==2)y++;if(route[i]==3)x--;}result[50]=x;result[51]=y;result[52]=path_find_bfs(0,2,5,2,route,2);result[53]=path_find_bfs(0,2,2,2,route,30);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:361
Find a shortest cardinal path with BFS and write direction codes 0=up, 1=right, 2=down, 3=left. Return its byte length, or zero for failure or an already-reached goal. The caller supplies max_len output bytes and respects the shared workspace/distance limits; excessive paths are not truncated.
kitaqgb/lib/slg_path.c:78
u8 path_find_bfs(u8 sx, u8 sy, u8 gx, u8 gy, u8* out_path, u8 max_len)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 head = 0;
u16 tail = 0;
u16 start;
u16 goal;
u8 found = 0;
if (w == 0 || h == 0) return 0;
if (sx >= w || sy >= h || gx >= w || gy >= h) return 0;
if ((sx != gx || sy != gy) && map_is_blocked(gx, gy) != 0) return 0;
total = (u16)((u16)w * (u16)h);
__memset(path_dist, 0xFF, total);
start = __map_index(sx, sy, w);
goal = __map_index(gx, gy, w);
path_dist[start] = 0;
path_queue[tail++] = start;
while (head < tail) {
u16 cur = path_queue[head++];
u8 cost = path_dist[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (cur == goal) {
found = 1;
break;
}
if (y > 0) {
u16 next = (u16)(cur - w);
if (path_dist[next] == 0xFF && map_is_blocked(x, (u8)(y - 1)) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(x + 1) < w) {
u16 next = (u16)(cur + 1);
if (path_dist[next] == 0xFF && map_is_blocked((u8)(x + 1), y) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(y + 1) < h) {
u16 next = (u16)(cur + w);
if (path_dist[next] == 0xFF && map_is_blocked(x, (u8)(y + 1)) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if (x > 0) {
u16 next = (u16)(cur - 1);
if (path_dist[next] == 0xFF && map_is_blocked((u8)(x - 1), y) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
}
// Reject unreachable or oversized paths before reconstructing output.
if (found == 0) return 0;
if (path_dist[goal] > max_len) return 0;
{
// Walk backward through decreasing costs, recording forward directions in
// reverse order. The fixed neighbor order makes tied routes deterministic.
u8 len = 0;
u16 cur = goal;
while (cur != start) {
u8 cur_cost = path_dist[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (y > 0 && path_dist[(u16)(cur - w)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 2;
cur = (u16)(cur - w);
continue;
}
if ((u8)(x + 1) < w && path_dist[(u16)(cur + 1)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 3;
cur = (u16)(cur + 1);
continue;
}
if ((u8)(y + 1) < h && path_dist[(u16)(cur + w)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 0;
cur = (u16)(cur + w);
continue;
}
if (x > 0 && path_dist[(u16)(cur - 1)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 1;
cur = (u16)(cur - 1);
continue;
}
return 0;
}
// Reverse the recovered directions so the first output step starts at the origin.
while (len != 0) {
len--;
out_path[path_dist[goal] - 1 - len] = path_rev[len];
}
return path_dist[goal];
}
}quest_set_state
指定したクエストの状態を、1バイト単位で置き換えます。値の意味はゲーム側で決めます。たとえば1を受注済み、3を完了として使えます。遷移が許されるかどうかの検査や、報酬・コールバックの実行は行いません。
書式
void quest_set_state(u8 quest_id, u8 state);引数
quest_id0〜63のクエスト番号をu8で渡します。64個の状態バイトの一つを直接指定する添字であり、ポインターや完了したクエストの数ではありません。番号とクエストの対応はゲーム側で決めます。
state新しい状態を0〜255のu8で指定します。8ビットすべてを置き換えます。ライブラリは各値に特定の意味を割り当てておらず、0や255も予約していません。
戻り値
ありません(void)。
使用条件と制限
関数内では番号の範囲を検査しません。フラグ番号は2048未満、クエスト番号は64未満にしてください。範囲外では無関係なRAMを読んだり書き換えたりするおそれがあります。サンプルでは起動時の内容に依存せず、明示的に初期化しています。
flags.cはRAM内にフラグ用256バイトと、別のクエスト用64バイトを持ちます。これらの関数はカートリッジのセーブRAMにアクセスせず、電源を切った後の保存も行いません。進行状況を残すには、ゲームのセーブ処理でフラグとクエストの値を保存し、読み込み時に復元してください。
rpg.hをインクルードし、lib/flags.cをゲームと一緒にコンパイルします。配列はそのソースファイル内で管理されるため、フラグ・クエストのAPIを通じて読み書きします。保存領域は全呼び出し元で共有されるので、メインループと割り込みから書き込む場合の調整は呼び出し側で行ってください。
使用例
quest_set_state(2,1); // In this game, 1 means accepted.
started=quest_state(2);
quest_set_state(2,3); // In this game, 3 means completed; replace the whole byte.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
クエスト2に1を保存した後、3で上書きするサンプルです。ACCEPTEDは最初に読み取った001、COMPLETEDは置き換え後の003です。最後の有効な番号63に255を設定するため、LAST QUESTは255になります。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは000になる必要があります。実行画像が示すのはRAM内の状態操作で、ゲームのセーブを実演するものではありません。
DMGのエミュレータでは、0〜2047の全フラグ番号、設定・解除の繰り返し、隣接項目、および64クエストそれぞれに対する全256状態値を確認しました。最後にフラグ256バイトのパターンとクエスト64バイトを期待データと照合しています。DMG・CGBのサンプル画面は240フレーム後に確認しました。実機とセーブの永続化は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:377
Replace a quest-state byte without validating its game-specific meaning. The caller must keep quest_id below 64.
kitaqgb/lib/flags.c:34
void quest_set_state(u8 quest_id, u8 state)
{
quest_states[quest_id] = state;
}quest_state
指定したクエストの保存済みの1バイトを読み取ります。1ビットのフラグと異なり、未開始・受注済み・完了など複数の段階を表せます。この関数は値を読むだけで、クエストの処理を実行したり段階を進めたりしません。
書式
u8 quest_state(u8 quest_id);引数
quest_id0〜63のクエスト番号をu8で渡します。64個の状態バイトの一つを直接指定する添字であり、ポインターや完了したクエストの数ではありません。番号とクエストの対応はゲーム側で決めます。
戻り値
保存されているu8の値(0〜255)です。0か1に正規化した真偽値ではなく、状態バイトそのものです。ゲーム側で定義した段階の値と比較してください。
使用条件と制限
関数内では番号の範囲を検査しません。フラグ番号は2048未満、クエスト番号は64未満にしてください。範囲外では無関係なRAMを読んだり書き換えたりするおそれがあります。サンプルでは起動時の内容に依存せず、明示的に初期化しています。
flags.cはRAM内にフラグ用256バイトと、別のクエスト用64バイトを持ちます。これらの関数はカートリッジのセーブRAMにアクセスせず、電源を切った後の保存も行いません。進行状況を残すには、ゲームのセーブ処理でフラグとクエストの値を保存し、読み込み時に復元してください。
rpg.hをインクルードし、lib/flags.cをゲームと一緒にコンパイルします。配列はそのソースファイル内で管理されるため、フラグ・クエストのAPIを通じて読み書きします。保存領域は全呼び出し元で共有されるので、メインループと割り込みから書き込む場合の調整は呼び出し側で行ってください。
使用例
finished=quest_state(2); // Read back 3 without advancing any quest logic.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ゲーム側でクエスト2の状態を1から3へ変更し、その値を読み取るサンプルです。ACCEPTEDは001、COMPLETEDは003です。表示名と段階の意味はサンプルが決めたもので、ライブラリが強制する規則ではありません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは000になる必要があります。実行画像が示すのはRAM内の状態操作で、ゲームのセーブを実演するものではありません。
DMGのエミュレータでは、0〜2047の全フラグ番号、設定・解除の繰り返し、隣接項目、および64クエストそれぞれに対する全256状態値を確認しました。最後にフラグ256バイトのパターンとクエスト64バイトを期待データと照合しています。DMG・CGBのサンプル画面は240フレーム後に確認しました。実機とセーブの永続化は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:374
Read a quest-state byte; quest_id must be below 64.
kitaqgb/lib/flags.c:27
u8 quest_state(u8 quest_id)
{
return quest_states[quest_id];
}rand_range
rng8() % maxを計算し、0以上max未満の値を選びます。たとえば上限10なら、0〜9の添字として使えます。上限が0の場合は乱数を取り出さず、その場で0を返します。
書式
u8 rand_range(u8 max);引数
max結果に含めない上限値です。型はu8で、指定範囲は0〜255です。max > 0なら戻り値は必ずmaxより小さくなります。256は指定できません。剰余を取らずに1バイトの結果をそのまま使う場合はrng8を使ってください。
戻り値
max > 0なら1回取得し、0〜max−1のu8を返します。max == 0では取得せずに0を返します。max == 1では結果は常に0ですが、乱数は1回消費します。
使用条件と制限
入力の範囲が上限で割り切れないと、剰余による縮小で出やすい結果が生じます。元の生成器が一様とは限りません。範囲付きの結果や確率のしきい値は、厳密な確率ではなくゲーム処理上の目安として使ってください。
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
bounded=rand_range(10); // An index from 0 through 9; this seed yields 2.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
10未満の添字を選ぶサンプルです。シード0x1234の最初のバイトは2なので、RANGE 10は00002です。ZERO BOUNDは00000、その後のNEXT BYTEは00002のままで、上限0の呼び出しが乱数を消費しないことも確かめられます。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:199
Reduce a random byte modulo max; zero returns zero without consuming randomness. Modulo reduction is biased unless max divides the generator range.
kitaqgb/lib/rng.c:37
u8 rand_range(u8 max)
{
if (max == 0) return 0;
return (u8)(rng8() % max);
}range_fill_move
上下左右へ1歩ずつ広げる幅優先探索で、開始セルからの移動コストを作ります。
書式
void range_fill_move(u8 sx, u8 sy, u8 move, u8* out_costmap);引数
sx開始セルX。範囲内を指定します。
sy開始セルY。
move移動上限0~254。0なら開始セルだけです。
out_costmap幅×高さバイトの出力。未到達は255、開始セルは0です。
戻り値
戻り値はありません。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
探索は共有の約4KiB作業領域を使い、再入できません。マップは1024セル以下、探索する距離は254以下にします。0xFFは未到達を表すため、255歩の経路は扱えません。出力配列は現在のマップの幅×高さバイトを用意します。
マップ未設定や範囲外の開始セルでは出力を変更しません。開始セル以外の壁には入りません。
使用例
range_fill_move(0,2,3,costs);for(i=0;i<30;i++)result[13+i]=costs[i];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:365
Flood cardinal neighbors at unit cost, stopping expansion at the movement limit and rejecting blocked destinations. Initialize output to 0xFF only after validating the map and origin; invalid input leaves output unchanged.
kitaqgb/lib/slg_path.c:14
void range_fill_move(u8 sx, u8 sy, u8 move, u8* out_costmap)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 head = 0;
u16 tail = 0;
u16 start;
if (w == 0 || h == 0) return;
if (sx >= w || sy >= h) return;
total = (u16)((u16)w * (u16)h);
__memset(out_costmap, 0xFF, total);
start = __map_index(sx, sy, w);
out_costmap[start] = 0;
path_queue[tail++] = start;
while (head < tail) {
u16 cur = path_queue[head++];
u8 cost = out_costmap[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (cost >= move) continue;
if (y > 0) {
u16 next = (u16)(cur - w);
if (out_costmap[next] == 0xFF && map_is_blocked(x, (u8)(y - 1)) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(x + 1) < w) {
u16 next = (u16)(cur + 1);
if (out_costmap[next] == 0xFF && map_is_blocked((u8)(x + 1), y) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(y + 1) < h) {
u16 next = (u16)(cur + w);
if (out_costmap[next] == 0xFF && map_is_blocked(x, (u8)(y + 1)) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if (x > 0) {
u16 next = (u16)(cur - 1);
if (out_costmap[next] == 0xFF && map_is_blocked((u8)(x - 1), y) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
}
}rle_decode
ランレングス形式のバイト列を、通常の書き込み可能なRAMへ展開します。個数と値の組ごとに同じバイトを指定個数だけ繰り返し、個数0で終了します。圧縮データを通常のポインターで読める場合や、展開後の内容を確認・加工してから転送したい場合に使います。
書式
u16 rle_decode(void* dst, const void* src);引数
dst展開後のデータ全体が入る、書き込み可能なRAMへのポインターです。関数には容量を渡せません。まだ読んでいない組を出力が上書きしないよう、入力と展開先を重ねないでください。
src個数・値のバイト対が並び、個数0で終わる読み取り可能な列へのポインターです。通常の組の個数は1〜255で、値には0を含む任意のバイトを使えます。終端の0には値のバイトを続ける必要がありません。呼び出し中は、そのポインターを現在のメモリ配置で読み取れる状態に保ってください。
戻り値
書き込んだバイト数をu16で返します。先頭の個数が0なら何も展開せず0を返し、終端そのものはコピーしません。展開長はu16で表せる範囲に収めてください。戻り値に別のエラーコードはなく、タイルの枚数でもありません。
使用条件と制限
文字列専用ではなく、バイト単位の形式です。たとえば{3, 0x12, 2, 0xA5, 0}は、16進表記で12 12 12 A5 A5の5バイトに展開されます。個数が0でない組の値が0でも、通常のデータとして扱い、終端とは見なしません。
圧縮後のサイズ、展開先の容量、不正データの扱いを指定する引数はありません。素材の作成時や読み込み時に形式を確認し、終端0を付けて、展開長を計算してから呼び出してください。不正な列や終端のない列では、入力・出力とも領域外へ進む可能性があります。
RAM用の展開関数は通常の書き込みを行い、LCDのアクセス可能時間には同期しません。展開先はRAMにしてください。表示する場合は、適切なVRAMコピーAPIで別途転送します。サンプルではLCDを止めてから転送しています。
バンク付きの読み取り中に、割り込み処理でROMの配置を変更したり、同じ遠距離コピーの補助処理を重ねて呼んだりしないでください。補助処理はバンク管理用の状態を共有します。通常ポインター版では、展開関数と入力データを同時に読める配置にします。関数のバンクへの切り替えで、入力ポインターが指すデータが隠れないようにしてください。
rle_decodeとrle_decode_farはrpg.hで宣言され、lib/rle.cに実装されています。ゲームと一緒に実装を一度だけコンパイルしてください。完全なサンプルでは呼び出し側と展開関数を共通バンクに置くため、#pragma bank 0の後でrle.cをインクルードしています。このサンプルのビルドでは、rle.cをさらに別の入力ファイルとして渡さないでください。__rle_decode_vram自体にはライブラリの実装ファイルは不要です。
使用例
near_count=rle_decode(near_buffer+1,local_rle); // 48 bytes in writable RAM.
__vram_copy(0x8800,near_buffer+1,near_count); // Upload those bytes while the LCD is off.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
9バイトの圧縮列を保護用バイト付きのRAMバッファへ展開し、48バイトの結果をタイルデータの0x8800へコピーするサンプルです。RLE TO RAMは048になります。最上段はCGBで赤く表示し、通常ポインターで展開した3枚を示します。
各段は左から、8×8の塗りつぶし正方形、8×8で太さ1ピクセルの枠、8×8タイルの左半分を埋める4×8の長方形です。タイル列は2・5・8で、ピクセルのx座標では16・40・64です。三段の上端のy座標は32・72・112です。DMGは黒、CGBは段ごとに赤・緑・青で表示します。FAILEDは000になる必要があります。表示数値はバイト数で、1枚16バイトのGBタイル3枚には48バイトの展開先が必要です。
検証は42条件で、空の列、1バイトの繰り返し、値0、255バイトの繰り返し、出力のページ境界を越える510バイト、入力のページ境界を越える列を扱います。宛先と保護領域の528バイト全体、展開長、ROMバンクの復元を照合します。直接VRAM版はDMG・CGBのLCD表示中と停止中、およびCGBのVBK 1も対象です。2枚の表示用サンプル画像はエミュレータの240フレーム後に確認します。不正な素材、割り込みからの並行利用、実機は検証対象に含みません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:382
Expand count/value byte pairs until a zero count terminates the stream. The caller must provide a valid terminator and sufficient destination storage; there are no source or destination bounds parameters. Return the 16-bit byte count.
kitaqgb/lib/rle.c:8
u16 rle_decode(void* dst, const void* src)
{
const u8* in = (const u8*)src;
u8* out = (u8*)dst;
u16 total = 0;
while (1) {
u8 count = *in++;
u8 value;
if (count == 0) break;
value = *in++;
while (count != 0) {
*out++ = value;
total++;
count--;
}
}
return total;
}rle_decode_far
指定したROMバンクから__farpeek8で圧縮データを1バイトずつ読み、同じ個数・値の形式をRAMへ展開します。切り替え式のROMバンクに置いた圧縮素材に使います。展開先は通常のRAMポインターであり、bank引数が指定するのは入力側のバンクです。
書式
u16 rle_decode_far(void* dst, u8 bank, const void* src);引数
dst展開後のデータ全体が入る、書き込み可能なRAMへのポインターです。関数には容量を渡せません。まだ読んでいない組を出力が上書きしないよう、入力と展開先を重ねないでください。
bank入力ROMのバンク番号をu8で指定します。カートリッジのマッパーと素材の配置に合う番号・CPUアドレスの組を使ってください。圧縮データの各バイト取得後には、元のROMバンク選択を復元します。展開先のVRAMバンクを選ぶ引数ではなく、MBC5の9ビットのバンク番号も表せません。
src指定したバンク内の圧縮列を指すCPUポインターです。アドレスは進みますが、バンク番号は変わりません。終端0までの列全体を読み取り可能な配置に収めてください。0x7FFFを越えても、次のROMバンクの先頭へ自動的に続くことはありません。
戻り値
書き込んだバイト数をu16で返します。先頭の個数が0なら何も展開せず0を返し、終端そのものはコピーしません。展開長はu16で表せる範囲に収めてください。戻り値に別のエラーコードはなく、タイルの枚数でもありません。
使用条件と制限
文字列専用ではなく、バイト単位の形式です。たとえば{3, 0x12, 2, 0xA5, 0}は、16進表記で12 12 12 A5 A5の5バイトに展開されます。個数が0でない組の値が0でも、通常のデータとして扱い、終端とは見なしません。
圧縮後のサイズ、展開先の容量、不正データの扱いを指定する引数はありません。素材の作成時や読み込み時に形式を確認し、終端0を付けて、展開長を計算してから呼び出してください。不正な列や終端のない列では、入力・出力とも領域外へ進む可能性があります。
RAM用の展開関数は通常の書き込みを行い、LCDのアクセス可能時間には同期しません。展開先はRAMにしてください。表示する場合は、適切なVRAMコピーAPIで別途転送します。サンプルではLCDを止めてから転送しています。
バンク付きの読み取り中に、割り込み処理でROMの配置を変更したり、同じ遠距離コピーの補助処理を重ねて呼んだりしないでください。補助処理はバンク管理用の状態を共有します。通常ポインター版では、展開関数と入力データを同時に読める配置にします。関数のバンクへの切り替えで、入力ポインターが指すデータが隠れないようにしてください。
rle_decodeとrle_decode_farはrpg.hで宣言され、lib/rle.cに実装されています。ゲームと一緒に実装を一度だけコンパイルしてください。完全なサンプルでは呼び出し側と展開関数を共通バンクに置くため、#pragma bank 0の後でrle.cをインクルードしています。このサンプルのビルドでは、rle.cをさらに別の入力ファイルとして渡さないでください。__rle_decode_vram自体にはライブラリの実装ファイルは不要です。
使用例
far_count=rle_decode_far(far_buffer+1,2,banked_rle); // Read encoded pairs from ROM bank 2.
if(*rle_bank_marker!=0xA1)failures++; // ROM bank 1 must be visible again.
__vram_copy(0x8830,far_buffer+1,far_count);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ROMバンク2から圧縮列を読んで48バイトをRAMへ展開し、0x8830へ転送するサンプルです。BANKED RAMは048になります。中央の段はCGBで緑です。ROMバンク1の目印も読み、呼び出し前の配置に戻ったことを確認します。
各段は左から、8×8の塗りつぶし正方形、8×8で太さ1ピクセルの枠、8×8タイルの左半分を埋める4×8の長方形です。タイル列は2・5・8で、ピクセルのx座標では16・40・64です。三段の上端のy座標は32・72・112です。DMGは黒、CGBは段ごとに赤・緑・青で表示します。FAILEDは000になる必要があります。表示数値はバイト数で、1枚16バイトのGBタイル3枚には48バイトの展開先が必要です。
検証は42条件で、空の列、1バイトの繰り返し、値0、255バイトの繰り返し、出力のページ境界を越える510バイト、入力のページ境界を越える列を扱います。宛先と保護領域の528バイト全体、展開長、ROMバンクの復元を照合します。直接VRAM版はDMG・CGBのLCD表示中と停止中、およびCGBのVBK 1も対象です。2枚の表示用サンプル画像はエミュレータの240フレーム後に確認します。不正な素材、割り込みからの並行利用、実機は検証対象に含みません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:386
Decode the same count/value format using bank-qualified reads for every source byte. Keep the encoded stream within the supplied bank/address mapping and provide enough destination space; a zero count ends the stream.
kitaqgb/lib/rle.c:34
u16 rle_decode_far(void* dst, u8 bank, const void* src)
{
const u8* p = (const u8*)src;
u8* out = (u8*)dst;
u16 total = 0;
while (1) {
u8 count = __farpeek8(bank, p);
u8 value;
p++;
if (count == 0) break;
value = __farpeek8(bank, p);
p++;
while (count != 0) {
*out++ = value;
total++;
count--;
}
}
return total;
}rng16
rng8で連続して取得した2バイトから、符号なし16ビット値を作ります。1回目のバイトがビット15〜8、2回目がビット7〜0になります。1バイトでは必要な値の範囲を表せない場合に使います。
書式
u16 rng16();戻り値
(1回目のバイト << 8) | 2回目のバイトで作ったu16の値(0〜65535)です。ちょうど2回のバイト取得を消費します。65536通りの値が一様に現れることを示すものではありません。
使用条件と制限
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
word=rng16(); // First draw 2 is high, second draw 34 is low: 0x0222 = 546.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
2バイトからワードを作る順序を確かめるサンプルです。0x1234で初期化すると、バイト2と34から0x0222を作るため、RNG16は00546になります。FIRSTとSECONDでは対応するバイト値を個別に確認できます。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:196
Consume two generator bytes in high-byte/low-byte order to form a 16-bit value.
kitaqgb/lib/rng.c:22
u16 rng16()
{
u16 hi = (u16)rng8();
u16 lo = (u16)rng8();
return (u16)((hi << 8) | lo);
}rng8
GBライブラリを通じて1バイトの疑似乱数を取得します。rng8は__rng8をちょうど1回呼ぶ関数で、rng.cのほかの補助関数もこの基本操作を利用します。
書式
u8 rng8();戻り値
共有生成器を1回進めて得たu8の値(0〜255)です。これは型と値の範囲を示しており、各バイト値が同じ確率で出るという意味ではありません。
使用条件と制限
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234); // Reset the same generator used by the intrinsics.
wrapped=rng8(); // One byte draw: 2 after the preceding seed.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ライブラリのバイト取得と組み込み関数を比べるサンプルです。rng_seed(0x1234)の後のRNG8は00002となり、FIRSTと一致します。シード設定では最初の乱数を消費しません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:194
Advance the compiler-provided pseudorandom generator and return one byte.
kitaqgb/lib/rng.c:4
u8 rng8()
{
return __rng8();
}rng_chance
rng_range(100) < percentで、成立・不成立を判定します。敵がアイテムを落とすかどうかなど、おおよその確率で決めるゲーム処理に使えます。0なら必ず偽、100以上なら必ず真を返し、いずれの場合も乱数を消費しません。
書式
u8 rng_chance(u8 percent);引数
percentu8で指定する成立確率の目安です。1〜99では1バイトの乱数を剰余で縮めて比較します。0なら偽、100〜255なら真です。中間の値は判定のしきい値であり、厳密にその百分率になることを保証しません。
戻り値
偽なら0、真なら1に正規化した真偽値です。しきい値1〜99の呼び出しでは、1バイトの乱数を消費します。
使用条件と制限
入力の範囲が上限で割り切れないと、剰余による縮小で出やすい結果が生じます。元の生成器が一様とは限りません。範囲付きの結果や確率のしきい値は、厳密な確率ではなくゲーム処理上の目安として使ってください。
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
chance=rng_chance(50); // Here 2 % 100 < 50, so the result is 1 (true).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
50%を目安とする判定を1回行うサンプルです。シードから得たバイトは2で、2 % 100 < 50が成立するためCHANCE 50は00001になります。1回成立したことは、長期的な成功率が50%であるという検証ではありません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:214
Return false for zero and true for values at least 100. Intermediate values use the modulo-based range generator, so percentages are approximate.
kitaqgb/lib/rng.c:51
u8 rng_chance(u8 percent)
{
if (percent == 0) return 0;
if (percent >= 100) return 1;
return (u8)(rng_range(100) < percent);
}rng_next16
rng16と同じ、2回の取得で組み立てたワードを返します。ライブラリ内でrng16を呼ぶ別名の関数で、1回目が上位、2回目が下位バイトになります。取得前に余分な値を読み飛ばすことはありません。
書式
u16 rng_next16();戻り値
(1回目のバイト << 8) | 2回目のバイトで作ったu16の値(0〜65535)です。ちょうど2回のバイト取得を消費します。65536通りの値が一様に現れることを示すものではありません。
使用条件と制限
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
nextword=rng_next16(); // The same two-draw word: 546.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じシードでワード取得の別名関数とrng16を比べるサンプルです。NEXT16とRNG16はともに00546で、上位・下位の順に取得した2バイトを表します。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:209
Alias the two-step 16-bit generator.
kitaqgb/lib/rng.c:30
u16 rng_next16()
{
return rng16();
}rng_next8
rng8と同じ処理で次の1バイトを取得します。名前のnextは乱数列を進めることを表します。rng8とrng_next8を続けて呼ぶと、一つの共有生成器から順に2回取り出すことになります。
書式
u8 rng_next8();戻り値
共有生成器を1回進めて得たu8の値(0〜255)です。これは型と値の範囲を示しており、各バイト値が同じ確率で出るという意味ではありません。
使用条件と制限
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
nextbyte=rng_next8(); // One draw, just like rng8(): 2.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
同じシードでバイト取得の別名関数を確認するサンプルです。比較の前にそれぞれシードを設定するため、NEXT8とRNG8はともに00002です。設定し直さなければ、同じ列から順番に値を消費します。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:207
Alias the byte generator; this consumes one generator step.
kitaqgb/lib/rng.c:16
u8 rng_next8()
{
return rng8();
}rng_range
指定した上限をそのままrand_rangeに渡します。rng_で始まる関数名に揃えて範囲付きの選択を行いたい場合に使えます。1バイトの取得と剰余計算を使い、上限0で乱数を消費しない動作も同じです。
書式
u8 rng_range(u8 max);引数
max結果に含めない上限値です。型はu8で、指定範囲は0〜255です。max > 0なら戻り値は必ずmaxより小さくなります。256は指定できません。剰余を取らずに1バイトの結果をそのまま使う場合はrng8を使ってください。
戻り値
max > 0なら1回取得し、0〜max−1のu8を返します。max == 0では取得せずに0を返します。max == 1では結果は常に0ですが、乱数は1回消費します。
使用条件と制限
入力の範囲が上限で割り切れないと、剰余による縮小で出やすい結果が生じます。元の生成器が一様とは限りません。範囲付きの結果や確率のしきい値は、厳密な確率ではなくゲーム処理上の目安として使ってください。
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234);
alias=rng_range(10); // The same exclusive upper bound and result, 2.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
範囲付き選択の二つの関数名を比べるサンプルです。各呼び出しの前にシードを設定し直すため、ALIAS 10とRANGE 10はともに00002になります。上限値10は結果に含みません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:211
Alias rand_range and return a value in [0,max), or zero when max is zero.
kitaqgb/lib/rng.c:44
u8 rng_range(u8 max)
{
return rand_range(max);
}rng_seed
GBライブラリのインターフェースからシードを設定します。__rng_seedを呼ぶラッパーであり、rng8・rng16・各選択関数が使う共有状態を初期化します。別の乱数生成器を作る関数ではありません。
書式
void rng_seed(u16 seed);引数
seed0〜65535の符号なし16ビットのシードです。指定した2バイトをそのまま保存します。再現したいゲーム処理の乱数取得を始める前に設定し、使用した値を記録してください。
戻り値
ありません(void)。
使用条件と制限
状態0も受け付けます。次のバイト取得でGB生成器が0x5AA5に置き換えてから更新するため、__rng_seed(0)の直後の__rng8()は122を返します。毎回シードを設定し直すと、乱数性が増すのではなく列の先頭に戻ります。
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
rng_seed(0x1234); // Reset the same generator used by the intrinsics.
wrapped=rng8(); // One byte draw: 2 after the preceding seed.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ライブラリのバイト取得と組み込み関数を比べるサンプルです。rng_seed(0x1234)の後のRNG8は00002となり、FIRSTと一致します。シード設定では最初の乱数を消費しません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:205
Replace the generator seed so subsequent calls can reproduce a sequence.
kitaqgb/lib/rng.c:10
void rng_seed(u16 seed)
{
__rng_seed(seed);
}save_check
データを取り出さず、save_existsと同じ完全性検査を行います。
書式
u8 save_check(u8 slot);引数
slotスロット番号0~3。
戻り値
有効なら1、それ以外は0。長さ0の保存も有効です。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
使用例
result[4]=save_check(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:341
Alias save_exists, including its header and checksum validation.
kitaqgb/lib/save.c:175
u8 save_check(u8 slot)
{
return save_exists(slot);
}save_clear
指定スロットの512バイトを0で埋め、保存を無効にします。
書式
void save_clear(u8 slot);引数
slotスロット番号0~3。
戻り値
戻り値はありません。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
0~3以外のスロットは無視します。ほかのスロットは変更しません。
使用例
save_clear(0);result[16]=save_check(0);result[17]=save_write(3,payload,0);result[18]=save_exists(3);save_clear(3);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:344
Zero the complete selected slot, including header and payload; invalid slot IDs are ignored. Cartridge RAM is disabled when clearing finishes.
kitaqgb/lib/save.c:225
void save_clear(u8 slot)
{
u16 base;
u16 i;
if (slot >= SAVE_SLOT_COUNT) return;
base = save_slot_base(slot);
save_enable();
i = 0;
while (i < SAVE_SLOT_SIZE) {
save_write_abs((u16)(base + i), 0);
i++;
}
save_disable();
}save_exists
スロットのヘッダー・長さ・チェックサムを調べ、有効な保存があるか確認します。
書式
u8 save_exists(u8 slot);引数
slotスロット番号0~3。
戻り値
有効なら1、それ以外は0。長さ0の保存も有効です。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
使用例
result[0]=save_exists(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:334
Validate a slot's header, payload bounds and checksum without copying the payload out. A nonzero result means the stored record passes these checks.
kitaqgb/lib/save.c:182
u8 save_exists(u8 slot)
{
u16 base;
u16 stored_len;
u8 stored_sum;
if (slot >= SAVE_SLOT_COUNT) return 0;
base = save_slot_base(slot);
save_enable();
if (save_read_abs(base + 0) != SAVE_MAGIC0) {
save_disable();
return 0;
}
if (save_read_abs(base + 1) != SAVE_MAGIC1) {
save_disable();
return 0;
}
if (save_read_abs(base + 2) != SAVE_VERSION) {
save_disable();
return 0;
}
stored_len = (u16)save_read_abs(base + 3);
stored_len |= (u16)((u16)save_read_abs(base + 4) << 8);
stored_sum = save_read_abs(base + 5);
if (stored_len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) {
save_disable();
return 0;
}
if (save_checksum_ram((u16)(base + SAVE_HEADER_SIZE), stored_len) != stored_sum) {
save_disable();
return 0;
}
save_disable();
return 1;
}save_init
RAMバンク0を選んで一度有効化し、無効化して初期化を終えます。
書式
void save_init();戻り値
戻り値はありません。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
保存データは消去せず、カートリッジの検出やフォーマットも行いません。
使用例
save_init();save_clear(0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:337
Initialize the expected RAM access state and leave cartridge RAM disabled. Existing slot contents are preserved.
kitaqgb/lib/save.c:70
void save_init()
{
save_enable();
save_disable();
}save_load
save_readと同じ検査を通して保存データを読み込みます。
書式
u8 save_load(u8 slot, void* data, u16 len);引数
slotスロット番号0~3。
data読み込み先。lenバイトを書き込める必要があります。
lenデータ長0~506バイト。読み込み時は保存された長さと完全一致させます。
戻り値
読み込み成功で1、失敗で0。要求した長さが保存時と一致する必要があります。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
8ビット加算チェックサムはすべての破損を検出するものではありません。書き込みは電源断に対して原子的ではなく、成功値は実機への保存保証ではありません。
使用例
readback[1]=0;result[10]=save_load(0,readback,3);result[11]=readback[1];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:339
Alias the checked save_read operation.
kitaqgb/lib/save.c:114
u8 save_load(u8 slot, void* data, u16 len)
{
return save_read(slot, data, len);
}save_read
ヘッダー・長さ・チェックサムを検査し、成功時だけデータをコピーします。
書式
u8 save_read(u8 slot, void* data, u16 len);引数
slotスロット番号0~3。
data読み込み先。lenバイトを書き込める必要があります。
lenデータ長0~506バイト。読み込み時は保存された長さと完全一致させます。
戻り値
成功で1、検査失敗で0。失敗時は出力配列を変更しません。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
8ビット加算チェックサムはすべての破損を検出するものではありません。書き込みは電源断に対して原子的ではなく、成功値は実機への保存保証ではありません。
使用例
readback[0]=99;result[5]=save_read(0,readback,2);result[6]=readback[0];result[7]=save_read(0,readback,3);result[8]=readback[0];result[9]=readback[2];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:331
Require matching magic/version, exact requested length and a valid checksum before copying data into the destination. Validation failure leaves the destination unchanged. An invalid slot returns before touching RAM control; validation failures after enabling RAM disable it before returning zero.
kitaqgb/lib/save.c:125
u8 save_read(u8 slot, void* data, u16 len)
{
u8* dst = (u8*)data;
u16 base;
u16 stored_len;
u8 stored_sum;
u16 i;
if (slot >= SAVE_SLOT_COUNT) return 0;
base = save_slot_base(slot);
save_enable();
if (save_read_abs(base + 0) != SAVE_MAGIC0) {
save_disable();
return 0;
}
if (save_read_abs(base + 1) != SAVE_MAGIC1) {
save_disable();
return 0;
}
if (save_read_abs(base + 2) != SAVE_VERSION) {
save_disable();
return 0;
}
stored_len = (u16)save_read_abs(base + 3);
stored_len |= (u16)((u16)save_read_abs(base + 4) << 8);
stored_sum = save_read_abs(base + 5);
if (stored_len != len || stored_len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) {
save_disable();
return 0;
}
if (save_checksum_ram((u16)(base + SAVE_HEADER_SIZE), stored_len) != stored_sum) {
save_disable();
return 0;
}
i = 0;
while (i < stored_len) {
dst[i] = save_read_abs((u16)(base + SAVE_HEADER_SIZE + i));
i++;
}
save_disable();
return 1;
}save_write
指定スロットへデータと長さ・チェックサムを書き込みます。
書式
u8 save_write(u8 slot, const void* data, u16 len);引数
slotスロット番号0~3。
data保存する読み取り元。lenバイトが必要です。
lenデータ長0~506バイト。読み込み時は保存された長さと完全一致させます。
戻り値
引数が有効なら1、不正なスロット・長さなら0。書き戻し検査はしません。
使用条件と制限
既定は4スロット×512バイトです。各スロットは6バイトのヘッダーと最大506バイトのデータを持ちます。MBC5と8KiB SRAMのビルド例を示します。処理後はRAMを無効にし、以前のRAMバンクや有効状態は復元しません。
8ビット加算チェックサムはすべての破損を検出するものではありません。書き込みは電源断に対して原子的ではなく、成功値は実機への保存保証ではありません。
使用例
payload[0]=7;payload[1]=42;payload[2]=255;result[1]=save_write(0,payload,3);result[2]=save_write(4,payload,3);result[3]=save_write(0,payload,507);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
7・42・255の3バイトを保存し、長さ不一致・無効スロット・破損チェックサムを試します。読み込み成功時の中央値は42、破損時には出力先77を保持し、消去後の検査は0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k実装とソース
kitaqgb/lib/rpg.h:326
Validate slot and payload length, write payload bytes, then write the header and checksum. Return one after completing the writes; interruption can leave a partially replaced slot, so this is not transactional save storage.
kitaqgb/lib/save.c:81
u8 save_write(u8 slot, const void* data, u16 len)
{
const u8* src = (const u8*)data;
u16 base;
u16 i;
u8 sum = 0;
if (slot >= SAVE_SLOT_COUNT) return 0;
if (len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) return 0;
base = save_slot_base(slot);
save_enable();
i = 0;
while (i < len) {
u8 v = src[i];
save_write_abs((u16)(base + SAVE_HEADER_SIZE + i), v);
sum = (u8)(sum + v);
i++;
}
save_write_abs(base + 0, SAVE_MAGIC0);
save_write_abs(base + 1, SAVE_MAGIC1);
save_write_abs(base + 2, SAVE_VERSION);
save_write_abs(base + 3, (u8)(len & 0xFF));
save_write_abs(base + 4, (u8)(len >> 8));
save_write_abs(base + 5, sum);
save_disable();
return 1;
}script_run
実行するスクリプトを選び、待ち回数・選択結果・復帰先を初期化します。
書式
void script_run(u8 bank, const u8* script);引数
bank命令列のROMバンク。
script有効な終端付き命令列の先頭。呼び出しだけでは実行しません。
戻り値
戻り値はありません。
使用条件と制限
命令は00 END、01 TEXT(bank,ptr16)、02 SET_FLAG(id16)、03 CLEAR_FLAG(id16)、04 JUMP(bank,ptr16)、05 IF_FLAG(id16,bank,ptr16)、06 CHOICE(count,table16)、07 WAIT(n)、08 CALL_EVENT(bank,ptr16)です。16ビット値は下位バイトが先です。文字列・ポインタ表と命令列は読み取り中に消さず、バンク境界をまたがせないでください。
使用例
flag_clear(19);script_run(1,(const u8*)&entry);result[0]=flag_get(19);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
バンク1からバンク2へジャンプし、フラグ19を立てて待つ例です。最初の待ち2回は1、ジャンプ先のフラグは1、WAIT後に終了2となります。別バンクへ制御が届いたことをフラグで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:250
Replace the active script and clear wait, choice and saved-return state.
kitaqgb/lib/script.c:105
void script_run(u8 bank, const u8* script)
{
script_active = 1;
script_bank = bank;
script_pc = script;
script_wait = 0;
script_last_choice = 0xFF;
script_return_active = 0;
}script_step
命令を進め、表示・選択・待ち・終端で呼び出し元へ戻ります。
書式
u8 script_step();戻り値
0=TEXT/CHOICEで中断、1=待ち、2=終了または未実行。未知の命令も2で終了します。
使用条件と制限
命令は00 END、01 TEXT(bank,ptr16)、02 SET_FLAG(id16)、03 CLEAR_FLAG(id16)、04 JUMP(bank,ptr16)、05 IF_FLAG(id16,bank,ptr16)、06 CHOICE(count,table16)、07 WAIT(n)、08 CALL_EVENT(bank,ptr16)です。16ビット値は下位バイトが先です。文字列・ポインタ表と命令列は読み取り中に消さず、バンク境界をまたがせないでください。
WAIT nは読み取った回で1を返し、その後さらにn回待ちます。CALL_EVENTの復帰先は1段だけです。CHOICEは0でない件数が必要で、選択まで内部で待ち続けます。待ちのない無限ジャンプはゲームループを止めるため、必ず終了かyieldする命令へ到達させます。
使用例
result[3]=script_step();result[4]=flag_get(19);result[5]=script_step();result[6]=script_step();result[7]=script_step();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
バンク1からバンク2へジャンプし、フラグ19を立てて待つ例です。最初の待ち2回は1、ジャンプ先のフラグは1、WAIT後に終了2となります。別バンクへ制御が届いたことをフラグで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:254
Execute commands until an output/choice yields (0), a wait yields (1), or execution ends (2). Control/flag commands continue within the same call, so a script loop without a yielding command can block the game loop indefinitely.
kitaqgb/lib/script.c:125
u8 script_step()
{
if (script_active == 0) return 2;
if (script_wait != 0) {
script_wait--;
return 1;
}
while (script_active != 0) {
u8 op = script_read8();
// An END returns to the one saved caller when present; otherwise it ends the script.
if (op == SCRIPT_OP_END) {
if (script_return_active != 0) {
script_bank = script_return_bank;
script_pc = script_return_pc;
script_return_active = 0;
continue;
}
script_active = 0;
return 2;
}
if (op == SCRIPT_OP_TEXT) {
u8 bank = script_read8();
u16 ptr = script_read16();
text_print_far(bank, (const u8*)ptr);
return 0;
}
if (op == SCRIPT_OP_SET_FLAG) {
u16 id = script_read16();
flag_set(id);
continue;
}
if (op == SCRIPT_OP_CLEAR_FLAG) {
u16 id = script_read16();
flag_clear(id);
continue;
}
if (op == SCRIPT_OP_JUMP) {
// Read all operands in the source bank before installing the destination.
u8 bank = script_read8();
u16 ptr = script_read16();
script_bank = bank;
script_pc = (const u8*)ptr;
continue;
}
// Read all branch operands before switching bank/address so an untaken branch
// still consumes the full instruction.
if (op == SCRIPT_OP_IF_FLAG) {
u16 id = script_read16();
u8 bank = script_read8();
u16 ptr = script_read16();
if (flag_get(id) != 0) {
script_bank = bank;
script_pc = (const u8*)ptr;
}
continue;
}
if (op == SCRIPT_OP_CHOICE) {
u8 count = script_read8();
u16 table = script_read16();
script_last_choice = script_choice(script_bank, (const u8*)table, count);
return 0;
}
if (op == SCRIPT_OP_WAIT) {
script_wait = script_read8();
return 1;
}
// Save the continuation only when the single return slot is empty. Nested
// calls replace the current script but do not push another return address.
if (op == SCRIPT_OP_CALL_EVENT) {
u8 bank = script_read8();
u16 ptr = script_read16();
if (script_return_active == 0) {
script_return_active = 1;
script_return_bank = script_bank;
script_return_pc = script_pc;
}
script_bank = bank;
script_pc = (const u8*)ptr;
continue;
}
// Unknown opcodes terminate with the same return code as END; there is no distinct malformed-stream error.
script_active = 0;
return 2;
}
return 2;
}script_wait_frames
次のscript_step呼び出しを指定回数だけ待ち状態にします。
書式
void script_wait_frames(u8 n);引数
n待つ呼び出し回数0~255。0なら待ちを解除します。
戻り値
戻り値はありません。
使用条件と制限
1フレームに1回script_stepを呼ぶとフレーム数になります。関数自体はハードウェアのVBlankを待ちません。
使用例
script_wait_frames(2);result[1]=script_step();result[2]=script_step();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
バンク1からバンク2へジャンプし、フラグ19を立てて待つ例です。最初の待ち2回は1、ジャンプ先のフラグは1、WAIT後に終了2となります。別バンクへ制御が届いたことをフラグで確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:257
Set the number of subsequent script_step calls to spend waiting. Frame-based timing requires the application to call script_step once per frame.
kitaqgb/lib/script.c:117
void script_wait_frames(u8 n)
{
script_wait = n;
}text_choice
操作対象の文字領域に縦並びの選択肢を描き、選択を待ちます。上下でカーソルを循環移動し、Aで決定、Bで取消します。ループは選択肢を描き直し、VBlankを待ってから入力を1回調べます。戻り値は0から始まる選択番号で、取消または空の一覧では255です。
書式
u8 text_choice(const u8* const* choices, u8 count);引数
choicescount個の読み取り可能な文字列ポインターが入った配列へのポインターです。各文字列は0で終わるタイル番号列で、選択肢のラベルは会話用の制御コードを解釈しません。選択が終わるまで、ポインター配列と各文字列を読み取れる配置に保ちます。
count選択肢の数です。0ならポインター配列を読まず、直ちに255を返します。1以上なら内側にcount行以上を用意し、カーソル1マスと最長ラベルが収まる幅を確保します。切り取りやスクロールは行いません。
戻り値
Aで決定した場合は0〜count−1の番号です。255はBによる取消、またはcountが0だったことを表すため、必要なら呼び出し側で区別します。関数は文字領域を閉じず、一覧も消去しません。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
呼び出す前に領域を開き、十分な大きさを用意します。選択肢の処理は開いているかを検査せず、記録済みの位置を使います。ラベルは左端から2列、上端から1行内側に始まり、そのすぐ左のマスがカーソルです。選択肢の描画には会話の文字速度設定を使いません。
入力は押した瞬間に反応し、UP、DOWN、A、Bの順で処理します。方向を押し続けても移動は繰り返さないため、いったん離して押し直します。直前のボタン状態を0で始めるため、入る前から押しているボタンも直ちに作用します。移動とAを同時に押すと移動後の番号を返しますが、最後に描いたカーソルは移動前の位置のままの場合があります。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
selected=text_choice(dialogue_choices,3);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
DOWNを1回押してからAを押します。確認した実行では、右向きのカーソルがSHIELDの左のタイル座標(2,4)にあり、SELECTEDは1です。3つのラベルは(3,3)、(3,4)、(3,5)に始まります。枠・カーソル・ラベルはCGBでは緑、DMGでは黒で、結果と操作説明は黒です。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_choice.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_choice.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:231
Redraw choices and block for edge-triggered navigation: UP/DOWN wrap, A accepts, and B returns 0xFF. Zero choices also returns 0xFF. Choice strings use raw tile bytes, and the caller must fit them inside the available rows.
kitaqgb/lib/text.c:316
u8 text_choice(const u8* const* choices, u8 count)
{
u8 cursor;
u8 prev;
if (count == 0) return 0xFF;
cursor = 0;
prev = 0;
while (1) {
u8 i;
for (i = 0; i < count; i++) {
u8 py = (u8)(text_y + 1 + i);
__settile_xy((u8)(text_x + 1), py, (i == cursor) ? TEXT_TILE_CURSOR : TEXT_TILE_FILL);
text_draw_string_at((u8)(text_x + 2), py, choices[i]);
}
__wait_vblank();
{
u16 kt = __readpadex(prev);
u8 trigger;
prev = (u8)kt;
trigger = (u8)(kt >> 8);
if ((trigger & PAD_KEY_UP) != 0) {
if (cursor == 0) cursor = (u8)(count - 1);
else cursor--;
}
if ((trigger & PAD_KEY_DOWN) != 0) {
cursor++;
if (cursor >= count) cursor = 0;
}
if ((trigger & PAD_KEY_A) != 0) return cursor;
if ((trigger & PAD_KEY_B) != 0) return 0xFF;
}
}
}text_clear_rect
背景タイルマップの長方形の範囲をTEXT_TILE_FILLで埋めます。ラベルの消去、数値表示欄の初期化、パネルの一部の消去に使います。現在の文字カーソル、文字領域の開閉状態、大きさは変わりません。
書式
void text_clear_rect(u8 x, u8 y, u8 w, u8 h);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
wタイルのマス単位の幅です。text_windowでは左右の枠を含み、内側の幅はmax(w−2, 0)マスです。text_clear_rectでは、各行で埋めるマス数です。
hタイルのマス単位の高さです。text_windowでは上下の枠を含み、内側の高さはmax(h−2, 0)マスです。text_clear_rectでは、埋める行数です。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
公開されている数値表示関数は、新しく表示する値の桁数だけを表示欄として使います。その右に残ったマスは消しません。同じ位置の12345へ7を重ねると72345と表示されます。7だけにするには、先にtext_clear_rectで確保した5マスを消去します。符号付き16ビットの表示欄には6マスを用意します。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_clear_rect(7,5,5,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
枠の5行目には、最初にKEEP ERASE KEEPを書きます。(7,5)から5マスを消すとERASEだけが消え、左右のKEEPと枠は残ります。CLEANの行でも、5マスの数値表示欄を消してから7を書いています。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:237
Fill the supplied tile rectangle without changing the active text window or cursor.
kitaqgb/lib/text.c:220
void text_clear_rect(u8 x, u8 y, u8 w, u8 h)
{
__settile_rect(x, y, w, h, TEXT_TILE_FILL);
}text_close
操作対象の文字領域を枠ごとTEXT_TILE_FILLで埋め、閉じた状態にします。閉じているときに再度呼んでも何もしません。領域を開く前にあった背景タイルを復元する処理ではありません。
書式
void text_close();戻り値
戻り値はありません。描画や待機の処理は、関数が戻る前に行われます。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
領域が閉じている場合や内側に使えるマスがない場合、通常の文字は表示しません。ただし制御バイトは解釈するため、WAITによる待機やページ制御による待機・記録済み領域の消去は起こり得ます。閉じている領域へのtext_printが常に何もしないわけではありません。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
text_close();
text_print("HIDDEN"); // With the area closed, ordinary characters are not drawn.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは(1,9)に18×3マスの領域を開き、ERASE MEを書いてから閉じ、HIDDENの表示を試します。CLOSED AREAの下のx=1〜18、y=9〜11のマスは空白になり、文字も枠も残りません。上の青い会話領域は見えたままです。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:220
Erase the open window with the fill tile and disable windowed character output.
kitaqgb/lib/text.c:200
void text_close()
{
if (text_opened == 0) return;
__settile_rect(text_x, text_y, text_w, text_h, TEXT_TILE_FILL);
text_opened = 0;
}text_open
背景マップ上に文字領域を開きます。位置と大きさを記録し、文字カーソルを内側の先頭へ戻して枠を描き、内側を消去します。別の位置で開くと操作対象の領域とカーソルが変わりますが、以前に別の位置へ描いた領域は消去しません。
書式
void text_open(u8 x, u8 y, u8 w, u8 h);引数
x8×8ピクセルの背景マップのマス単位で指定する、領域の左端の列です。幅全体が0〜31列に収まるようにします。
y8×8ピクセルの背景マップのマス単位で指定する、領域の上端の行です。高さ全体が0〜31行に収まるようにします。
w左右の枠を含む全体の幅をマス単位で指定します。内側に文字を1列表示するには3マス以上必要です。
h上下の枠を含む全体の高さをマス単位で指定します。内側に文字を1行表示するには3マス以上必要です。
戻り値
戻り値はありません。描画や待機の処理は、関数が戻る前に行われます。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
text_open(1,2,18,5);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
DIALOGUE FLOWは(1,2)に18×5マスの枠を開き、内側の16×3マスにNEAR、READY、BANK 2を1行ずつ表示します。見える枠線はピクセル座標のx=11〜147、y=19〜51にあり、CGBでは文字とともに青、DMGでは黒です。その後で消去テスト用の領域を開いても、この枠は残ります。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:218
Set the tile-space window, reset its cursor and redraw it immediately. Opening does not reset the existing character speed or load font tiles.
kitaqgb/lib/text.c:187
void text_open(u8 x, u8 y, u8 w, u8 h)
{
text_x = x;
text_y = y;
text_w = w;
text_h = h;
text_opened = 1;
text_reset_cursor();
text_draw_frame();
text_clear_inside();
}text_print
通常のポインターから制御コード付きの列を読み、操作対象の文字領域のカーソル位置へ文字タイルを書きます。改行、指定回数の待ち、ページ送り待ちを解釈し、内側の幅で折り返します。ENDまで処理してから戻るため、ページ送りではボタンが押されるまで呼び出し元へ戻りません。
書式
void text_print(const u8* str);引数
strTEXT_CTRL_END(0)で終わる列への読み取り可能なポインターです。処理が待機している間も、WAIT直後の引数を含めて列全体を読み取れるメモリ配置に保ちます。バッファ長の指定や形式の検査はありません。
戻り値
戻り値はありません。描画や待機の処理は、関数が戻る前に行われます。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
制御バイトは、0x00が終了、0x0Aが内側の次行への改行、0x01が直後の1バイトで指定するVBlank待ち、0x02がページ送り待ち・内側の消去・カーソルの初期化です。それ以外はタイル番号です。WAITの引数には0も指定でき、この0は列の終端にはなりません。
改行または自動折り返しで最後の内側の行を越えると、ページ待ちの印を描き、A・B・STARTのいずれかを待って内側を消し、先頭のマスから再開します。行をスクロールするのではなく、ページを入れ替えます。明示的な0x02は印を追加せず待機します。ページ待ちに入る時点のボタン状態を記憶していないため、すでに押されているボタンでも進みます。
領域が閉じている場合や内側に使えるマスがない場合、通常の文字は表示しません。ただし制御バイトは解釈するため、WAITによる待機やページ制御による待機・記録済み領域の消去は起こり得ます。閉じている領域へのtext_printが常に何もしないわけではありません。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
text_print(dialogue_near);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
通常の列はNEAR、改行、READY、12回の待ち、ENDの順です。NEARはタイル座標(2,3)、READYは(2,4)に始まります。その後のバンク付き文字列は同じカーソルから続くため、先頭の改行でBANK 2を内側の3行目へ置きます。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:224
Decode an END-terminated stream of tile bytes and newline/wait/page controls. WAIT consumes a following frame-count byte. The source must remain readable through blocking waits; the routine does not validate stream length.
kitaqgb/lib/text.c:261
void text_print(const u8* str)
{
while (*str != TEXT_CTRL_END) {
u8 c = *str++;
if (c == TEXT_CTRL_NEWLINE) {
text_newline();
continue;
}
if (c == TEXT_CTRL_WAIT) {
u8 frames = *str++;
text_delay_frames(frames);
continue;
}
if (c == TEXT_CTRL_PAGE_WAIT) {
text_wait_advance();
text_clear_inside();
text_reset_cursor();
continue;
}
text_put_tile(c);
}
}text_print_far
__farpeek8で指定ROMバンクから1バイトずつ読み、同じ会話用の制御コード付き列を処理します。現在読み込まれている入力用バンク以外に置いた会話データに使います。文字位置、速度、折り返し、ページ制御はtext_printと同じです。
書式
void text_print_far(u8 bank, const u8* str);引数
bank符号なし8ビットの入力ROMバンク番号です。サンプルは列をバンク2に置き、表示後も呼び出し元のバンク1の目印を読めることを確認します。
str指定バンク内の列を指すCPUアドレスです。アドレスは進みますがバンク番号は変わらず、切り替え式ROM領域の端を越えても次のバンクへ自動的には続きません。
戻り値
戻り値はありません。描画や待機の処理は、関数が戻る前に行われます。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
制御バイトは、0x00が終了、0x0Aが内側の次行への改行、0x01が直後の1バイトで指定するVBlank待ち、0x02がページ送り待ち・内側の消去・カーソルの初期化です。それ以外はタイル番号です。WAITの引数には0も指定でき、この0は列の終端にはなりません。
改行または自動折り返しで最後の内側の行を越えると、ページ待ちの印を描き、A・B・STARTのいずれかを待って内側を消し、先頭のマスから再開します。行をスクロールするのではなく、ページを入れ替えます。明示的な0x02は印を追加せず待機します。ページ待ちに入る時点のボタン状態を記憶していないため、すでに押されているボタンでも進みます。
領域が閉じている場合や内側に使えるマスがない場合、通常の文字は表示しません。ただし制御バイトは解釈するため、WAITによる待機やページ制御による待機・記録済み領域の消去は起こり得ます。閉じている領域へのtext_printが常に何もしないわけではありません。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
text_print_far(2,dialogue_far);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルはROMバンク2から改行とBANK 2を読みます。その後、呼び出し元のバンク1にある目印を読み、BANK RESTOREDに161を表示します。BANK 2の行はタイル座標(2,5)、バンク復元の数値は(16,14)にあります。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:227
Decode the same text controls while fetching each byte from the specified ROM bank. The pointer advances within that bank; it does not roll into the next bank.
kitaqgb/lib/text.c:286
void text_print_far(u8 bank, const u8* str)
{
const u8* p = str;
while (1) {
u8 c = __farpeek8(bank, p);
p++;
if (c == TEXT_CTRL_END) return;
if (c == TEXT_CTRL_NEWLINE) {
text_newline();
continue;
}
if (c == TEXT_CTRL_WAIT) {
u8 frames = __farpeek8(bank, p);
p++;
text_delay_frames(frames);
continue;
}
if (c == TEXT_CTRL_PAGE_WAIT) {
text_wait_advance();
text_clear_inside();
text_reset_cursor();
continue;
}
text_put_tile(c);
}
}text_print_s16
符号付き16ビット値を10進数で表示します。負の値ではマイナス記号のタイルに続いて絶対値を描き、0と正の値には符号を付けません。最も長い表示は-32768で、6マスを使います。
書式
void text_print_s16(u8 x, u8 y, s16 value);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
value−32768〜32767の符号付き値です。サンプルでは16ビットの値32768(0x8000)をs16に変換し、−32768を表示します。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
公開されている数値表示関数は、新しく表示する値の桁数だけを表示欄として使います。その右に残ったマスは消しません。同じ位置の12345へ7を重ねると72345と表示されます。7だけにするには、先にtext_clear_rectで確保した5マスを消去します。符号付き16ビットの表示欄には6マスを用意します。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_print_s16(11,12,(s16)32768);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
S16の行は(11,12)から-32768を表示し、マイナス記号を含めて6マスを使います。CGBでは緑です。符号付き16ビットの最小値を扱い、その正の絶対値がs16には収まらない場合を確認します。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:247
Print a minus tile for negative values, followed by the unsigned magnitude. Positive values use the unsigned decimal path; no plus sign is emitted.
kitaqgb/lib/text.c:246
void text_print_s16(u8 x, u8 y, s16 value)
{
u16 magnitude;
if (value < 0) {
__settile_xy(x, y, (u8)'-');
magnitude = (u16)((s16)0 - value);
text_print_u16_width((u8)(x + 1), y, magnitude, text_dec_digits_u16(magnitude));
return;
}
text_print_u16(x, y, (u16)value);
}text_print_u16
符号なし16ビット値を10進数にして、指定タイル位置から表示します。先頭に0を付けず、1〜5マスを使います。得点や所持数など、255を超える値に使えます。
書式
void text_print_u16(u8 x, u8 y, u16 value);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
value0〜65535の符号なし値です。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
公開されている数値表示関数は、新しく表示する値の桁数だけを表示欄として使います。その右に残ったマスは消しません。同じ位置の12345へ7を重ねると72345と表示されます。7だけにするには、先にtext_clear_rectで確保した5マスを消去します。符号付き16ビットの表示欄には6マスを用意します。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_print_u16(11,10,65535);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
U16の行は(11,10)に65535を表示し、5マスを使います。CGBでは青です。OVERWRITEは、5マスの表示欄を消さずに12345へ7を重ねた結果の72345を示します。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:244
Print an unsigned word in decimal using only the required number of cells.
kitaqgb/lib/text.c:239
void text_print_u16(u8 x, u8 y, u16 value)
{
text_print_u16_width(x, y, value, text_dec_digits_u16(value));
}text_print_u8
符号なし8ビット値を10進数にして、指定タイル位置から表示します。1〜3マスを使い、先頭に0は付けません。値0も1マスで表示します。残機や弾数などの小さなカウンターに使えます。
書式
void text_print_u8(u8 x, u8 y, u8 value);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
value0〜255の符号なし値です。それより広い型の式は、関数の引数でu8へ変換されます。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
公開されている数値表示関数は、新しく表示する値の桁数だけを表示欄として使います。その右に残ったマスは消しません。同じ位置の12345へ7を重ねると72345と表示されます。7だけにするには、先にtext_clear_rectで確保した5マスを消去します。符号付き16ビットの表示欄には6マスを用意します。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_print_u8(11,8,255);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
U8の行は(11,8)から255を表示し、ちょうど3マスを使います。CGBでは青です。OVERWRITEとCLEANの行では、短い値に置き換える前に表示欄を消す必要があることを確認できます。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:242
Print an unsigned byte in decimal using only the required number of cells.
kitaqgb/lib/text.c:233
void text_print_u8(u8 x, u8 y, u8 value)
{
text_print_u16_width(x, y, (u16)value, text_dec_digits_u16((u16)value));
}text_print_xy
終端0のタイル番号列を、背景マップ上の指定位置から横方向へ描きます。文字ウィンドウによる切り取り、折り返し、制御コードの解釈、1文字ごとの待ち時間はありません。text_printが使うカーソルも動かしません。
書式
void text_print_xy(u8 x, u8 y, const u8* str);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
str0で終わるタイル番号列への、読み取り可能なポインターです。各バイトをそのままタイル番号に使います。ASCIIの文字列を文字として表示するには、文字コードと同じタイル番号に対応する字形を置きます。呼び出しが戻るまで、入力のメモリ配置を保って読み取れるようにしてください。 UTF-8の解読や字形の読み込みは行いません。日本語などの多バイト文字を表示する場合は、ゲーム側で文字をタイル番号列へ変換し、対応する字形を用意してください。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_print_xy(2,3,"HELLO!");呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
HELLO!はタイル座標(2,3)、ピクセル座標(16,24)から始まり、CGBでは赤い枠の内側に赤く表示されます。位置は背景マップ上の絶対座標で、文字領域を開いてもxやyにオフセットは加わりません。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:240
Draw a zero-terminated tile string at absolute tile coordinates, without window clipping, control codes or the windowed printing delay.
kitaqgb/lib/text.c:227
void text_print_xy(u8 x, u8 y, const u8* str)
{
text_draw_string_at(x, y, str);
}text_set_speed
text_printまたはtext_print_farが通常の文字を書いた後に、VBlankを何回待つかを設定します。0なら文字ごとの待ち時間はありません。領域を開閉しても設定は変わりません。文字速度が0でも、明示的なWAIT命令の待ち時間は実行します。
書式
void text_set_speed(u8 speed);引数
speed表示した1文字の後に待つVBlankの回数で、0〜255です。ミリ秒ではありません。text_print_xy、数値表示関数、選択肢一覧の表示には作用しません。
戻り値
戻り値はありません。描画や待機の処理は、関数が戻る前に行われます。
使用条件と制限
有効な背景マップの範囲を指定し、先にフォントと枠のタイルを読み込みます。文字領域はLCDCのビット3が選ぶ背景マップを使い、ハードウェアのWindowレイヤーを有効にはしません。CGBでタイル番号を書くときはVBKを0にし、色属性は別に設定します。サンプルにはASCIIに合わせたフォントと、枠・カーソル・ページ待ちのタイルを用意しています。 会話文と選択肢は1バイトのタイル番号を使い、UTF-8を解読しません。同梱フォントにない文字を表示するには、字形タイルと、それに対応する番号列を用意します。会話文では制御バイトに使う値を文字へ割り当てないでください。
これらは処理を待つ関数で、待機中にゲーム更新、音楽の更新、描画ループを代わりに呼ぶことはありません。LCD停止中の__wait_vblankは直ちに戻るため、文字速度やWAITによるフレーム単位の待ち時間は発生しません。時間をかけた会話表示やフレームごとの入力確認ではLCDを有効にしてください。会話中も他の仕事を進める必要がある場合は、呼び出し側で待機を分割して扱う設計が必要です。
下位の安全なタイル書き込みは、LCD表示中には書き込み可能なモードを待ち、DI/EIで書き込みを保護します。開始時の割り込み禁止状態を保持せず、有効にする場合があります。これらの待機を含む関数を割り込みハンドラーから呼んだり、割り込み禁止状態の保持を期待したりしないでください。
完全な各サンプルはtext.cを1回取り込みます。ライブラリと説明書のサンプル用インクルード先を指定してビルドし、さらに別の入力ソースとしてlib/text.cを追加しないでください。バンク付き文字列のサンプルはバンク2のデータを配置するため、64KiBのMBC5 ROMを使います。
使用例
text_set_speed(2);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
会話サンプルは通常・バンク付きの両方の文字列に速度2を使い、12回待つWAITも含めます。画像は待機後の表示です。速度3、明示的な5回の待ち、閉じた状態での4回の待ちは別のフレーム観測で調べ、完成画像だけで待ち時間を確認したことにはしません。
エミュレータでは背景マップの内容全体と4枚のサンプル画像を照合します。別の実行で、開閉状態、通常・バンク付き制御列、時間待ち、明示・自動のページ待ち、A・B・STARTによる送り、押し続け、選択肢の循環、空の一覧、同時入力の優先順位を確認します。入力前後の観測点で、ページ待ちが実際に処理を止めることも調べます。実機と割り込みからの並行利用は検証していません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:233
Set VBlank waits per windowed character; zero prints without character delays.
kitaqgb/lib/text.c:208
void text_set_speed(u8 speed)
{
text_speed = speed;
}text_window
背景タイルマップに枠付きの文字領域を描き、ウィンドウ内文字表示の対象にします。text_openと同じ処理を呼び、カーソルを内側の先頭に戻して枠を描き直し、内側を消去します。設定済みの文字表示の待ち時間は保持します。
書式
void text_window(u8 x, u8 y, u8 w, u8 h);引数
x8×8ピクセルの背景マップのマス単位で指定する開始列です。出力全体が0〜31列に収まるようにします。背景スクロールをしない場合、画面に見えるのは0〜19列です。
y8×8ピクセルの背景マップのマス単位で指定する開始行で、0〜31の範囲です。背景スクロールをしない場合、画面に見えるのは0〜17行です。長方形は下端まで範囲内に収めます。
wタイルのマス単位の幅です。text_windowでは左右の枠を含み、内側の幅はmax(w−2, 0)マスです。text_clear_rectでは、各行で埋めるマス数です。
hタイルのマス単位の高さです。text_windowでは上下の枠を含み、内側の高さはmax(h−2, 0)マスです。text_clear_rectでは、埋める行数です。
戻り値
戻り値はありません。関数から戻るまでにタイルマップを書き換えます。文字数や成否コードは返しません。
使用条件と制限
LCDCのビット3が選ぶ背景マップへ描画します。ここでいう文字ウィンドウは背景マップ上の文字領域であり、ハードウェアのWindowレイヤーではありません。CGBでタイル番号を書くときはVBKを0にします。色属性、パレット、スクロールは設定しません。
描画前にフォントと枠の画像を読み込みます。既定の枠のタイル番号は1〜6、消去用は0、数字の先頭はASCIIの0(タイル48)です。マイナス記号にはASCIIの-(タイル45)を使います。サンプルには6枚の枠タイルと、ASCIIに合わせて配置した説明書用フォントを用意しています。
長方形や文字列は32×32のマップに収めてください。長方形の塗りつぶしは、幅や高さをマップの端で切り詰めません。幅または高さが0なら何も埋めませんが、text_windowはその領域を開いた状態として記録し、使用可能な内側のマスは0になります。
タイル書き込みの組み込み処理はLCDの状態を調べ、停止中なら直ちに書き、表示中なら書き込み可能なLCDモードを待ってDI/EIで書き込みを保護します。表示中の経路は割り込みを有効にするため、呼び出し前の割り込み禁止状態を維持すると考えないでください。ここでのサンプルとバイト単位の検証は、LCDを止めて書き込みます。
完全なサンプルはtext.cを1回取り込み、ライブラリの実装もプログラムと一緒にコンパイルします。このサンプルのビルドでは、別の入力としてlib/text.cを追加しないでください。TEXT_*のタイル定数を変更する場合は、rpg.hと実装を取り込む前に定義し、ライブラリをコンパイルする箇所で定義をそろえます。
使用例
text_window(1,2,18,5);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは(1,2)から18×5マスの領域を開きます。内側は16×3マスです。用意した枠は太さ1ピクセルで、見える外周の線はピクセル座標でx=11〜147、y=19〜51にあります。CGBでは赤、DMGでは黒です。
検証では、2枚のサンプル画面の全ピクセルと背景マップの1024バイト全体を照合します。別のDMG・CGB用ROMでは、符号なし8ビットの全256値、10進数の桁の境界、符号付き値の両端を調べ、数値の横に置いた点が保護領域として残ることも確認します。配置の検証には、空や幅の狭い長方形、マップ端の配置、制御値と同じ番号のタイル、絶対位置へ書いた後のウィンドウのカーソルを含みます。実機ではなくエミュレータでの検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:235
Open and redraw a window using the same tile-space arguments as text_open.
kitaqgb/lib/text.c:214
void text_window(u8 x, u8 y, u8 w, u8 h)
{
text_open(x, y, w, h);
}unit_attack_range
各セルとのマンハッタン距離がatk_min以上atk_max以下になる攻撃範囲を作ります。
書式
u8 unit_attack_range(unit_t* u, u8* out_mask);引数
u有効なunit_t。位置はセル単位、moveは歩数、atk_min/atk_maxは射程、hpとactedは行動可否に使います。
out_mask幅×高さバイトの書き込み先。セルごとに0または1を書きます。
戻り値
マップがあれば1、幅または高さが0なら0。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
障害物・射線・味方の位置は考慮しません。距離の計算結果が255以下になる範囲で使います。最小射程が最大射程より大きければ全セル0です。
使用例
result[46]=unit_attack_range(&hero,mask);result[47]=mask[12];result[48]=mask[14];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:352
Mark cells whose Manhattan distance falls within the inclusive attack range. This geometric mask does not test obstacles, line of sight or other units.
kitaqgb/lib/slg_unit.c:38
u8 unit_attack_range(unit_t* u, u8* out_mask)
{
u8 w = map_current_width();
u8 h = map_current_height();
u8 y;
if (w == 0 || h == 0) return 0;
__memset(out_mask, 0, (u16)((u16)w * (u16)h));
y = 0;
while (y < h) {
u8 x = 0;
while (x < w) {
u8 dist = __manhattan(u->x, u->y, x, y);
if (dist >= u->atk_min && dist <= u->atk_max) {
out_mask[__map_index(x, y, w)] = 1;
}
x++;
}
y++;
}
return 1;
}unit_can_act
HPが残り、まだ行動していないユニットかを調べます。
書式
u8 unit_can_act(const unit_t* u);引数
u有効なunit_t。位置はセル単位、moveは歩数、atk_min/atk_maxは射程、hpとactedは行動可否に使います。
戻り値
hp≠0かつacted=0なら1、それ以外は0。状態は変更しません。
使用例
result[11]=unit_can_act(&hero);hero.acted=1;result[12]=unit_can_act(&hero);hero.acted=0;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:355
A unit can act only while HP is nonzero and its acted flag is clear. The unit pointer must refer to a valid record.
kitaqgb/lib/slg_unit.c:5
u8 unit_can_act(const unit_t* u)
{
if (u->hp == 0) return 0;
if (u->acted != 0) return 0;
return 1;
}unit_move_range
ユニットの位置からmove歩以内に到達できるセルを移動マスクへ変換します。
書式
u8 unit_move_range(unit_t* u, u8* out_mask);引数
u有効なunit_t。位置はセル単位、moveは歩数、atk_min/atk_maxは射程、hpとactedは行動可否に使います。
out_mask幅×高さバイトの書き込み先。セルごとに0または1を書きます。
戻り値
マップがあれば1、幅または高さが0なら0。ユニットの原点は必ず範囲内にしてください。
使用条件と制限
座標はピクセルではなく現在のマップのセルです。通常マップは8×8ピクセル、メタタイルマップは16×16ピクセルで1セルです。
探索は共有の約4KiB作業領域を使い、再入できません。マップは1024セル以下、探索する距離は254以下にします。0xFFは未到達を表すため、255歩の経路は扱えません。出力配列は現在のマップの幅×高さバイトを用意します。
探索開始セルは壁でも到達済みになります。HP・acted・他のユニットは移動範囲に影響しません。
使用例
result[43]=unit_move_range(&hero,mask);result[44]=mask[12];result[45]=mask[14];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左に移動3歩以内のコスト、右にSからGまでの最短経路を表示します。赤い3セルの壁を避け、緑の*が9歩の経路を示します。青い数字は到達コストです。DMGは同じ配置を黒で表示します。射程マスクは壁を無視する点もRAMで検査します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none実装とソース
kitaqgb/lib/rpg.h:349
Build movement costs, then convert reachable cells to one and unreachable 0xFF cells to zero. The caller provides one output byte per current-map cell and a valid unit origin; path-search workspace limits also apply.
kitaqgb/lib/slg_unit.c:15
u8 unit_move_range(unit_t* u, u8* out_mask)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 i;
if (w == 0 || h == 0) return 0;
// A valid origin is required: the fill leaves storage unchanged on invalid origins, but this loop still converts it.
range_fill_move(u->x, u->y, u->move, out_mask);
total = (u16)((u16)w * (u16)h);
i = 0;
while (i < total) {
out_mask[i] = (u8)((out_mask[i] == 0xFF) ? 0 : 1);
i++;
}
return 1;
}weighted_choice
各項目に設定した1バイトの相対的な重みから、項目の添字を選びます。重みの合計でrng16の値の剰余を求め、その値を含む最初の累積区間を探します。重みが[1, 3, 6]なら、添字0・1・2の担当区間はそれぞれ0、1〜3、4〜9です。
書式
u8 weighted_choice(const u8* weights, u8 count);引数
weightscount個の読み取り可能なu8の重みを指すポインターです。配列は変更しません。合計が正なら重み0の項目は選ばれません。count == 0ではポインターを読み取らないためnullを指定できます。それ以外では有効な配列を渡してください。
count重みの個数で、0〜255を指定します。最大でも255の重みが255個なので合計は65025となり、関数内のu16の累積値に収まります。まず配列を走査して合計し、次に選択区間が見つかるまで走査します。
戻り値
合計が正なら、2回のバイト取得後に0〜count−1の添字を返します。個数0または全重み0なら、取得せずに0を返します。この代替値と、有効な選択結果である添字0は区別できません。区別が必要な処理では、空の配列や合計0を呼び出し側で確認してください。
使用条件と制限
重みが決めるのは相対的な区間の大きさであり、実測での百分率を保証しません。rng16の出力分布と、合計による剰余の両方が出現頻度に影響します。合計が正なら、重みが正の項目が一つだけでも必ず2回のバイト取得を消費します。
各呼び出しは持続する状態を共有します。再現には、同じ対象機種・シード・呼び出し順が必要で、ほかの補助関数による取得もその順番に含まれます。シード設定と取得は一つの実行箇所にまとめてください。今回の確認は、割り込み処理と並行して安全に使えることを保証するものではありません。
GBとFCは異なるアルゴリズムを使い、同じシードでも同じ列になることを保証しません。ゲーム処理向けの決定的な生成器であり、暗号用の乱数源ではありません。検証対象は実装の動作で、統計的な品質や周期ではありません。
ライブラリ関数を使う場合はrpg.hをインクルードし、ゲームのソースと一緒にlib/rng.cをコンパイルします。__rng_seedと__rng8自体はコンパイラが生成するため、この2つだけを使うプログラムにはrng.cは不要です。
使用例
weights[0]=1;weights[1]=3;weights[2]=6;
rng_seed(0x1234);
chosen=weighted_choice(weights,3); // 546 % 10 = 6 selects index 2.
empty=weighted_choice((const u8*)0,0); // No items: 0 is a fallback, not a chosen item.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3項目に重み1・3・6を設定するサンプルです。シードから得たワード546の剰余6は、添字2の区間4〜9に入るので、WEIGHT INDEXは00002になります。EMPTY LISTの00000は空の配列に対する代替値であり、項目0の選択に成功したという意味ではありません。
完全なプログラムでは、白地に10進数の結果を表示します。DMGでは黒文字、CGBでは青文字です。FAILEDは00000になる必要があります。画像はこのサンプルのエミュレータ実行結果です。
各条件で32個の出力と、その後の8回のバイト取得を独立したホスト側モデルと照合しました。GBは93条件、FCは18条件で、定数・実行時のシード指定や、GBの範囲・確率・重みの境界条件を含みます。サンプル画面はエミュレータで240フレーム後に確認しました。実機や統計的な品質は未検証です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/rpg.h:203
Select an index from cumulative byte weights using a 16-bit random draw. A zero total, including an empty list, returns zero; callers must distinguish that case from a valid index. Modulo reduction can introduce a small bias.
kitaqgb/lib/rng.c:61
u8 weighted_choice(const u8* weights, u8 count)
{
u8 i;
u16 total;
u16 pick;
u16 acc;
// At most 255 byte weights sum to 65025, so this accumulator fits u16.
// A nonempty list requires count readable weight bytes.
total = 0;
i = 0;
while (i < count) {
total = (u16)(total + (u16)weights[i]);
i++;
}
if (total == 0) return 0;
pick = (u16)(rng16() % total);
acc = 0;
i = 0;
while (i < count) {
acc = (u16)(acc + (u16)weights[i]);
if (pick < acc) return i;
i++;
}
return (u8)(count - 1);
}scene.h — シーンの登録、変更と更新
シーン表のenter・update・draw・exitを使い、タイトル画面やプレイ画面の処理を切り替えます。
scene_change
現在のシーンを終了させ、指定したシーンを開始します。
書式
void scene_change(u8 scene_id);引数
scene_id開始するシーンの添字。0~count−1です。
戻り値
戻り値はありません。
使用条件と制限
範囲外IDは無視します。有効な変更ではexit→ID更新・変更フラグ設定→enterの順です。同じIDの指定でもexitとenterを実行します。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
scene_change(0);scene_change(9);result[11]=events;result[12]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
0の開始、無効ID9の無視、0→1の遷移、1→1の再入場を実行し、イベント順を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:20
Ignore invalid IDs; otherwise exit the current scene and enter the requested one. Selecting the current ID still performs exit/enter. Callbacks run synchronously.
kitaqgb/lib/scene.c:27
void scene_change(u8 scene_id)
{
SceneFunc fn;
if (scene_id >= scene_count) return;
// Exit/enter handlers must not recursively change scenes or replace this shared table.
if (scene_has_current != 0) {
fn = scene_table[(__safe_index u8)scene_current].exit;
if (fn != 0) fn();
}
scene_current = scene_id;
scene_has_current = 1;
scene_changed = 1;
fn = scene_table[(__safe_index u8)scene_current].enter;
if (fn != 0) fn();
}scene_draw
アクティブなシーンのdraw処理を一回呼びます。
書式
void scene_draw();戻り値
戻り値はありません。
使用条件と制限
アクティブなシーンがない場合とdrawが0の場合は何もしません。変更フラグは消去しません。画面の転送やフレーム待機はコールバック側で設計します。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
scene_draw();result[15]=events;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0のdrawで12、シーン1のdrawで22がイベント列に入ることを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:25
Call the active scene's draw handler if one exists; no current scene is a no-op.
kitaqgb/lib/scene.c:60
void scene_draw()
{
SceneFunc fn;
if (scene_has_current == 0) return;
fn = scene_table[(__safe_index u8)scene_current].draw;
if (fn != 0) fn();
}scene_get_current
保存されている現在シーンのIDを読み出します。
書式
u8 scene_get_current();戻り値
保存されている8ビットIDです。初期化直後も0を返します。
使用条件と制限
0という戻り値だけではシーン0が開始済みか判別できません。初期化後に最初の scene_change() を行ったかを呼び出し側で管理してください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
result[16]=scene_get_current();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0の開始後は0、シーン1への変更後は1を取得します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:27
Return the stored scene ID; zero is also returned before the first transition.
kitaqgb/lib/scene.c:70
u8 scene_get_current()
{
return scene_current;
}scene_init
シーン表を登録し、まだどのシーンも開始していない状態にします。
書式
void scene_init(const SceneDef* scenes, u8 count);引数
scenesenter・update・draw・exitを持つ
SceneDef配列。count配列内のシーン数。0なら有効なシーンはありません。
戻り値
戻り値はありません。
使用条件と制限
現在IDと変更フラグを0にします。enterは呼ばないため、開始するには scene_change() が必要です。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
scene_init(scenes,2);scene_update();scene_draw();result[9]=events;result[10]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
二つのシーンを登録し、開始前にupdate/drawを呼んでもイベントが発生しないことを確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:15
Attach the caller-owned scene table and clear current-scene state without invoking callbacks.
kitaqgb/lib/scene.c:10
void scene_init(const SceneDef* scenes, u8 count)
{
scene_table = scenes;
scene_count = count;
scene_current = 0;
scene_changed = 0;
scene_has_current = 0;
}scene_set_table
使用するシーン表を置き換え、アクティブなシーンを解除します。
書式
void scene_set_table(const SceneDef* scenes, u8 count);引数
scenesenter・update・draw・exitを持つ
SceneDef配列。count配列内のシーン数。0なら有効なシーンはありません。
戻り値
戻り値はありません。
使用条件と制限
scene_init() と同様に状態を初期化します。置き換え前のexitも、新しい表のenterも呼びません。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
scene_set_table(scenes,2);result[23]=events;result[24]=scene_was_changed();scene_draw();result[25]=events;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
9件のイベントがある時点で表を置き換えます。件数は9のままで、変更フラグは0となり、続くdrawも何もしません。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:17
Replace the table by resetting scene state; this does not call the old scene's exit handler.
kitaqgb/lib/scene.c:20
void scene_set_table(const SceneDef* scenes, u8 count)
{
scene_init(scenes, count);
}scene_update
アクティブなシーンのupdate処理を一回呼びます。
書式
void scene_update();戻り値
戻り値はありません。
使用条件と制限
アクティブなシーンがなければ何もしません。ある場合は呼び出し前に変更フラグを消すため、update内で起きたシーン変更は呼び出し後にも残ります。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
scene_update();result[13]=events;result[14]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0のupdateでイベント11を記録します。直前に1だった変更フラグは通常のupdate後に0になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:23
Clear the change flag before calling the active scene's update handler, so a transition made during that handler is visible afterward.
kitaqgb/lib/scene.c:49
void scene_update()
{
SceneFunc fn;
if (scene_has_current == 0) return;
scene_changed = 0;
fn = scene_table[(__safe_index u8)scene_current].update;
if (fn != 0) fn();
}scene_was_changed
シーン変更が記録されているかを調べます。
書式
u8 scene_was_changed();戻り値
変更フラグが立っていれば1、消えていれば0です。
使用条件と制限
読み出しでは消しません。初期化・表の置換と、アクティブなシーンのupdate開始時に消えます。無効IDの指定は変更しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
result[17]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
変更直後の1、通常のupdate後の0、表を置き換えた後の0を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scene.h:29
Read the transition flag, which is cleared at the start of an active scene update.
kitaqgb/lib/scene.c:76
u8 scene_was_changed()
{
return scene_changed;
}scroll.h — 背景・ウィンドウ・分割スクロール
背景とウィンドウの即時・保留スクロール、および最大8件の走査線分割を扱います。取得関数の管理値、実際のレジスター値、保留値の違いに注意してください。
Scroll_BgAdd
管理中の背景座標に符号付きの移動量を加え、即時反映します。
書式
void Scroll_BgAdd(s8 dx, s8 dy);引数
dx横方向の符号付きピクセル差分、−128~127。
dy縦方向の符号付きピクセル差分、−128~127。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 計算結果は256で周回します。加算元は保留値や分割時の一時レジスター値ではありません。 この関数は __scroll_bg_add を呼び出すラッパーです。
使用例
Scroll_BgAdd(-20,3);result[22]=M_SCX;result[23]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:73
Apply signed deltas to the byte-sized background-scroll coordinates.
kitaqgb/lib/scroll.c:41
void Scroll_BgAdd(s8 dx, s8 dy) { __scroll_bg_add(dx, dy); }Scroll_Flush
保留中の背景・ウィンドウ座標を管理中の値とハードウェアへ反映し、保留フラグを消します。
書式
void Scroll_Flush();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保留のない座標ペアは変更しません。割り込み禁止やVBlank待ちは行わないため、呼び出し側で安全なタイミングを確保します。 この関数は __scroll_flush を呼び出すラッパーです。
使用例
Scroll_Flush();result[18]=M_SCX;result[19]=M_SCY;result[20]=M_WX;result[21]=M_WY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:70
Apply the buffered scroll/window state through the compiler runtime.
kitaqgb/lib/scroll.c:38
void Scroll_Flush() { __scroll_flush(); }Scroll_GetBgX
管理中の背景横スクロール値を取得します。
書式
u8 Scroll_GetBgX();戻り値
管理中の現在値を0~255の u8 で返します。WXには7ピクセルのオフセットを含みます。
使用条件と制限
ハードウェアレジスターの読み取りではありません。未反映のバッファー、直接レジスターへ書いた値、走査線分割中の一時値は取得しません。 この関数は __scroll_bg_x_get を呼び出すラッパーです。
使用例
result[6]=Scroll_GetBgX();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:50
Read the tracked current background X; this is not a direct SCX register read.
kitaqgb/lib/scroll.c:18
u8 Scroll_GetBgX() { return __scroll_bg_x_get(); }Scroll_GetBgY
管理中の背景縦スクロール値を取得します。
書式
u8 Scroll_GetBgY();戻り値
管理中の現在値を0~255の u8 で返します。WXには7ピクセルのオフセットを含みます。
使用条件と制限
ハードウェアレジスターの読み取りではありません。未反映のバッファー、直接レジスターへ書いた値、走査線分割中の一時値は取得しません。 この関数は __scroll_bg_y_get を呼び出すラッパーです。
使用例
result[7]=Scroll_GetBgY();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:52
Read the tracked current background Y; pending and raster-only values are excluded.
kitaqgb/lib/scroll.c:20
u8 Scroll_GetBgY() { return __scroll_bg_y_get(); }Scroll_GetWindowX
管理中のウィンドウWXを取得します。
書式
u8 Scroll_GetWindowX();戻り値
管理中の現在値を0~255の u8 で返します。WXには7ピクセルのオフセットを含みます。
使用条件と制限
ハードウェアレジスターの読み取りではありません。未反映のバッファー、直接レジスターへ書いた値、走査線分割中の一時値は取得しません。 この関数は __scroll_win_x_get を呼び出すラッパーです。
使用例
result[12]=Scroll_GetWindowX();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:61
Read tracked current WX, including its seven-pixel offset; not a live hardware read.
kitaqgb/lib/scroll.c:29
u8 Scroll_GetWindowX() { return __scroll_win_x_get(); }Scroll_GetWindowY
管理中のウィンドウWYを取得します。
書式
u8 Scroll_GetWindowY();戻り値
管理中の現在値を0~255の u8 で返します。WXには7ピクセルのオフセットを含みます。
使用条件と制限
ハードウェアレジスターの読み取りではありません。未反映のバッファー、直接レジスターへ書いた値、走査線分割中の一時値は取得しません。 この関数は __scroll_win_y_get を呼び出すラッパーです。
使用例
result[13]=Scroll_GetWindowY();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:63
Read tracked current WY; pending and raster-only values are excluded.
kitaqgb/lib/scroll.c:31
u8 Scroll_GetWindowY() { return __scroll_win_y_get(); }Scroll_Init
背景を(0,0)、ウィンドウをWX=7・WY=0へ初期化します。
書式
void Scroll_Init();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
現在値・保留値をそろえます。ウィンドウ表示状態や分割表の初期化は行いません。
使用例
Scroll_Init();result[0]=M_SCX;result[1]=M_SCY;result[2]=M_WX;result[3]=M_WY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:42
Reset background scroll to zero and place the window at screen origin using hardware WX=7 and WY=0.
kitaqgb/lib/scroll.c:5
void Scroll_Init()
{
__scroll_bg_set(0, 0);
__scroll_win_set(7, 0);
}Scroll_SetBg
背景の横・縦スクロールを即時設定します。
書式
void Scroll_SetBg(u8 scx, u8 scy);引数
scx背景の横スクロール。0~255ピクセルで、増やすと背景が左へ移動します。
scy背景の縦スクロール。0~255ピクセルで、増やすと背景が上へ移動します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
__scroll_bg_set のラッパーです。現在値・保留値・レジスターをそろえ、背景の保留フラグを消します。VBlank待ちは行いません。
使用例
Scroll_SetBg(8,16);result[28]=M_SCX;result[29]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:44
Write both hardware background-scroll bytes immediately.
kitaqgb/lib/scroll.c:12
void Scroll_SetBg(u8 scx, u8 scy) { __scroll_bg_set(scx, scy); }Scroll_SetBgBuffered
背景の横・縦スクロールを次回反映用としてまとめて保存します。
書式
void Scroll_SetBgBuffered(u8 scx, u8 scy);引数
scx背景の横スクロール。0~255ピクセルで、増やすと背景が左へ移動します。
scy背景の縦スクロール。0~255ピクセルで、増やすと背景が上へ移動します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
管理中の現在値とレジスターは変えません。__scroll_flush または Scroll_Flush が必要です。バッファーへの設定そのものはVBlankを待ちません。 この関数は __scroll_bg_set_buffered を呼び出すラッパーです。
使用例
Scroll_SetBgBuffered(16,24);result[14]=M_SCX;result[15]=Scroll_GetBgX();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:66
Stage background-scroll values in the intrinsic buffer for a later flush.
kitaqgb/lib/scroll.c:34
void Scroll_SetBgBuffered(u8 scx, u8 scy) { __scroll_bg_set_buffered(scx, scy); }Scroll_SetBgX
背景の横スクロールだけを即時設定します。
書式
void Scroll_SetBgX(u8 scx);引数
scx背景の横スクロール。0~255ピクセルで、増やすと背景が左へ移動します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 この関数は __scroll_bg_x_set を呼び出すラッパーです。
使用例
Scroll_SetBgX(250);result[4]=M_SCX;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:46
Write horizontal background scroll while leaving vertical scroll unchanged.
kitaqgb/lib/scroll.c:14
void Scroll_SetBgX(u8 scx) { __scroll_bg_x_set(scx); }Scroll_SetBgY
背景の縦スクロールだけを即時設定します。
書式
void Scroll_SetBgY(u8 scy);引数
scy背景の縦スクロール。0~255ピクセルで、増やすと背景が上へ移動します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 この関数は __scroll_bg_y_set を呼び出すラッパーです。
使用例
Scroll_SetBgY(2);result[5]=M_SCY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:48
Write vertical background scroll while leaving horizontal scroll unchanged.
kitaqgb/lib/scroll.c:16
void Scroll_SetBgY(u8 scy) { __scroll_bg_y_set(scy); }Scroll_SetWindow
ウィンドウのWX・WYを即時設定します。
書式
void Scroll_SetWindow(u8 wx, u8 wy);引数
wxハードウェアのWX値、0~255。画面X座標に7を加えます。画面左端なら7です。
wyウィンドウ上端の画面Y座標、0~255。可視画面の行は0~143です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 この関数は __scroll_win_set を呼び出すラッパーです。
使用例
Scroll_SetWindow(15,8);result[8]=M_WX;result[9]=M_WY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:55
Write raw hardware WX/WY values; callers add seven to a screen-space window X.
kitaqgb/lib/scroll.c:23
void Scroll_SetWindow(u8 wx, u8 wy) { __scroll_win_set(wx, wy); }Scroll_SetWindowBuffered
ウィンドウのWX・WYを次回反映用としてまとめて保存します。
書式
void Scroll_SetWindowBuffered(u8 wx, u8 wy);引数
wxハードウェアのWX値、0~255。画面X座標に7を加えます。画面左端なら7です。
wyウィンドウ上端の画面Y座標、0~255。可視画面の行は0~143です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
管理中の現在値とレジスターは変えません。__scroll_flush または Scroll_Flush が必要です。バッファーへの設定そのものはVBlankを待ちません。 この関数は __scroll_win_set_buffered を呼び出すラッパーです。
使用例
Scroll_SetWindowBuffered(39,32);result[16]=M_WX;result[17]=Scroll_GetWindowX();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:68
Stage raw window-coordinate values for a later flush.
kitaqgb/lib/scroll.c:36
void Scroll_SetWindowBuffered(u8 wx, u8 wy) { __scroll_win_set_buffered(wx, wy); }Scroll_SetWindowX
ウィンドウのWXだけを即時設定します。
書式
void Scroll_SetWindowX(u8 wx);引数
wxハードウェアのWX値、0~255。画面X座標に7を加えます。画面左端なら7です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 この関数は __scroll_win_x_set を呼び出すラッパーです。
使用例
Scroll_SetWindowX(23);result[10]=M_WX;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:57
Write raw hardware WX without converting a screen-space X coordinate.
kitaqgb/lib/scroll.c:25
void Scroll_SetWindowX(u8 wx) { __scroll_win_x_set(wx); }Scroll_SetWindowY
ウィンドウのWYだけを即時設定します。
書式
void Scroll_SetWindowY(u8 wy);引数
wyウィンドウ上端の画面Y座標、0~255。可視画面の行は0~143です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 この関数は __scroll_win_y_set を呼び出すラッパーです。
使用例
Scroll_SetWindowY(16);result[11]=M_WY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:59
Write the window Y coordinate through the intrinsic.
kitaqgb/lib/scroll.c:27
void Scroll_SetWindowY(u8 wy) { __scroll_win_y_set(wy); }Scroll_SplitCommit
現在のウィンドウ表示状態を保存し、分割表の最初の走査線イベントを予約します。
書式
void Scroll_SplitCommit();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
分割表は共有で最大8件です。追加は即時適用もソートも行わず、9件目以降を無視します。安全なフレーム間でreset→push→commitを行い、割り込みハンドラーが完了できる間隔を空けてください。予約行と実際の画素への反映開始は、割り込み遅延により一致するとは限りません。 空でなければIEのVBlank/STATビットを立ててEIを実行します。空の表ではLYC割り込みを停止しますが、IEやCPUの割り込み許可を元に戻しません。 この関数は __scroll_split_commit を呼び出すラッパーです。
使用例
Scroll_SplitCommit();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scrollラッパーで3段の背景スクロールを作ります。LY=64と104でXを8、16へ変え、24×16矩形を(32,16)・(24,80)・(16,112)に表示します。各段の横ずれを画像で確認できます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:92
Publish the pending split configuration through the compiler runtime.
kitaqgb/lib/scroll.c:66
void Scroll_SplitCommit() { __scroll_split_commit(); }Scroll_SplitPush
指定走査線で背景スクロールを変更するエントリーを分割表の末尾へ追加します。
書式
void Scroll_SplitPush(u8 ly, u8 scx, u8 scy);引数
ly予約する走査線番号。可視イベントは0~143、昇順で追加します。0の先頭エントリーはVBlank中に適用されます。
scx背景の横スクロール。0~255ピクセルで、増やすと背景が左へ移動します。
scy背景の縦スクロール。0~255ピクセルで、増やすと背景が上へ移動します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
分割表は共有で最大8件です。追加は即時適用もソートも行わず、9件目以降を無視します。安全なフレーム間でreset→push→commitを行い、割り込みハンドラーが完了できる間隔を空けてください。予約行と実際の画素への反映開始は、割り込み遅延により一致するとは限りません。 この関数は __scroll_split_push を呼び出すラッパーです。
使用例
Scroll_SplitPush(64,8,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scrollラッパーで3段の背景スクロールを作ります。LY=64と104でXを8、16へ変え、24×16矩形を(32,16)・(24,80)・(16,112)に表示します。各段の横ずれを画像で確認できます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:85
Append a background-scroll split directly. Use Raster_Push when caller-side scanline ordering and capacity validation are desired.
kitaqgb/lib/scroll.c:53
void Scroll_SplitPush(u8 ly, u8 scx, u8 scy) { __scroll_split_push(ly, scx, scy); }Scroll_SplitPushBgColor0
CGBの背景パレット0・色0を走査線イベントで変えるエントリーを追加します。
書式
void Scroll_SplitPushBgColor0(u8 ly, u16 rgb15);引数
ly予約する走査線番号。可視イベントは0~143、昇順で追加します。0の先頭エントリーはVBlank中に適用されます。
rgb15RGB555の15ビット色。赤がbit0~4、緑が5~9、青が10~14です。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
CGB専用です。Scroll_SplitPushEx 相当の表1件を使い、最大8件に数えます。パレット書き込みはアクセス可能になるまで待ちます。本サンプルの通常速度CGBでは、LY=64/112のイベントが行66/114から画面の色に反映されます。予約行そのものを描画開始行として扱わないでください。
使用例
Scroll_SplitPushBgColor0(0,0x7C00);Scroll_SplitPushBgColor0(64,0x001F);Scroll_SplitPushBgColor0(112,0x03E0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
CGBの背景色0を画面途中で切り替えます。本サンプルは通常速度で、上から行0~65が青、66~113が赤、114~143が緑です。イベント予約は0・64・112で、色書き込みと割り込みには遅延があります。DMGにはこのカラーパレット機能はありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_color.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_color.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:90
Reuse the two background-scroll fields to carry the low/high RGB15 bytes for a split whose operation flag selects background color zero.
kitaqgb/lib/scroll.c:61
void Scroll_SplitPushBgColor0(u8 ly, u16 rgb15)
{
__scroll_split_push_ex(ly, (u8)rgb15, (u8)(rgb15 >> 8), 0, 0, KQ_SCROLL_SPLIT_BG_COLOR0);
}Scroll_SplitPushEx
フラグで背景・ウィンドウ・表示状態・背景色0の操作を選ぶ分割エントリーを追加します。
書式
void Scroll_SplitPushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);引数
ly予約する走査線番号。可視イベントは0~143、昇順で追加します。0の先頭エントリーはVBlank中に適用されます。
scx背景の横スクロール。0~255ピクセルで、増やすと背景が左へ移動します。
scy背景の縦スクロール。0~255ピクセルで、増やすと背景が上へ移動します。
wxハードウェアのWX値、0~255。画面X座標に7を加えます。画面左端なら7です。
wyウィンドウ上端の画面Y座標、0~255。可視画面の行は0~143です。
flags操作ビット:BG=1、WIN座標=2、表示=4、非表示=8、CGB背景パレット0の色0=16。未知のビットは捨てます。色0指定時はscx/scyをRGB15の下位/上位バイトに使います。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
分割表は共有で最大8件です。追加は即時適用もソートも行わず、9件目以降を無視します。安全なフレーム間でreset→push→commitを行い、割り込みハンドラーが完了できる間隔を空けてください。予約行と実際の画素への反映開始は、割り込み遅延により一致するとは限りません。 表示・非表示の両ビットがあれば表示が優先します。色0はCGB専用で、背景座標との同時指定は同じscx/scyのバイトを両目的に使います。 この関数は __scroll_split_push_ex を呼び出すラッパーです。
使用例
Scroll_SplitPushEx(104,16,0,7,0,KQ_SCROLL_SPLIT_USE_BG|KQ_SCROLL_SPLIT_WIN_HIDE);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scrollラッパーで3段の背景スクロールを作ります。LY=64と104でXを8、16へ変え、24×16矩形を(32,16)・(24,80)・(16,112)に表示します。各段の横ずれを画像で確認できます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:87
Append an extended split with raw background/window fields and operation flags.
kitaqgb/lib/scroll.c:55
void Scroll_SplitPushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags)
{
__scroll_split_push_ex(ly, scx, scy, wx, wy, flags);
}Scroll_SplitReset
分割表の件数と進行位置を0にし、分割処理とSTATのLYC割り込みを無効にします。
書式
void Scroll_SplitReset();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
分割表は共有で最大8件です。追加は即時適用もソートも行わず、9件目以降を無視します。安全なフレーム間でreset→push→commitを行い、割り込みハンドラーが完了できる間隔を空けてください。予約行と実際の画素への反映開始は、割り込み遅延により一致するとは限りません。 表のバイト列、IE、CPUの割り込み許可状態は消去・解除しません。 この関数は __scroll_split_reset を呼び出すラッパーです。
使用例
Scroll_SplitReset();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scrollラッパーで3段の背景スクロールを作ります。LY=64と104でXを8、16へ変え、24×16矩形を(32,16)・(24,80)・(16,112)に表示します。各段の横ずれを画像で確認できます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:82
Clear the pending split table before building a new raster configuration.
kitaqgb/lib/scroll.c:50
void Scroll_SplitReset() { __scroll_split_reset(); }Scroll_WindowAdd
管理中のウィンドウ座標に符号付きの移動量を加え、即時反映します。
書式
void Scroll_WindowAdd(s8 dx, s8 dy);引数
dx横方向の符号付きピクセル差分、−128~127。
dy縦方向の符号付きピクセル差分、−128~127。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
待機は行いません。即時設定・加算は対象の座標ペアの保留値を現在値で置き換え、保留フラグを消します。軸単独の即時設定でも、同じペアのもう一方の保留値を取り消します。 計算結果は256で周回します。加算元は保留値や分割時の一時レジスター値ではありません。 この関数は __scroll_win_add を呼び出すラッパーです。
使用例
Scroll_WindowAdd(4,-40);result[24]=M_WX;result[25]=M_WY;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:75
Apply signed deltas to the byte-sized hardware window coordinates.
kitaqgb/lib/scroll.c:43
void Scroll_WindowAdd(s8 dx, s8 dy) { __scroll_win_add(dx, dy); }Scroll_WindowHide
LCDCのウィンドウ表示ビットと管理中の表示状態を無効にします。
書式
void Scroll_WindowHide();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標やマップ選択は変更しません。表示にはLCD・背景/ウィンドウ機能が有効で、ウィンドウ座標が可視範囲にある必要があります。 この関数は __scroll_win_hide を呼び出すラッパーです。
使用例
Scroll_WindowHide();result[27]=M_LCDC&32;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:79
Request that the window be hidden through the compiler intrinsic.
kitaqgb/lib/scroll.c:47
void Scroll_WindowHide() { __scroll_win_hide(); }Scroll_WindowShow
LCDCのウィンドウ表示ビットと管理中の表示状態を有効にします。
書式
void Scroll_WindowShow();戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
座標やマップ選択は変更しません。表示にはLCD・背景/ウィンドウ機能が有効で、ウィンドウ座標が可視範囲にある必要があります。 この関数は __scroll_win_show を呼び出すラッパーです。
使用例
Scroll_WindowShow();result[26]=M_LCDC&32;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
Scroll関数を使って、即時設定・保留・flush・加算・表示切り替えを学びます。背景Xはflushで250から16、ウィンドウWXは23から39へ変わります。表示ビットはshow後32、hide後0です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
カメラ(8,16)で赤い32×24矩形は画面(24,16)に表示されます。ウィンドウは画面(96,80)に64×64の青い枠、(112,96)に緑の16×16正方形を表示します。DMGではいずれも黒です。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/scroll.h:77
Request window visibility through the compiler intrinsic.
kitaqgb/lib/scroll.c:45
void Scroll_WindowShow() { __scroll_win_show(); }slg.h — 盤面、手の一覧とundo
バイト値の盤面、容量付きの手順リスト、取り消し記録のスタックです。手順や取り消し記録を盤面へ適用する操作は、ゲーム側で組み合わせます。
slg_board_clear
盤面の全マスを指定値で埋めます。
書式
void slg_board_clear(SLGBoard* board, u8 value);引数
board操作する
SLGBoard。初期化時は非NULLである必要があります。value保存する1バイト値。駒や地形などの意味はゲーム側で決めます。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
slg_board_clear(&board,0);result[1]=cells[0];result[2]=cells[11];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:33
Fill every cell with value; a null board is ignored. A valid board must own enough accessible cell storage for its declared dimensions.
kitaqgb/lib/slg_board.c:36
void slg_board_clear(SLGBoard* board, u8 value)
{
u16 total;
u16 i;
u8* p;
if (board == 0) return;
total = (u16)((u16)board->width * (u16)board->height);
i = 0;
p = board->cells;
while (i < total) {
*p = value;
p++;
i++;
}
}slg_board_get
指定マスの値を読みます。NULLの盤面または範囲外の座標は0として扱います。
書式
u8 slg_board_get(const SLGBoard* board, u8 x, u8 y);引数
board操作する
SLGBoard。初期化時は非NULLである必要があります。x左からのマス番号。0が左端です。リストへの記録時は盤面範囲を検証しません。
y上からのマス番号。0が上端です。リストへの記録時は盤面範囲を検証しません。
戻り値
有効なマスの1バイト値、または無効な参照なら0。値0だけで空マスと範囲外を区別できません。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
result[3]=slg_board_get(&board,1,1);result[4]=slg_board_get(&board,4,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:36
Return zero for a null board or out-of-range coordinate; otherwise read the cell. Zero therefore cannot distinguish an invalid lookup from an empty cell.
kitaqgb/lib/slg_board.c:54
u8 slg_board_get(const SLGBoard* board, u8 x, u8 y)
{
u8* p;
if (board == 0) return 0;
if (x >= board->width || y >= board->height) return 0;
p = slg_cell_ptr((SLGBoard*)board, x, y);
return *p;
}slg_board_init
盤面の幅・高さとマス配列を関連付けます。配列の既存内容は保持します。
書式
void slg_board_init(SLGBoard* board, u8 width, u8 height, u8* cells);引数
board操作する
SLGBoard。初期化時は非NULLである必要があります。width盤面の列数。1行のマス数です。
height盤面の行数。
cells行優先で並ぶ
width*heightバイトの書き込み可能なマス配列。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
cells[0]=9;slg_board_init(&board,4,3,cells);result[0]=slg_board_get(&board,0,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:30
Attach width * height bytes of caller-owned cell storage without clearing it.
kitaqgb/lib/slg_board.c:27
void slg_board_init(SLGBoard* board, u8 width, u8 height, u8* cells)
{
board->width = width;
board->height = height;
board->cells = cells;
}slg_board_set
有効な盤面内の1マスを書き換えます。NULLまたは範囲外なら何もしません。
書式
void slg_board_set(SLGBoard* board, u8 x, u8 y, u8 value);引数
board操作する
SLGBoard。初期化時は非NULLである必要があります。x左からのマス番号。0が左端です。リストへの記録時は盤面範囲を検証しません。
y上からのマス番号。0が上端です。リストへの記録時は盤面範囲を検証しません。
value保存する1バイト値。駒や地形などの意味はゲーム側で決めます。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
slg_board_set(&board,1,1,7);slg_board_set(&board,4,1,9);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:38
Write a cell only when the board and coordinate are valid.
kitaqgb/lib/slg_board.c:64
void slg_board_set(SLGBoard* board, u8 x, u8 y, u8 value)
{
u8* p;
if (board == 0) return;
if (x >= board->width || y >= board->height) return;
p = slg_cell_ptr(board, x, y);
*p = value;
}slg_move_list_clear
登録済み手順の件数を0にします。配列の内容は消しません。
書式
void slg_move_list_clear(SLGMoveList* list);引数
list操作する手順リスト。初期化時は非NULLである必要があります。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
slg_move_list_clear(&moves);result[10]=moves.count;result[11]=move_data[0].value;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:42
Forget the moves without erasing their storage; a null list is ignored.
kitaqgb/lib/slg_board.c:83
void slg_move_list_clear(SLGMoveList* list)
{
if (list == 0) return;
list->count = 0;
}slg_move_list_init
手順配列と最大件数を設定し、登録件数を0にします。
書式
void slg_move_list_init(SLGMoveList* list, SLGMove* items, u8 capacity);引数
list操作する手順リスト。初期化時は非NULLである必要があります。
items少なくとも
capacity個のSLGMoveを格納できる呼び出し元の配列。capacity最大記録数。0なら追加できません。1記録はx・y・valueの3バイトです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
slg_move_list_init(&moves,move_data,2);result[5]=moves.count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:40
Attach caller-owned move storage and reset the list length.
kitaqgb/lib/slg_board.c:75
void slg_move_list_init(SLGMoveList* list, SLGMove* items, u8 capacity)
{
list->items = items;
list->capacity = capacity;
list->count = 0;
}slg_move_list_push
座標と値を手順リストの末尾へ追加します。盤面の変更は行いません。
書式
u8 slg_move_list_push(SLGMoveList* list, u8 x, u8 y, u8 value);引数
list操作する手順リスト。初期化時は非NULLである必要があります。
x左からのマス番号。0が左端です。リストへの記録時は盤面範囲を検証しません。
y上からのマス番号。0が上端です。リストへの記録時は盤面範囲を検証しません。
value保存する1バイト値。駒や地形などの意味はゲーム側で決めます。
戻り値
追加できれば1、NULLまたは満杯なら0。失敗時は件数と既存記録を保持します。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
result[6]=slg_move_list_push(&moves,1,1,7);result[7]=slg_move_list_push(&moves,2,1,8);result[8]=slg_move_list_push(&moves,3,1,9);result[9]=moves.count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:45
Append coordinates and a value to the bounded move list. Return zero on a null/full list without replacing any existing move.
kitaqgb/lib/slg_board.c:91
u8 slg_move_list_push(SLGMoveList* list, u8 x, u8 y, u8 value)
{
SLGMove* move;
if (list == 0) return 0;
if (list->count >= list->capacity) return 0;
move = &list->items[(__safe_index u8)list->count];
move->x = x;
move->y = y;
move->value = value;
list->count++;
return 1;
}slg_undo_init
取り消し用の記録配列と容量を設定し、空のスタックにします。
書式
void slg_undo_init(SLGUndoStack* stack, SLGMove* items, u8 capacity);引数
stack操作する取り消しスタック。初期化時は非NULLである必要があります。
items少なくとも
capacity個のSLGMoveを格納できる呼び出し元の配列。capacity最大記録数。0なら追加できません。1記録はx・y・valueの3バイトです。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
slg_undo_init(&undo,undo_data,2);result[12]=undo.count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:47
Attach caller-owned undo records and initialize an empty stack.
kitaqgb/lib/slg_board.c:106
void slg_undo_init(SLGUndoStack* stack, SLGMove* items, u8 capacity)
{
stack->items = items;
stack->capacity = capacity;
stack->count = 0;
}slg_undo_pop
最後に保存した取り消し記録を取り出して削除します。盤面へ戻す処理は呼び出し側で行います。
書式
u8 slg_undo_pop(SLGUndoStack* stack, SLGMove* out_move);引数
stack操作する取り消しスタック。初期化時は非NULLである必要があります。
out_move取り出した記録の保存先。NULLなら記録を捨てるだけです。
戻り値
取り出せれば1、NULLまたは空なら0。空の場合、出力先の内容を変更しません。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
result[15]=slg_undo_pop(&undo,&old);result[16]=old.value;result[17]=slg_board_get(&board,1,1);slg_undo_pop(&undo,&old);slg_board_set(&board,old.x,old.y,old.value);result[18]=slg_board_get(&board,1,1);result[19]=slg_undo_pop(&undo,&old);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:53
Remove the newest undo record and optionally copy it out. A null output still discards the record; a null or empty stack returns zero.
kitaqgb/lib/slg_board.c:128
u8 slg_undo_pop(SLGUndoStack* stack, SLGMove* out_move)
{
if (stack == 0) return 0;
if (stack->count == 0) return 0;
stack->count--;
if (out_move != 0) {
out_move->x = stack->items[stack->count].x;
out_move->y = stack->items[stack->count].y;
out_move->value = stack->items[stack->count].value;
}
return 1;
}slg_undo_push
マスの変更前の値を取り消しスタックへ保存します。盤面そのものは変更しません。
書式
u8 slg_undo_push(SLGUndoStack* stack, u8 x, u8 y, u8 old_value);引数
stack操作する取り消しスタック。初期化時は非NULLである必要があります。
x左からのマス番号。0が左端です。リストへの記録時は盤面範囲を検証しません。
y上からのマス番号。0が上端です。リストへの記録時は盤面範囲を検証しません。
old_valueそのマスを変更する前の1バイト値。関数は盤面から自動取得しません。
戻り値
追加できれば1、NULLまたは満杯なら0。失敗時は件数と既存記録を保持します。
使用条件と制限
保存領域は呼び出し元が用意します。初期化はメモリを確保せず、内容を消しません。盤面操作の座標はマス単位です。
使用例
result[13]=slg_undo_push(&undo,1,1,slg_board_get(&board,1,1));slg_board_set(&board,1,1,8);slg_undo_push(&undo,1,1,8);result[14]=slg_undo_push(&undo,0,0,9);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
盤面、手順リスト、取り消し記録の役割を分けて学びます。盤面初期化は値9を保持し、容量2のリストの3件目は0で失敗します。popだけでは盤面は8のままで、取り出した記録を盤面へ適用して初めて7に戻ります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/slg.h:50
Record a cell's previous value for later undo. Return zero if the stack is null or full; this function does not change the board itself.
kitaqgb/lib/slg_board.c:115
u8 slg_undo_push(SLGUndoStack* stack, u8 x, u8 y, u8 old_value)
{
if (stack == 0) return 0;
if (stack->count >= stack->capacity) return 0;
stack->items[stack->count].x = x;
stack->items[stack->count].y = y;
stack->items[stack->count].value = old_value;
stack->count++;
return 1;
}sprite.h — OBJの確保、配置、メタスプライト、アニメーション
スプライトは通常8×8または8×16ピクセルの移動可能な表示物体で、OAM(Object Attribute Memory)の項目で指定します。スロットを確保し、作業用OAMへ位置・タイル・属性を設定してから転送します。複数スロットを1物体として扱うのがメタスプライトです。GBは40スロット・1走査線10個、FCは64スロット・1走査線8個が上限です。
GB属性はビット4がDMGのOBP1選択、5・6がX・Y反転、7が背景の色番号0以外より奥への配置です。CGBでは0〜2がOBJパレット、3がタイルデータのVRAMバンクで、DMG用パレット選択ビットはCGB本来の色選択には使いません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
anim_update
SpriteAnimの時間カウンターを進め、選択されたタイル番号を返します。ticks_per_frame回の呼び出しごとに次の連番タイルへ進み、frame_count枚で先頭へ戻ります。戻り値は呼び出し側でsprite_set_tileへ渡します。
書式
u8 anim_update(SpriteAnim* anim);引数
animfirst_tile、frame_count、frame、ticks、ticks_per_frameを持つ書き換え可能なSpriteAnimです。上限が0でない場合、frameはframe_count未満、ticksはticks_per_frame未満に初期化してください。
戻り値
選択した8ビットのタイル番号です。状態ポインターがヌルなら0を返します。OAMへの書き込みは行いません。
使用条件と制限
frame_countが0なら進めずにfirst_tileを返し、ticks_per_frameが0なら現在のコマを維持します。時間を進めるのは映像のフレーム数ではなく呼び出し回数です。タイル番号は256で折り返し、8×16モードでも1枚おきには進みません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_example_animation.first_tile=128;sprite_example_animation.frame_count=2;
sprite_example_animation.frame=0;sprite_example_animation.ticks=0;
sprite_example_animation.ticks_per_frame=2;
sprite_example_expect(anim_update(&sprite_example_animation)==128);
tile=anim_update(&sprite_example_animation);
sprite_example_expect(tile==129);sprite_set_tile(5,tile);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
先頭タイル128・2コマ・1コマあたり2回で、最初の更新は矢印の128、次は正方形の129を返します。ANIMATIONの横の正方形で、その戻り値をOAMへ反映したことを確認できます。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:86
Advance animation ticks/frame with wrapping frame selection and return the resulting tile. Zero ticks-per-frame freezes animation; null state returns zero.
kitaqgb/lib/sprite.c:211
u8 anim_update(SpriteAnim* anim)
{
if (anim == 0) return 0;
if (anim->frame_count == 0) return anim->first_tile;
if (anim->ticks_per_frame == 0) return (u8)(anim->first_tile + anim->frame);
anim->ticks++;
if (anim->ticks >= anim->ticks_per_frame) {
anim->ticks = 0;
anim->frame++;
if (anim->frame >= anim->frame_count) anim->frame = 0;
}
return (u8)(anim->first_tile + anim->frame);
}metasprite_draw
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
個数を指定したスプライト部品の配列を連続スロットへ配置します。各部品は符号付きの位置差・タイル・属性を持ちます。空きスロットは有効にし、使用中のスロットは上書きして、末尾の上限で停止します。
書式
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count);引数
first_id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
y画面上のY座標をピクセル単位で渡します。GB版は16を加えてOAMへ格納します。
partscount個以上のMetaSpritePartを読み出せる配列です。各4バイトの要素は符号付きdx・dy、タイル、属性の順で、終端記号は使いません。
count部品数を符号なし8ビット値で指定します。0なら書き込みません。連続スロットがSPRITE_MAXに達した場合は、そこまでの部品だけを書き込みます。
戻り値
書き込んだ部品数を0〜countで返します。スロット上限で打ち切られたかどうかは、この値で確認します。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
符号付き8ビットの位置差を基準座標に加え、結果を8ビットに折り返します。呼び出し中は部品配列全体を参照可能にしてください。使用中のスロットも上書きするため、別の物体のスロットは保護されません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_example_expect(metasprite_draw(10,88,SPRITE_EXAMPLE_Y(64),sprite_example_pair,2)==2);
sprite_example_expect(sprite_count_used()==9); // Sparse IDs count actual active slots.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:83
Place consecutive parts, counting each newly activated slot once. Existing active slots are overwritten without increasing the allocation count. Return the number written; an invalid first slot or zero count writes nothing.
kitaqgb/lib/sprite.c:191
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count)
{
u8 i = 0;
while (i < count) {
u8 id = (u8)(first_id + i);
if (id >= SPRITE_MAX) return i;
if (kq_sprite_active[id] == 0) {
kq_sprite_active[id] = 1;
kq_sprite_used = (u8)(kq_sprite_used + 1);
}
sprite_set_pos(id, (u8)(x + parts[i].dx), (u8)(y + parts[i].dy));
sprite_set_tile(id, parts[i].tile);
sprite_set_flags(id, parts[i].flags);
i++;
}
return count;
}sprite_alloc
空いている最初のスプライトスロットを確保し、位置を設定するまで非表示にします。非表示でも確保は維持されます。表示前にタイルと属性を設定してください。
書式
u8 sprite_alloc();戻り値
確保したスロットIDを返します。全スロットが使用中なら0xFFです。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
for(i=0;i<8;i++) sprite_example_expect(sprite_alloc()==i);
sprite_example_expect(sprite_count_used()==8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:53
Claim the first free slot, hide it, and return its ID; return 0xFF when full. Previously stored tile/flag bytes are retained until the caller replaces them.
kitaqgb/lib/sprite.c:60
u8 sprite_alloc()
{
u8 i = 0;
while (i < SPRITE_MAX) {
if (kq_sprite_active[i] == 0) {
kq_sprite_active[i] = 1;
kq_sprite_used++;
sprite_hide(i);
return i;
}
i++;
}
return 0xFF;
}sprite_count_used
非表示のスプライトやmetasprite_drawで有効にしたスロットも含め、確保済みの数を返します。画面に見えている数ではなく、割り当て済みの数です。
書式
u8 sprite_count_used();戻り値
0〜SPRITE_MAXの符号なし8ビット値です。
使用条件と制限
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_example_expect(sprite_count_used()==9); // After hiding, freeing and drawing the pair.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:73
Return the number of active slots, including slots activated by metasprite_draw. Hidden allocated slots remain active until sprite_free releases them.
kitaqgb/lib/sprite.c:148
u8 sprite_count_used()
{
return kq_sprite_used;
}sprite_flush_oam
VBlankを待ってから、sprite_flush_oam_nowで作業用OAMを転送します。
書式
void sprite_flush_oam();戻り値
ありません(void)。
使用条件と制限
GB版はHRAM上のDMAルーチンを実行し、境界に整列した作業用OAMから160バイトを転送します。転送中は割り込みを禁止し、終了後は必ず有効にします。呼び出し前の割り込み禁止状態は復元しません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
while(1) { sprite_flush_oam(); } // Transfer prepared sprites at the next frame boundary.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:70
Wait for VBlank, then transfer shadow OAM using the immediate DMA routine.
kitaqgb/lib/sprite.c:139
void sprite_flush_oam()
{
__wait_vblank();
sprite_flush_oam_now();
}sprite_flush_oam_now
作業用OAMを直ちにハードウェアへ転送します。安全な表示期間になるまで待つ処理は含まれないため、呼び出し側で実行時期を決めます。
書式
void sprite_flush_oam_now();戻り値
ありません(void)。
使用条件と制限
GB版はHRAM上のDMAルーチンを実行し、境界に整列した作業用OAMから160バイトを転送します。転送中は割り込みを禁止し、終了後は必ず有効にします。呼び出し前の割り込み禁止状態は復元しません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
// Rendering is disabled here; all shadow entries have been prepared.
sprite_flush_oam_now();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:68
Install the HRAM stub lazily and execute OAM DMA with interrupts disabled. This routine does not wait for VBlank and always enables interrupts afterward; it does not restore the caller's previous interrupt-enable state.
kitaqgb/lib/sprite.c:122
void sprite_flush_oam_now()
{
SpriteDmaRoutine dma;
if (kq_sprite_dma_ready == 0) {
sprite_install_dma_stub();
}
dma = (SpriteDmaRoutine)kq_sprite_dma_stub;
__asm {
DI
}
dma();
__asm {
EI
}
}sprite_free
確保済みのスロットを解放して非表示にし、使用数を1減らします。範囲外のIDや、すでに解放済みのスロットには何もしません。
書式
void sprite_free(u8 id);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_free(7);sprite_free(7);
sprite_example_expect(sprite_count_used()==7); // Repeated release is ignored.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:55
Release an allocated valid slot and hide it. Repeated frees do not decrement the count.
kitaqgb/lib/sprite.c:78
void sprite_free(u8 id)
{
if (sprite_valid(id) == 0) return;
if (kq_sprite_active[id] == 0) return;
kq_sprite_active[id] = 0;
if (kq_sprite_used != 0) kq_sprite_used--;
sprite_hide(id);
}sprite_hide
作業用の位置情報を変更してスプライトを隠します。確保状態・タイル・属性は維持されるため、表示範囲内の位置を設定すれば再確保せずに表示できます。
書式
void sprite_hide(u8 id);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_hide(6);
sprite_example_expect(sprite_count_used()==8); // Hiding retains allocation.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:64
Move a valid shadow slot off screen without freeing it.
kitaqgb/lib/sprite.c:112
void sprite_hide(u8 id)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].y = 0;
kq_sprite_oam[id].x = 0;
}sprite_init
スプライトの割り当て状態を初期化し、OAMの作業用データで全スロットを非表示にします。ソフトウェア側の高さは8ピクセルになります。実機のスプライトサイズ設定とOAM転送は別に行います。
書式
void sprite_init();戻り値
ありません(void)。
使用条件と制限
GB版の初期化では各スロットの4バイトをすべて消去し、HRAMのDMAルーチンの準備状態も初期化します。独自のDMA転送元に160バイト全体を確保しない限り、SPRITE_MAXは標準の40を使ってください。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_init();
sprite_example_expect(sprite_count_used()==0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後の使用数は0です。8回の確保はID 0〜7を返します。6番を隠しても8のまま、7番を2回解放すると7になり、10・11番に2部品を置くと9になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:50
Clear allocation and shadow OAM state and reset the DMA-stub installation flag. The default height is eight pixels; the hardware display mode is configured elsewhere.
kitaqgb/lib/sprite.c:22
void sprite_init()
{
u8 i = 0;
kq_sprite_used = 0;
kq_sprite_height = 8;
kq_sprite_dma_ready = 0;
while (i < SPRITE_MAX) {
kq_sprite_active[i] = 0;
kq_sprite_oam[i].y = 0;
kq_sprite_oam[i].x = 0;
kq_sprite_oam[i].tile = 0;
kq_sprite_oam[i].flags = 0;
i++;
}
}sprite_max_scanline_count
管理している縦位置とkq_sprite_heightから、表示範囲の1走査線に重なる有効スプライトの最大数を求めます。
書式
u8 sprite_max_scanline_count();戻り値
0〜SPRITE_MAXの符号なし8ビット値です。
使用条件と制限
kq_sprite_heightを実機の高さ8または16に合わせてください。この推定は透明ピクセル、横方向の表示範囲、ハードウェア優先順位を考慮しません。FC版は管理用座標を読むため、OAM組み込み関数で直接変更した位置は反映されません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_example_expect(sprite_max_scanline_count()==SPRITE_EXAMPLE_LINE_COUNT);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:79
Estimate the largest overlap of active, non-hidden shadow sprites across visible scanlines using the configured sprite height. This is a software overlap count, not a rendered-pixel or hardware-priority simulation.
kitaqgb/lib/sprite.c:158
u8 sprite_max_scanline_count()
{
u8 line = 0;
u8 best = 0;
while (line < 144) {
u8 i = 0;
u8 count = 0;
u8 ly_oam = (u8)(line + 16);
while (i < SPRITE_MAX) {
u8 sy = kq_sprite_oam[i].y;
if (kq_sprite_active[i] != 0 && sy != 0) {
if (ly_oam >= sy && ly_oam < (u8)(sy + kq_sprite_height)) {
count++;
}
}
i++;
}
if (count > best) best = count;
line++;
}
return best;
}sprite_set_flags
スプライトの属性バイト全体を置き換えます。対象機種のフラグ定数を組み合わせ、反転・パレット・背景との優先関係を指定します。指定しなかったビットは0になります。
書式
void sprite_set_flags(u8 id, u8 flags);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
flags対象機種のSPRITE_FLAG_*定数で作る属性バイト全体です。複数のフラグはビットOR(|)で組み合わせます。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
GB属性はビット4がDMGのOBP1選択、5・6がX・Y反転、7が背景の色番号0以外より奥への配置です。CGBでは0〜2がOBJパレット、3がタイルデータのVRAMバンクで、DMG用パレット選択ビットはCGB本来の色選択には使いません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_set_flags(1,SPRITE_FLAG_XFLIP);
sprite_set_flags(2,SPRITE_FLAG_YFLIP);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:62
Replace a valid shadow slot's complete hardware attribute byte.
kitaqgb/lib/sprite.c:105
void sprite_set_flags(u8 id, u8 flags)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].flags = flags;
}sprite_set_pos
画面上のピクセル座標でGBスプライトの作業用OAM位置を変更します。ハードウェア用の補正としてXに8、Yに16を加算します。
書式
void sprite_set_pos(u8 id, u8 x, u8 y);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
y画面上のY座標をピクセル単位で渡します。GB版は16を加えてOAMへ格納します。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
座標の加算は8ビットで行い、256で折り返します。画面範囲へのクリッピングは行いません。範囲内のスロットなら、未確保でも内容を変更できます。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_set_pos(0,88,SPRITE_EXAMPLE_Y(16)); // Visible top-left: (88,16).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:58
Convert screen coordinates to OAM coordinates by adding X=8 and Y=16. The byte additions wrap; this updates shadow storage even for an unallocated slot.
kitaqgb/lib/sprite.c:89
void sprite_set_pos(u8 id, u8 x, u8 y)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].x = (u8)(x + 8);
kq_sprite_oam[id].y = (u8)(y + 16);
}sprite_set_tile
作業用OAMの1スロットのタイル番号を置き換えます。タイルメモリにある絵を選択する処理であり、画像データの転送やスプライトの確保は行いません。
書式
void sprite_set_tile(u8 id, u8 tile);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
tile8ビットのタイル番号です。画像を先に読み込んでください。8×16スプライトのハードウェア規則はそのまま適用され、2枚組に合わせた番号補正は行いません。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_set_tile(5,129); // Select the green square beside ANIMATION.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
先頭タイル128・2コマ・1コマあたり2回で、最初の更新は矢印の128、次は正方形の129を返します。ANIMATIONの横の正方形で、その戻り値をOAMへ反映したことを確認できます。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:60
Set a valid shadow slot's tile byte; allocation and hardware transfer are separate.
kitaqgb/lib/sprite.c:98
void sprite_set_tile(u8 id, u8 tile)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].tile = tile;
}sprite_warn_scanline_overflow
sprite_max_scanline_countを、GBでは10個、FCでは8個という1走査線の上限と比較します。ソフトウェアの推定結果を返す処理であり、ハードウェアのオーバーフローフラグは読みません。
書式
u8 sprite_warn_scanline_overflow();戻り値
推定値が1走査線の上限を超えると1、それ以外は0です。
使用条件と制限
kq_sprite_heightを実機の高さ8または16に合わせてください。この推定は透明ピクセル、横方向の表示範囲、ハードウェア優先順位を考慮しません。FC版は管理用座標を読むため、OAM組み込み関数で直接変更した位置は反映されません。
例ではGBの完全版スプライトライブラリを使います。KQ_SPRITE_MINIMAL_RUNTIMEを定義するとsprite.cからsprite_free、sprite_set_pos、sprite_count_used、2つの走査線照会、metasprite_draw、anim_updateが除外されます。これらを使う場合は定義しないでください。
使用例
sprite_example_expect(sprite_warn_scanline_overflow()==1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
SCAN LIMITの下の列は(8,128)から始まります。同じ行にGBは11個、FCは9個を配置し、意図的に1個超過させています。見える矢印はGBが10本、FCが8本で、最後の1本は表示されないのが期待値です。ソフトウェアの最大数と警告のチェックは11/9と1になります。
スプライトの確保・位置・反転・複合物体・重なり・アニメーション・走査線上限を学ぶプログラムです。右上の2桁は、プログラム内の全チェック通過を表す00が期待値です。矢印と正方形はそれぞれ8×8のタイルです。
NORMALは(88,16)の右向き矢印、FLIP Xは(88,32)の左向き矢印、FLIP Yは(88,48)の上下反転した矢印です。METAは(88,64)と(96,64)に2本を配置します。OVERLAPでは(88,80)の矢印が(92,80)の正方形より前にあり、矢印の透明部分から正方形が見えます。ANIMATIONは(88,96)の正方形です。(120,16)と(120,32)は非表示・解放後なので何も表示しません。
CGB・FCの矢印は軸が緑、輪郭が赤、先端の細部が青で、正方形は緑です。透明部分にはGBでは白、FCでは黒の背景が見えます。DMGでは赤・緑・青の代わりに明るい灰色・暗い灰色・黒を使います。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite.h:75
Report whether the software overlap estimate exceeds ten sprites on a scanline.
kitaqgb/lib/sprite.c:182
u8 sprite_warn_scanline_overflow()
{
return (u8)(sprite_max_scanline_count() > 10);
}sprite_order.h — 優先度と交替によるスプライト選択
sprite_orderは、重要な物体を先に選びつつ、同じ優先度の物体のOAM順を交替させるライブラリです。最大255件から画面全体で最大40件を出力し、走査線あたり10個という制限への対処を助けます。既存のspriteはスロットの確保やDMA転送を担当し、sprite_orderは表示候補の選択順を担当します。
sprite_order.hを読み込み、sprite_order.cを1回だけコンパイルします。初期化後は、各フレームでbegin、必要な回数のpush、buildの順に呼びます。このライブラリだけならsprite.cやグローバル変数の定義は不要です。
ハードウェアは同じ走査線にかかる最初の10個をOAM順に選びます。低いpriority値のものを先に置くことで、混雑時にも残したい物体を優先します。ただし、同じ優先度の物体へ均等な表示頻度を保証する方式ではありません。優先物体だけで10個に達すれば、その行の下位の物体は表示されません。
SpriteOrderOamEntryはY、X、tile、flagsの4バイトです。sprite.hの影OAMを使う場合は(SpriteOrderOamEntry*)kq_sprite_oamへ出力できます。ただしSPRITE_MAXは標準の40とし、sprite_allocの割当状態と並び替えを混在させないでください。buildは割当管理情報を更新しません。
sprite_order_begin
次のフレームの表示候補を登録するため、登録数を0にします。配列、高さ、回転位相を保持するため、毎フレームinitを呼ぶ場合と違って表示順の交替が続きます。
書式
void sprite_order_begin(SpriteOrder* order);引数
order呼び出し側で確保した
SpriteOrderのアドレス。初回はinitで設定し、処理中は前景コードだけで操作してください。
戻り値
戻り値はありません。orderがNULLなら何もしません。古い配列のバイト列や出力済みOAMは消去しません。
使用条件と制限
sprite_order.hを読み込み、sprite_order.cを1回だけコンパイルします。初期化後は、各フレームでbegin、必要な回数のpush、buildの順に呼びます。このライブラリだけならsprite.cやグローバル変数の定義は不要です。
ハードウェアは同じ走査線にかかる最初の10個をOAM順に選びます。低いpriority値のものを先に置くことで、混雑時にも残したい物体を優先します。ただし、同じ優先度の物体へ均等な表示頻度を保証する方式ではありません。優先物体だけで10個に達すれば、その行の下位の物体は表示されません。
使用例
sprite_order_begin(&order_demo);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
このサンプルは、同じ走査線に14個を置いたときの表示選択を確認するものです。左のHIGHにある菱形2個が高優先度、右のNORMALの四角12個が通常優先度です。CGBでは菱形が赤、四角が青、DMGでは灰色と黒になります。菱形は常に2個とも表示され、四角は8個だけ表示されます。下の0~Bが四角の位置番号です。PHASE 04では4~B、PHASE 08では8~Bと0~3が見えます。通常ビルドは60回のフレーム待機ごとにPHASEを進めます。掲載画像は同じプログラムの位相を固定したもので、動作版でも表示する四角の変化を確認しています。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite_order.h:42
Discard this frame's submissions while preserving storage, height and phase. A null state is ignored. Call once before submitting the next frame's sprites.
kitaqgb/lib/sprite_order.c:24
void sprite_order_begin(SpriteOrder* order)
{
if (order != 0) order->count = 0;
}sprite_order_build
登録済みの候補から、優先度と回転位相に従って160バイトの影OAMを作ります。使わない枠を全て0にして隠し、次回の位相へ進めます。登録用配列の順序は変えません。
書式
u8 sprite_order_build(SpriteOrder* order, SpriteOrderOamEntry* output, u8 limit);引数
order呼び出し側で確保した
SpriteOrderのアドレス。初回はinitで設定し、処理中は前景コードだけで操作してください。outputSpriteOrderOamEntry40件分の書き込み可能な配列。limitが0や40未満でも160バイト全体へ書き込みます。limit出力する最大件数0~40。40より大きい値は40に制限します。走査線ごとの上限指定ではなく、OAM全体の件数です。
戻り値
出力した件数0~40を返します。正常な空の登録列では全枠を消去しphase=0にします。空でなければphaseをcountで折り返して順序の開始位置に使い、最後に1進めて再び折り返します。limit=0でも位相は進みます。NULLのorder/output、無効な状態、高さ不正、count>capacityでは0を返し、出力とphaseを変更しません。
使用条件と制限
ハードウェアは同じ走査線にかかる最初の10個をOAM順に選びます。低いpriority値のものを先に置くことで、混雑時にも残したい物体を優先します。ただし、同じ優先度の物体へ均等な表示頻度を保証する方式ではありません。優先物体だけで10個に達すれば、その行の下位の物体は表示されません。
ここでのpriorityは選択順です。背景より後ろに描く属性とは別です。DMGでは、選択された物体同士の重なりはX座標の小さいものが優先され、Xが同じときにOAM順で決まります。CGBの通常のカラー用重なり順はOAM順ですが、OPRIでDMG互換の順序を選ぶ設定には影響しません。このライブラリはハードウェアの優先規則を変更しません。
SpriteOrderOamEntryはY、X、tile、flagsの4バイトです。sprite.hの影OAMを使う場合は(SpriteOrderOamEntry*)kq_sprite_oamへ出力できます。ただしSPRITE_MAXは標準の40とし、sprite_allocの割当状態と並び替えを混在させないでください。buildは割当管理情報を更新しません。
buildはRAMへの書き込みだけです。出力配列は160バイト必要で、状態や登録用配列と重ねてはいけません。OAM DMAに使うバッファは256バイト境界に配置します。書き込みを完了してからVBlank中に転送し、途中の配列を割り込みから転送しないでください。サンプルではsprite_flush_oamを使います。
登録は1個ずつ一定量の処理で行い、出力時は最大4回、登録配列を走査して160バイトの影OAMを初期化します。配列自体の並び替えや一時ソート配列は不要です。255件を毎フレーム扱える速度を保証するものではありません。実際のゲームで必要な候補数に絞ってください。
使用例
order_demo_state[1]=sprite_order_build(&order_demo,(SpriteOrderOamEntry*)kq_sprite_oam,40);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
このサンプルは、同じ走査線に14個を置いたときの表示選択を確認するものです。左のHIGHにある菱形2個が高優先度、右のNORMALの四角12個が通常優先度です。CGBでは菱形が赤、四角が青、DMGでは灰色と黒になります。菱形は常に2個とも表示され、四角は8個だけ表示されます。下の0~Bが四角の位置番号です。PHASE 04では4~B、PHASE 08では8~Bと0~3が見えます。通常ビルドは60回のフレーム待機ごとにPHASEを進めます。掲載画像は同じプログラムの位相を固定したもので、動作版でも表示する四角の変化を確認しています。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite_order.h:62
Write a complete 160-byte shadow OAM: ascending priority, then cyclic order within each priority. Clamp limit to 40, return the number emitted and zero all unused entries (including Y) so they cannot consume scanline slots. Advance phase once for a nonempty valid queue, even when limit is zero. Empty queues reset phase to zero. Null/disabled/corrupt state or null output returns 0 without modifying output or phase. Output must provide 40 entries and must not overlap item/state storage. This only writes RAM. Build in foreground, then transfer during VBlank. DMG overlap priority also depends on X: this cannot override that hardware rule. OAM order chooses the first ten sprites per scanline on both DMG/CGB. It neither updates sprite_alloc's bookkeeping nor performs OAM DMA.
kitaqgb/lib/sprite_order.c:57
u8 sprite_order_build(SpriteOrder* order, SpriteOrderOamEntry* output, u8 limit)
{
u8 i;
u8 count;
u8 phase;
u8 priority;
u8 visited;
u8 written;
SpriteOrderItem* item;
if (order == 0 || output == 0) return 0;
if (order->items == 0 || order->capacity == 0) return 0;
if (order->count > order->capacity) return 0;
if (order->height != 8 && order->height != 16) return 0;
// Clear all hardware slots regardless of the requested emission limit.
for (i = 0; i < SPRITE_ORDER_OAM_COUNT; i++) {
output[i].y = 0;
output[i].x = 0;
output[i].tile = 0;
output[i].flags = 0;
}
count = order->count;
if (count == 0) {
order->phase = 0;
return 0;
}
if (limit > SPRITE_ORDER_OAM_COUNT) limit = SPRITE_ORDER_OAM_COUNT;
phase = order->phase;
while (phase >= count) phase = (u8)(phase - count);
written = 0;
for (priority = 0; priority < SPRITE_ORDER_LEVELS && written < limit; priority++) {
i = phase;
visited = 0;
while (visited < count && written < limit) {
item = &order->items[i];
if (item->priority == priority) {
output[written].y = item->oam.y;
output[written].x = item->oam.x;
output[written].tile = item->oam.tile;
output[written].flags = item->oam.flags;
written++;
}
visited++;
i++;
if (i >= count) i = 0;
}
}
phase++;
if (phase >= count) phase = 0;
order->phase = phase;
return written;
}sprite_order_init
表示順を管理する状態に登録用配列を関連付け、登録数と回転位相を0に戻します。新しいシーンや配列へ切り替えるときに使います。
書式
u8 sprite_order_init(SpriteOrder* order, SpriteOrderItem* items, u8 capacity, u8 height);引数
order呼び出し側で確保した
SpriteOrderのアドレス。初回はinitで設定し、処理中は前景コードだけで操作してください。items書き込み可能な
SpriteOrderItem配列。1件5バイトで、capacity件分を確保し、使用中は有効に保ってください。capacity配列の件数1~255。バイト数やハードウェアの表示枠数ではありません。
height高さは全物体で共通です。LCDCの8×8/8×16設定に合わせて8または16を指定してください。ライブラリはLCDCを設定しません。8×16モードでは、ハードウェアがタイル番号の最下位ビットを無視します。
戻り値
成功は1、itemsがNULL、capacityが0、heightが8/16以外、またはorderがNULLなら0です。orderが非NULLの失敗ではitems=NULL、capacity=count=phase=0、height=8の無効状態になります。items配列は変更しません。
使用条件と制限
sprite_order.hを読み込み、sprite_order.cを1回だけコンパイルします。初期化後は、各フレームでbegin、必要な回数のpush、buildの順に呼びます。このライブラリだけならsprite.cやグローバル変数の定義は不要です。
使用例
sprite_init();sprite_order_init(&order_demo,order_demo_items,14,8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
このサンプルは、同じ走査線に14個を置いたときの表示選択を確認するものです。左のHIGHにある菱形2個が高優先度、右のNORMALの四角12個が通常優先度です。CGBでは菱形が赤、四角が青、DMGでは灰色と黒になります。菱形は常に2個とも表示され、四角は8個だけ表示されます。下の0~Bが四角の位置番号です。PHASE 04では4~B、PHASE 08では8~Bと0~3が見えます。通常ビルドは60回のフレーム待機ごとにPHASEを進めます。掲載画像は同じプログラムの位相を固定したもので、動作版でも表示する四角の変化を確認しています。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite_order.h:38
Bind writable item storage and select the hardware sprite height (8 or 16). Return 1 on success. Invalid storage, zero capacity or another height returns 0 and leaves a non-null state disabled. A null state returns 0 without writes.
kitaqgb/lib/sprite_order.c:6
u8 sprite_order_init(SpriteOrder* order, SpriteOrderItem* items, u8 capacity, u8 height)
{
if (order == 0) return 0;
order->items = 0;
order->capacity = 0;
order->count = 0;
order->height = 8;
order->phase = 0;
if (items == 0 || capacity == 0) return 0;
if (height != 8 && height != 16) return 0;
order->items = items;
order->capacity = capacity;
order->height = height;
return 1;
}sprite_order_push
画面に少なくとも1ピクセル分かかるスプライトを1件登録します。画面外の物体は先に除外し、OAMの選択枠を無駄に使わないようにします。
書式
u8 sprite_order_push(SpriteOrder* order, s16 x, s16 y, u8 tile, u8 flags, u8 priority);引数
order呼び出し側で確保した
SpriteOrderのアドレス。初回はinitで設定し、処理中は前景コードだけで操作してください。x画面左上を原点とする符号付きX座標。左端が−7~159なら受け付けます。−8以下または160以上は全体が画面外です。保存時に8を足してOAM座標へ変換します。
y画面左上を原点とする符号付きY座標。高さ8なら−7~143、高さ16なら−15~143を受け付けます。保存時に16を足します。
tileタイル番号0~255。そのまま保存します。8×16モードの扱いはheightの説明を参照してください。
flagsハードウェア属性の1バイト。左右・上下反転、背景優先、DMG/CGBパレット、CGB VRAMバンクなどを指定し、そのまま保存します。
priorityソフトウェアの選択優先度0~3。0が最優先です。4以上は登録を拒否します。ハードウェア属性のflagsとは別の値です。
戻り値
登録できれば1を返しcountを1増やします。NULLまたは無効な状態、高さ不正、満杯、不正なpriority、完全な画面外なら0で、登録配列とcountは変わりません。
使用条件と制限
高さは全物体で共通です。LCDCの8×8/8×16設定に合わせて8または16を指定してください。ライブラリはLCDCを設定しません。8×16モードでは、ハードウェアがタイル番号の最下位ビットを無視します。
ハードウェアは同じ走査線にかかる最初の10個をOAM順に選びます。低いpriority値のものを先に置くことで、混雑時にも残したい物体を優先します。ただし、同じ優先度の物体へ均等な表示頻度を保証する方式ではありません。優先物体だけで10個に達すれば、その行の下位の物体は表示されません。
ここでのpriorityは選択順です。背景より後ろに描く属性とは別です。DMGでは、選択された物体同士の重なりはX座標の小さいものが優先され、Xが同じときにOAM順で決まります。CGBの通常のカラー用重なり順はOAM順ですが、OPRIでDMG互換の順序を選ぶ設定には影響しません。このライブラリはハードウェアの優先規則を変更しません。
使用例
sprite_order_push(&order_demo,8,64,128,0,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
このサンプルは、同じ走査線に14個を置いたときの表示選択を確認するものです。左のHIGHにある菱形2個が高優先度、右のNORMALの四角12個が通常優先度です。CGBでは菱形が赤、四角が青、DMGでは灰色と黒になります。菱形は常に2個とも表示され、四角は8個だけ表示されます。下の0~Bが四角の位置番号です。PHASE 04では4~B、PHASE 08では8~Bと0~3が見えます。通常ビルドは60回のフレーム待機ごとにPHASEを進めます。掲載画像は同じプログラムの位相を固定したもので、動作版でも表示する四角の変化を確認しています。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm実装とソース
kitaqgb/lib/sprite_order.h:49
Submit one sprite at signed, top-left SCREEN pixel coordinates. Preserve the tile and attribute bytes; use priority 0 (first) through 3 (last). Fully off-screen sprites, invalid priorities, disabled state and full storage return 0 without consuming a slot. Partly visible sprites are accepted. Return 1 on acceptance. Hardware 8x16 mode still ignores the tile index's low bit.
kitaqgb/lib/sprite_order.c:31
u8 sprite_order_push(SpriteOrder* order, s16 x, s16 y, u8 tile, u8 flags, u8 priority)
{
SpriteOrderItem* item;
if (order == 0) return 0;
if (order->items == 0 || order->capacity == 0) return 0;
if (order->height != 8 && order->height != 16) return 0;
if (order->count >= order->capacity || priority >= SPRITE_ORDER_LEVELS) return 0;
if (x <= -8 || x >= 160 || y >= 144) return 0;
if (order->height == 16) {
if (y <= -16) return 0;
} else {
if (y <= -8) return 0;
}
item = &order->items[order->count];
item->oam.y = (u8)(y + 16);
item->oam.x = (u8)(x + 8);
item->oam.tile = tile;
item->oam.flags = flags;
item->priority = priority;
order->count++;
return 1;
}system.h — 起動、フレーム数、待機と割り込み
フレーム待機回数の管理、待機後のコールバック、CPU割り込みの許可・禁止を扱います。
gb_disable_interrupts
gb_disable_interruptsはsystem_disable_interruptsを呼ぶ別名です。GBのCPU割り込みを無効にします。
CPUのマスク可能な割り込みを禁止します。
書式
void gb_disable_interrupts();戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
必要な共有データの更新を短く保ち、更新後は適切に割り込みを再開してください。フレームの待機条件とは区別して使用します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
gb_disable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルでは禁止と再開を対にして呼びます。CPUの割り込み状態は専用の検証プログラムでも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:32
#define gb_disable_interrupts system_disable_interruptskitaqgb/lib/system.c:52
void system_disable_interrupts()
{
__asm {
DI
RET
}
}gb_enable_interrupts
gb_enable_interruptsはsystem_enable_interruptsを呼ぶ別名です。GBのCPU割り込みを有効にします。
CPUのマスク可能な割り込みを許可します。
書式
void gb_enable_interrupts();戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
許可する前に割り込み元とハンドラーを適切に準備してください。FCではNMIの許可・禁止とは独立しています。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
gb_enable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
禁止した割り込みを再開します。FCのサンプルはAPUのフレームIRQとDMC IRQを止めてから許可し、未処理IRQの連続発生を避けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:31
#define gb_enable_interrupts system_enable_interruptskitaqgb/lib/system.c:43
void system_enable_interrupts()
{
__asm {
EI
RET
}
}gb_frame_count
gb_frame_countはsystem_get_frameを呼ぶ別名です。待機関数を通過した回数を16ビットのソフトウェアカウンターから読みます。
フレーム待機ラッパーの16ビットカウンターを読み出します。
書式
u16 gb_frame_count();戻り値
現在の0~65535のカウンター値です。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
result[3]=gb_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した後に3を読みます。サンプルでは折り返し確認のためカウンターを65535へ設定し、次の待機後に0になることも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:30
#define gb_frame_count system_get_framekitaqgb/lib/system.c:31
u16 system_get_frame()
{
return kq_system_frame;
}gb_init
gb_initはsystem_initを呼ぶ別名です。ソフトウェアのフレーム数とコールバック登録を初期化します。
フレーム管理を初期化し、カウンターと登録コールバックを消去します。
書式
void gb_init();戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
gb_init();result[0]=gb_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後のフレーム番号が0であることを確認してからコールバックを登録します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:27
#define gb_init system_initkitaqgb/lib/system.c:7
void system_init()
{
kq_system_frame = 0;
system_vblank_callback = 0;
}gb_set_vblank_callback
gb_set_vblank_callbackはsystem_set_vblank_callbackを呼ぶ別名です。フレーム待機後に同期的に呼ぶコールバックを登録・解除します。
フレーム待機後に呼ぶ処理を登録、置換、または解除します。
書式
void gb_set_vblank_callback(SystemCallback callback);引数
callback呼び出す関数。0なら登録を解除します。
戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
引数なし・戻り値なしの通常の関数ポインターです。バンク番号は保持しません。呼び出される間は対象コードが読み出せる状態にしてください。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
gb_set_vblank_callback(on_frame);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
登録した関数が3回呼ばれた後、0を設定して解除します。次の待機でフレームは4へ進みますが、呼び出し回数は3のままです。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:29
#define gb_set_vblank_callback system_set_vblank_callbackkitaqgb/lib/system.c:25
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}gb_wait_vblank
gb_wait_vblankはsystem_wait_vblankを呼ぶ別名です。フレーム待機を行い、ソフトウェアのフレーム数を進めて登録済みコールバックを呼びます。
次のフレーム待機を行い、カウンターを加算してから登録コールバックを呼びます。
書式
void gb_wait_vblank();戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
コールバックはこの関数内から同期的に呼ばれます。割り込みハンドラーとして登録されるわけではありません。カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
gb_wait_vblank();gb_wait_vblank();gb_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回呼び出すとコールバックも3回実行され、コールバックが読むカウンターは3になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:28
#define gb_wait_vblank system_wait_vblankkitaqgb/lib/system.c:15
void system_wait_vblank()
{
__wait_vblank();
kq_system_frame++;
if (system_vblank_callback != 0) {
system_vblank_callback();
}
}system_disable_interrupts
CPUのマスク可能な割り込みを禁止します。
書式
void system_disable_interrupts();戻り値
戻り値はありません。
使用条件と制限
必要な共有データの更新を短く保ち、更新後は適切に割り込みを再開してください。フレームの待機条件とは区別して使用します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
system_disable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルでは禁止と再開を対にして呼びます。CPUの割り込み状態は専用の検証プログラムでも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:25
Disable CPU interrupts and return; callers must explicitly re-enable them later.
kitaqgb/lib/system.c:52
void system_disable_interrupts()
{
__asm {
DI
RET
}
}system_enable_interrupts
CPUのマスク可能な割り込みを許可します。
書式
void system_enable_interrupts();戻り値
戻り値はありません。
使用条件と制限
許可する前に割り込み元とハンドラーを適切に準備してください。FCではNMIの許可・禁止とは独立しています。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
system_enable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
禁止した割り込みを再開します。FCのサンプルはAPUのフレームIRQとDMC IRQを止めてから許可し、未処理IRQの連続発生を避けます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:23
Enable CPU interrupts and return using the inline-assembly function body.
kitaqgb/lib/system.c:43
void system_enable_interrupts()
{
__asm {
EI
RET
}
}system_get_frame
フレーム待機ラッパーの16ビットカウンターを読み出します。
書式
u16 system_get_frame();戻り値
現在の0~65535のカウンター値です。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
result[3]=system_get_frame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した後に3を読みます。サンプルでは折り返し確認のためカウンターを65535へ設定し、次の待機後に0になることも確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:19
Read the software count of completed system_wait_vblank calls.
kitaqgb/lib/system.c:31
u16 system_get_frame()
{
return kq_system_frame;
}system_get_frame8
フレームカウンターの下位8ビットを取り出します。短い周期の処理に利用できます。
書式
u8 system_get_frame8();戻り値
0~255の下位バイトです。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
result[4]=system_get_frame8();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した時点では3、16ビットカウンターが折り返した時点では0を得ます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:21
Return the low byte of the software frame counter for wrapping 8-bit timing.
kitaqgb/lib/system.c:37
u8 system_get_frame8()
{
return (u8)kq_system_frame;
}system_init
フレーム管理を初期化し、カウンターと登録コールバックを消去します。
書式
void system_init();戻り値
戻り値はありません。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
system_init();result[0]=system_get_frame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後のフレーム番号が0であることを確認してからコールバックを登録します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:12
Reset the software frame counter and remove any registered callback.
kitaqgb/lib/system.c:7
void system_init()
{
kq_system_frame = 0;
system_vblank_callback = 0;
}system_set_vblank_callback
フレーム待機後に呼ぶ処理を登録、置換、または解除します。
書式
void system_set_vblank_callback(SystemCallback callback);引数
callback呼び出す関数。0なら登録を解除します。
戻り値
戻り値はありません。
使用条件と制限
引数なし・戻り値なしの通常の関数ポインターです。バンク番号は保持しません。呼び出される間は対象コードが読み出せる状態にしてください。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
system_set_vblank_callback(on_frame);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
登録した関数が3回呼ばれた後、0を設定して解除します。次の待機でフレームは4へ進みますが、呼び出し回数は3のままです。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:17
Replace the callback used by system_wait_vblank; null disables the callback.
kitaqgb/lib/system.c:25
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}system_wait_vblank
次のフレーム待機を行い、カウンターを加算してから登録コールバックを呼びます。
書式
void system_wait_vblank();戻り値
戻り値はありません。
使用条件と制限
コールバックはこの関数内から同期的に呼ばれます。割り込みハンドラーとして登録されるわけではありません。カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
GB版の待機はLCDが停止していると直ちに戻ります。その場合もラッパーのカウンター加算とコールバックは行います。初期化だけではLCDや割り込み元は設定しません。
この完成サンプルは標準のLegacy ABIでビルドします。コールバックを関数ポインター経由で呼ぶため、--abi=stackではコンパイラが間接呼び出し未対応のエラーを報告します。別名を使ってもこの条件は同じです。
使用例
system_wait_vblank();system_wait_vblank();system_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回呼び出すとコールバックも3回実行され、コールバックが読むカウンターは3になります。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k実装とソース
kitaqgb/lib/system.h:15
Wait for VBlank, count this wrapper call, then invoke the callback synchronously. The counter wraps at 16 bits and is not an independent hardware frame clock.
kitaqgb/lib/system.c:15
void system_wait_vblank()
{
__wait_vblank();
kq_system_frame++;
if (system_vblank_callback != 0) {
system_vblank_callback();
}
}vram.h — VRAM更新の予約と転送
後で実行するVRAM書き込みコマンドのキューを空にし、オーバーフローの記録を消します。画面の消去、タイル画像の転送、ハードウェアVRAMの確保は行いません。
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
__wait_vblankで待ってからvram_flush_nowを呼びます。少量の画面更新をまとめて実行するときに使います。1回の待機だけで転送量に上限が設けられるわけではなく、全処理が1回のVBlank内に終わる保証もありません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
vram_clear_queue
保留中のVRAMコマンドを実行せずに破棄し、あふれフラグも解除します。
書式
void vram_clear_queue();戻り値
ありません(void)。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
vram_clear_queue();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:41
Discard pending commands and clear the latched overflow flag without writing VRAM.
kitaqgb/lib/vram.c:41
void vram_clear_queue()
{
kq_vram_queue_used = 0;
kq_vram_queue_overflowed = 0;
}vram_flush
__wait_vblankで待ってからvram_flush_nowを呼びます。少量の画面更新をまとめて実行するときに使います。1回の待機だけで転送量に上限が設けられるわけではなく、全処理が1回のVBlank内に終わる保証もありません。
書式
void vram_flush();戻り値
ありません(void)。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
VRAM内の転送先バイトアドレス(0x8000~0x9FFF)です。バンクは実行時の選択状態に従います。連続したアドレスへ転送し、タイルマップの行末では折り返しません。転送先の全範囲を有効にしてください。LCD動作中の安全な書き込み処理は割り込みを有効にする場合があり、呼び出し前の割り込み禁止状態を保持しません。
使用例
vram_flush(); // One small write through the VBlank/NMI path.
if (vram_get_queue_used()!=0) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:64
Wait for VBlank before executing the queue. Waiting once does not itself limit the queued workload to the available VBlank duration.
kitaqgb/lib/vram.c:148
void vram_flush()
{
__wait_vblank();
vram_flush_now();
}vram_flush_now
登録された全コマンドを順番に実行し、使用数を0にします。この関数自身はVBlankを待ちません。LCDが動作中なら呼び出される安全なVRAM組み込み関数が書き込み可能な期間を待ちますが、大きなキューは複数の走査線やフレームにまたがることがあります。オーバーフローの記録は残ります。
書式
void vram_flush_now();戻り値
ありません(void)。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
VRAM内の転送先バイトアドレス(0x8000~0x9FFF)です。バンクは実行時の選択状態に従います。連続したアドレスへ転送し、タイルマップの行末では折り返しません。転送先の全範囲を有効にしてください。LCD動作中の安全な書き込み処理は割り込みを有効にする場合があり、呼び出し前の割り込み禁止状態を保持しません。
使用例
// Rendering is stopped here. On FC, commit readiness before direct execution.
vram_flush_now();
after_flush=vram_get_queue_used();
if (after_flush!=0) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:61
Execute pending commands in insertion order without waiting for VBlank here. The caller must arrange suitable VRAM access timing and a sufficient transfer budget. Only the used count is cleared; overflow stays latched until an explicit reset.
kitaqgb/lib/vram.c:125
void vram_flush_now()
{
u8 i = 0;
while (i < kq_vram_queue_used) {
VramCommand* cmd = &kq_vram_queue[(__safe_index u8)i];
if (cmd->kind == VRAM_CMD_BG_TILE) {
__settile_xy(cmd->x, cmd->y, cmd->value);
} else if (cmd->kind == VRAM_CMD_BG_RECT) {
__settile_rect(cmd->x, cmd->y, cmd->w, cmd->h, cmd->value);
} else if (cmd->kind == VRAM_CMD_BG_BLOCK) {
__settilemap_rect(cmd->dst, cmd->x, cmd->y, cmd->w, cmd->h, cmd->src);
} else if (cmd->kind == VRAM_CMD_MEMCPY) {
__vram_copy(cmd->dst, cmd->src, cmd->len);
} else if (cmd->kind == VRAM_CMD_MEMSET) {
__vram_fill(cmd->dst, cmd->value, cmd->len);
}
i++;
}
kq_vram_queue_used = 0;
}vram_get_overflowed
キューあふれフラグを消さずに読み取ります。過去の登録失敗が記録されたままの場合があります。
書式
u8 vram_get_overflowed();戻り値
オーバーフローの記録がなければ0です。登録を拒否すると1になり、明示的にリセットするまで残ります。キューを実行してもこの記録は消えません。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
overflow_seen=vram_get_overflowed();
if (overflow_seen!=1) failures++;
// Reading the latch again does not clear it.
if (vram_get_overflowed()!=1) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:73
Read the overflow latch without clearing it.
kitaqgb/lib/vram.c:175
u8 vram_get_overflowed()
{
return kq_vram_queue_overflowed;
}vram_get_queue_capacity
このAPIのローカル版での違いは、2026-10-03の差分と検証を参照してください。
現在の使用量にかかわらず、コマンドバッファーの総容量を返します。
書式
u8 vram_get_queue_capacity();戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
capacity=vram_get_queue_capacity();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:69
Return the total number of command slots, independent of queue usage. This is command-buffer capacity, not free space in hardware VRAM.
kitaqgb/lib/vram.c:162
u8 vram_get_queue_capacity()
{
return VRAM_QUEUE_MAX;
}vram_get_queue_free
コマンドバッファーの総容量から現在の使用量を引いた空き容量を返します。次の記録や一連の処理が全部収まるか、登録前に確認してください。この取得関数は領域を予約しません。
書式
u8 vram_get_queue_free();戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
available=vram_get_queue_free();
if (used+available!=capacity) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:71
Return the remaining command-slot capacity.
kitaqgb/lib/vram.c:169
u8 vram_get_queue_free()
{
return (u8)(VRAM_QUEUE_MAX - kq_vram_queue_used);
}vram_get_queue_used
実行待ちのコマンドが現在占めている、コマンドバッファーの使用量を返します。ビデオメモリーへ書き込むデータのバイト数ではありません。
書式
u8 vram_get_queue_used();戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
used=vram_get_queue_used();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:66
Return the number of queued commands, not the number of transfer bytes.
kitaqgb/lib/vram.c:155
u8 vram_get_queue_used()
{
return kq_vram_queue_used;
}vram_init
後で実行するVRAM書き込みコマンドのキューを空にし、オーバーフローの記録を消します。画面の消去、タイル画像の転送、ハードウェアVRAMの確保は行いません。
書式
void vram_init();戻り値
ありません(void)。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
vram_init();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:39
Start with an empty command queue and a clear overflow flag.
kitaqgb/lib/vram.c:34
void vram_init()
{
kq_vram_queue_used = 0;
kq_vram_queue_overflowed = 0;
}vram_queue_bg_block
srcにあるw × h個のタイル番号を、baseのマップへ矩形として書き込む処理を登録します。転送元は1行wバイトで隙間なく並べ、転送先の行間隔は32バイトです。コマンドは転送元ポインターを保存するため、実行前に元データを変更すると表示されるブロックにも反映されます。
書式
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);引数
baseタイルと矩形のコマンドは、実行時のLCDCのビット3で背景マップを選びます。CGBのタイル番号にはVRAMバンク0を使ってください。ブロック転送は明示した基底アドレスを使い、通常は
0x9800または0x9C00です。行間隔は32バイトで、座標のクリッピングは行いません。x, y8×8タイル単位の左上座標です。ピクセル単位ではなく、はみ出しの切り取りも行いません。対象の矩形は
x + w <= 32、GBではy + h <= 32、FCのネームテーブルのタイル領域ではy + h <= 30に収めてください。タイル1個の場合は幅・高さを1として考えます。w, hタイル単位の幅と高さを符号なし8ビット値で指定します。ブロックの元データは行順に
w * hバイト並べます。寸法0では画面への書き込みは発生しませんが、キューの消費量は各機種の記録方式に従います。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
タイルと矩形のコマンドは、実行時のLCDCのビット3で背景マップを選びます。CGBのタイル番号にはVRAMバンク0を使ってください。ブロック転送は明示した基底アドレスを使い、通常は0x9800または0x9C00です。行間隔は32バイトで、座標のクリッピングは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_bg_block(MAP_BASE,10,4,3,2,block);
block[0]=1; // The queued pointer sees this change when the queue executes.
if (check_result!=1) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:50
Queue a tile-map rectangle while retaining src rather than copying its bytes. Keep the source data and its ROM bank accessible until the queue is flushed.
kitaqgb/lib/vram.c:79
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_BLOCK);
if (cmd == 0) return 0;
cmd->dst = base;
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->src = src;
return 1;
}vram_queue_bg_rect
背景マップの矩形を、同じタイル番号で埋める処理を登録します。左上のタイル座標は(x, y)、大きさはw × hタイルです。単色のタイルなら塗りつぶした矩形になり、模様のあるタイルなら各マスで同じ模様を繰り返します。
書式
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);引数
x, y8×8タイル単位の左上座標です。ピクセル単位ではなく、はみ出しの切り取りも行いません。対象の矩形は
x + w <= 32、GBではy + h <= 32、FCのネームテーブルのタイル領域ではy + h <= 30に収めてください。タイル1個の場合は幅・高さを1として考えます。w, hタイル単位の幅と高さを符号なし8ビット値で指定します。ブロックの元データは行順に
w * hバイト並べます。寸法0では画面への書き込みは発生しませんが、キューの消費量は各機種の記録方式に従います。tile0〜255のタイルマップのバイト値です。対応する画像は別に読み込み、適切なパターンテーブル・アドレス方式を選んでください。タイル番号はポインターや色の値ではありません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
タイルと矩形のコマンドは、実行時のLCDCのビット3で背景マップを選びます。CGBのタイル番号にはVRAMバンク0を使ってください。ブロック転送は明示した基底アドレスを使い、通常は0x9800または0x9C00です。行間隔は32バイトで、座標のクリッピングは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_bg_rect(2,4,4,3,1); // A filled 32x24 rectangle at (16,32).
if (check_result!=1) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:47
Queue a rectangle filled with one tile value; coordinates and dimensions are in tiles.
kitaqgb/lib/vram.c:65
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_RECT);
if (cmd == 0) return 0;
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->value = tile;
return 1;
}vram_queue_bg_tile
背景の(x, y)へタイル番号を書き込むコマンドを1つ登録します。画面上のタイルが変わるのはキューの実行時です。マップの1要素を書き換える処理で、タイルの画素データの転送やパレット属性の変更は行いません。
書式
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);引数
x, y8×8タイル単位の左上座標です。ピクセル単位ではなく、はみ出しの切り取りも行いません。対象の矩形は
x + w <= 32、GBではy + h <= 32、FCのネームテーブルのタイル領域ではy + h <= 30に収めてください。タイル1個の場合は幅・高さを1として考えます。tile0〜255のタイルマップのバイト値です。対応する画像は別に読み込み、適切なパターンテーブル・アドレス方式を選んでください。タイル番号はポインターや色の値ではありません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
タイルと矩形のコマンドは、実行時のLCDCのビット3で背景マップを選びます。CGBのタイル番号にはVRAMバンク0を使ってください。ブロック転送は明示した基底アドレスを使い、通常は0x9800または0x9C00です。行間隔は32バイトで、座標のクリッピングは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_bg_tile(2,2,1); // A solid 8x8 tile at pixel (16,16).
if (check_result!=1) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:43
Queue a single background tile write; return zero if no command slot is available.
kitaqgb/lib/vram.c:48
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_TILE);
if (cmd == 0) return 0;
cmd->x = x;
cmd->y = y;
cmd->value = tile;
return 1;
}vram_queue_memcpy
srcからdstへ、連続したlenバイトをコピーする処理を登録します。転送元・転送先は1バイトずつ進み、タイルの行境界では飛びません。マップのバイトや書き込み可能なタイルパターン領域を転送できますが、形式変換や1タイル当たりのバイト数の計算は行いません。
書式
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);引数
dstVRAM内の転送先バイトアドレス(
0x8000~0x9FFF)です。バンクは実行時の選択状態に従います。連続したアドレスへ転送し、タイルマップの行末では折り返しません。転送先の全範囲を有効にしてください。LCD動作中の安全な書き込み処理は割り込みを有効にする場合があり、呼び出し前の割り込み禁止状態を保持しません。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_memcpy(MAP_BASE+8*32+2,strip,3);
if (check_result!=1) failures++; // Copy three map bytes, not three tile patterns.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:53
Queue a byte transfer and retain the source pointer. The source must remain valid and visible until flush; len is measured in bytes, not tiles.
kitaqgb/lib/vram.c:94
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len)
{
VramCommand* cmd = vram_push(VRAM_CMD_MEMCPY);
if (cmd == 0) return 0;
cmd->dst = dst;
cmd->src = (const u8*)src;
cmd->len = len;
return 1;
}vram_queue_memset
dstから連続するlenバイトを、1バイトのvalueで埋める処理を登録します。繰り返すのは1バイトで、複数バイトからなるタイルパターンではありません。マップの32バイトの行間隔も自動では挿入しません。
書式
u8 vram_queue_memset(u16 dst, u8 value, u16 len);引数
dstVRAM内の転送先バイトアドレス(
0x8000~0x9FFF)です。バンクは実行時の選択状態に従います。連続したアドレスへ転送し、タイルマップの行末では折り返しません。転送先の全範囲を有効にしてください。LCD動作中の安全な書き込み処理は割り込みを有効にする場合があり、呼び出し前の割り込み禁止状態を保持しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_memset(MAP_BASE+8*32+10,1,5);
if (check_result!=1) failures++; // Five solid tiles form a 40x8 horizontal bar.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:57
Queue a byte fill and return zero if the command queue is full.
kitaqgb/lib/vram.c:111
u8 vram_queue_memset(u16 dst, u8 value, u16 len)
{
VramCommand* cmd = vram_push(VRAM_CMD_MEMSET);
if (cmd == 0) return 0;
cmd->dst = dst;
cmd->value = value;
cmd->len = len;
return 1;
}vram_queue_tile
vram_queue_tileは同じ引数でvram_queue_bg_tileを直接呼び、同じ戻り値を返します。背景マップの1要素を書き込むための短い名前です。スプライトの操作やタイル画像の転送ではありません。
書式
u8 vram_queue_tile(u8 x, u8 y, u8 tile);引数
x, y8×8タイル単位の左上座標です。ピクセル単位ではなく、はみ出しの切り取りも行いません。対象の矩形は
x + w <= 32、GBではy + h <= 32、FCのネームテーブルのタイル領域ではy + h <= 30に収めてください。タイル1個の場合は幅・高さを1として考えます。tile0〜255のタイルマップのバイト値です。対応する画像は別に読み込み、適切なパターンテーブル・アドレス方式を選んでください。タイル番号はポインターや色の値ではありません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
タイルと矩形のコマンドは、実行時のLCDCのビット3で背景マップを選びます。CGBのタイル番号にはVRAMバンク0を使ってください。ブロック転送は明示した基底アドレスを使い、通常は0x9800または0x9C00です。行間隔は32バイトで、座標のクリッピングは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_tile(4,2,1); // The short alias draws at pixel (32,16).
if (check_result!=1) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:45
Provide the short alias for a queued background tile write.
kitaqgb/lib/vram.c:59
u8 vram_queue_tile(u8 x, u8 y, u8 tile)
{
return vram_queue_bg_tile(x, y, tile);
}vram_queue_tiles
vram_queue_tilesはvram_queue_memcpyを直接呼びます。lenはタイル数ではなく、バイト数のままです。保存する転送元ポインター、使用するキューの記録、戻り値、実行タイミングも同じです。
書式
u8 vram_queue_tiles(u16 dst, const void* src, u16 len);引数
dstVRAM内の転送先バイトアドレス(
0x8000~0x9FFF)です。バンクは実行時の選択状態に従います。連続したアドレスへ転送し、タイルマップの行末では折り返しません。転送先の全範囲を有効にしてください。LCD動作中の安全な書き込み処理は割り込みを有効にする場合があり、呼び出し前の割り込み禁止状態を保持しません。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
今回のコマンドを登録できたら1、空きスロットがなければ0です。失敗するとオーバーフローを記録し、すでに登録したコマンドは保ちます。以前にオーバーフローしていても、今回の登録に成功すれば1を返します。
使用条件と制限
GBの単位はコマンドのスロット数で、標準の総容量は32です。転送量が多い場合も長さ0の場合も、各登録関数は1スロットを使います。VRAM_QUEUE_MAXは1〜255の範囲で、すべてのコンパイル単位で同じ値にしてください。空のキューではused=0、free=capacityです。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
check_result=vram_queue_tiles(MAP_BASE+8*32+6,strip+1,2);
if (check_result!=1) failures++; // This alias also measures length in bytes.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成プログラムでは、書き込みの予約、キュー容量の管理、オーバーフローからの復帰を学べます。7回の描画登録で、GBは全32スロット中7スロット、FCは全128バイト中52バイトを使います。CAPACITY/USED/FREEはそれぞれ032/007/025、128/052/076になります。AFTER FLUSHとFAILED CHECKSは000、OVERFLOW SEENは001が期待値です。意図的に満杯のキューへ追加して通知を検証します。通知は読み出しや実行では消えず、キューのクリアで消えます。FCでは未コミットの直接実行でキューが残ることも確認します。
画像の座標は左上を原点とするピクセル単位です。(16,16)と(32,16)に8×8の塗りつぶした正方形、(16,32)に32×24の塗りつぶした矩形、(80,64)に40×8の横棒が表示されます。最後のVBlank経由の書き込みでは、(144,80)に8×8の正方形を追加します。取り消した書き込みの位置(144,24)は空白のままです。DMGは白地に黒、CGBは白地に赤・青・緑、FCは黒地に赤・青・緑の図形です。これらはエミュレータでの画像であり、実機での検証ではありません。
コピー処理を見分けるため、独自の8×8パターンを3種類使います。タイル1は全体の塗りつぶし、2は左4列だけの塗りつぶし、3は上端の1ピクセルから下端の8ピクセルへ広がる左寄せの三角形です。(80,32)の3×2タイルのブロックは[1,2,3] / [3,0,2]の配置になり、0は空白です。先頭の転送元バイトは登録後に変更するため、左上が塗りつぶしになればポインターを保持していることがわかります。(16,64)のコピーは[1,2,3]、(48,64)のtiles別名関数は[2,3]を表示します。大きさは順に24×16、24×8、16×8ピクセルです。
CGBでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形を赤で表示します。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。文字は白地に黒のままです。形に加えて、描画した各画素の色も検証します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm実装とソース
kitaqgb/lib/vram.h:55
Alias the queued byte-copy operation; this does not convert a tile count to bytes.
kitaqgb/lib/vram.c:105
u8 vram_queue_tiles(u16 dst, const void* src, u16 len)
{
return vram_queue_memcpy(dst, src, len);
}wire3d.h — DMG向けワイヤーフレーム
モノクロの3D線描画です。モデルの頂点・辺・面と物体の位置・回転・倍率を指定し、初期化→フレーム開始→描画→転送の順で使います。物理計算は別途行います。角度は16段階、モデル倍率は256が等倍です。
Vec3はx/y/z、Edgeは頂点番号a/b、Faceは三角形の頂点番号a/b/cを持ちます。Modelのvertices・edges・facesはそれらの配列、各countは要素数です。edge_facesは辺に隣接する面番号f0/f1で、FACE_NONEは面なしを表します。flagsのMODEL_HIDDEN_LINESは面を用いた辺選択を要求します。面の向きは投影後の符号付き面積で判定するため、頂点の並べ方が重要です。
Objectはmodelへの参照と、位置x/y/z・角度rx/ry/rz・倍率scale_q8・表示可否visibleを保持します。モデルの共有は可能ですが、表をコピーしたりROMバンクを自動で切り替えたりはしません。参照先を描画中に読み取れるように配置します。CGBのObjectには色番号colorもあります。
96行設定のModelにはedge_masksとedge_mask_countもあります。物体のY回転に対応する16ビットの辺マスクをRAMへ用意します。使わない場合は両方を0にします。
Wire3D_BeginFrame
次のフレームの描画を開始し、隠線判定の適用を解除します。
書式
void Wire3D_BeginFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
96行設定ではステージとマスクを消去します。120行設定ではピクセルを消去せず、転送時にステージを消費します。未転送の描画や背景キューを破棄する操作ではありません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:34
WIRE3D_DMG_HEIGHT = 96. Clear staged pixels and occlusion mask and disable occlusion filtering. This leaves the displayed VRAM frame and pending BG tile queue unchanged.
kitaqgb/lib/wire3d_dmg.c:2641
void Wire3DDMG_BeginFrame()
{
w3ddmg_clear_stage_asm();
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 0;
}Wire3D_DrawLine2D
画面座標の両端を含む、ピクセルが上下左右につながる線をステージへ描きます。
書式
void Wire3D_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
端点は表示領域内に指定します。斜線には接続用のピクセルを補います。120行設定でdirty転送を有効にすると外接するタイル範囲を記録します。VRAMへの反映は転送関数で行います。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:35
WIRE3D_DMG_HEIGHT = 96. Draw a connected line into the 128x96 stage, including endpoints. During DrawScene, use the coarse five-sample occlusion rejection; otherwise draw directly. Supply viewport coordinates. VRAM is updated only by EndFrame.
kitaqgb/lib/wire3d_dmg.c:2685
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by)
{
if (w3ddmg_occlusion_active)
{
w3ddmg_line_stage_masked_c(ax, ay, bx, by);
return;
}
w3ddmg_line_x0 = ax;
w3ddmg_line_y0 = ay;
w3ddmg_line_x1 = bx;
w3ddmg_line_y1 = by;
if (ax < 128 && bx < 128 && ay < WIRE3D_DMG_SCREEN_H && by < WIRE3D_DMG_SCREEN_H)
w3ddmg_line_inside_asm();
else
w3ddmg_line_stage_asm();
}Wire3D_DrawLine3D
世界座標の両端を投影して、1本の線を描きます。
書式
void Wire3D_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);引数
ax世界座標の始点のX成分。
ay世界座標の始点のY成分。
az世界座標の始点のZ成分。
bx世界座標の終点のX成分。
by世界座標の終点のY成分。
bz世界座標の終点のZ成分。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
両端が奥行き範囲内の場合だけ描きます。近面・遠面との交点を計算する処理はありません。投影後の端点は個別に画面端へ制限されます。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:36
WIRE3D_DMG_HEIGHT = 96. Project both world endpoints and draw only when both pass the depth range. Do not intersect a crossing segment with near/far planes; projected endpoints are independently clamped to the screen edges.
kitaqgb/lib/wire3d_dmg.c:2808
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz)
{
w3ddmg_u8 sx0;
w3ddmg_u8 sy0;
w3ddmg_u8 sx1;
w3ddmg_u8 sy1;
if (w3ddmg_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3ddmg_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DDMG_DrawLine2D(sx0, sy0, sx1, sy1);
}Wire3D_DrawModel
頂点・辺のモデルを、指定位置・回転で等倍描画します。
書式
void Wire3D_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256でDrawModelScaledを呼びます。面や隣接情報があるモデルの辺選択にも、その関数の条件が適用されます。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:37
WIRE3D_DMG_HEIGHT = 96. Draw an unscaled model by delegating to DrawModelScaled with scale 256.
kitaqgb/lib/wire3d_dmg.c:2825
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}Wire3D_DrawModelScaled
モデルを拡大縮小し、Y・X・Zの順に回転、平行移動、投影して辺を描きます。
書式
void Wire3D_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256が等倍、0以下も等倍です。頂点は最大24、面は最大16。96行設定は辺も最大16、120行設定はedge_count個を参照します。NULLのモデル・頂点・辺配列は無視します。奥行き範囲をまたぐ辺は切り詰めず省略します。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:38
WIRE3D_DMG_HEIGHT = 96. Scale, rotate in Y/X/Z order, translate and project at most 24 vertices; then draw up to 16 edges filtered by yaw masks and optional front-facing faces. Nonpositive scale becomes unity (256). Null model/vertex/edge pointers are ignored; referenced tables must remain readable. Keep scale products and translations in signed 16-bit range. Depth-crossing edges are dropped, not clipped.
kitaqgb/lib/wire3d_dmg.c:2965
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
if (edge_count > WIRE3D_DMG_MODEL_EDGE_LIMIT) edge_count = WIRE3D_DMG_MODEL_EDGE_LIMIT;
w3ddmg_build_edge_flags(model, edge_count, face_count, rx, ry, rz);
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_edge_flags[(__safe_index w3ddmg_u8)i])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3D_DrawScene
最大8個の物体をカメラに近い順に描き、手前の面で後方の辺を隠します。
書式
void Wire3D_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);引数
objects位置・回転・倍率・visibleを持つ物体配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
物体中心の奥行きで順序を決め、面の外接矩形と線上の5点で隠れ方を近似します。ピクセル単位の深度バッファではありません。visibleが有効なモデルを描き、配列自体は並べ替えません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:39
WIRE3D_DMG_HEIGHT = 96. Process at most the first eight objects, sorted near to far by transformed origin depth. Draw visible valid models, then mark their front-face bounding rectangles to reject fully sampled later lines. This approximates inter-object occlusion, not a depth buffer. It clears its mask but retains existing staged pixels. Null arrays are ignored; objects and their data must remain readable.
kitaqgb/lib/wire3d_dmg.c:2723
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count)
{
w3ddmg_u8 scene_count;
w3ddmg_u8 i;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3D_DMG_SCENE_OBJECT_LIMIT) scene_count = WIRE3D_DMG_SCENE_OBJECT_LIMIT;
i = 0;
while (i < scene_count)
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = i;
w3ddmg_scene_depth[(__safe_index w3ddmg_u8)i] = w3ddmg_scene_object_depth(&objects[(__safe_index w3ddmg_u8)i]);
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3ddmg_u8 j;
j = (w3ddmg_u8)(i + 1);
while (j < scene_count)
{
w3ddmg_u8 oi;
w3ddmg_u8 oj;
oi = w3ddmg_scene_order[(__safe_index w3ddmg_u8)i];
oj = w3ddmg_scene_order[(__safe_index w3ddmg_u8)j];
// Put the nearer origin first so its face bounds can suppress later lines.
// The array itself stays in caller order; only the scratch index list is sorted.
if (w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oj] < w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oi])
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = oj;
w3ddmg_scene_order[(__safe_index w3ddmg_u8)j] = oi;
}
j = (w3ddmg_u8)(j + 1);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 1;
i = 0;
while (i < scene_count)
{
Wire3DDMG_Object* obj;
obj = &objects[(__safe_index w3ddmg_u8)w3ddmg_scene_order[(__safe_index w3ddmg_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
Wire3DDMG_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, (w3ddmg_i8)obj->rx, (w3ddmg_i8)obj->ry, (w3ddmg_i8)obj->rz, obj->scale_q8);
w3ddmg_mark_model_occluder(obj->model);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_occlusion_active = 0;
}Wire3D_EndFrame
背景予約とステージの描画をVRAMへ反映します。
書式
void Wire3D_EndFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
次のVBlankを待って背景予約を反映します。120行設定では有効な補助転送を先に行い、続いてメインを全体またはdirty転送します。ステージは転送で消費します。STAT待ちにより複数の表示期間にまたがる場合があります。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:42
WIRE3D_DMG_HEIGHT = 96. Wait for the next VBlank, flush pending BG map writes, then copy the stage to VRAM while polling access windows. Copying consumes the staged pixels. The transfer may extend beyond VBlank; this is not an atomic frame swap.
kitaqgb/lib/wire3d_dmg.c:3081
void Wire3DDMG_EndFrame()
{
w3ddmg_wait_vblank_start();
w3ddmg_flush_bg_queue();
w3ddmg_transfer_stage_asm();
}Wire3D_EraseModelFaces
モデルの手前向きの三角形面を投影し、ステージ上の線を面の内側から消します。
書式
void Wire3D_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
隠線フラグと面データが必要です。頂点24・面16まで処理し、投影できない面は省略します。倍率0以下は256です。投影用の共有作業領域を書き換えますが、画面転送は行いません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3D_Face faces[2]={{0,2,1},{0,3,2}};
Wire3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;plane.edge_masks=0;plane.edge_mask_count=0;
Wire3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3D_DrawLine2D(8,i,32,i);
for(i=24;i<=72;i++) Wire3D_DrawLine2D(40,i,88,i);
Wire3D_EraseTriangle2D(12,12,28,12,12,28);
Wire3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_faces.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:43
WIRE3D_DMG_HEIGHT = 96. Project up to 24 vertices and erase front-facing triangle interiors from the stage for a model with hidden-line flags and face data. Nonpositive scale means 256 (unity). Up to 16 faces are considered; depth-crossing faces are skipped. This also replaces shared projection caches, but does not update VRAM.
kitaqgb/lib/wire3d_dmg.c:2841
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3D_EraseSpan2D
指定した1行の両端を含む横区間を消去します。
書式
void Wire3D_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);引数
y消去する行のY座標(ピクセル)。
x0最小側のX端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
Xの左右は自動で並べ替えますが、両端を0~127に収めてください。範囲外のYは無視します。Xのクリッピングは行いません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:44
WIRE3D_DMG_HEIGHT = 96. Order the X endpoints and erase the inclusive staged span. Both endpoints must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
kitaqgb/lib/wire3d_dmg.c:2943
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1)
{
w3ddmg_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
w3ddmg_stage_clear_span(y, x0, x1);
}Wire3D_EraseTriangle2D
画面座標で指定した三角形の内部を、行ごとの塗りつぶしで消去します。
書式
void Wire3D_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
cx頂点CのX座標(表示領域内のピクセル)。
cy頂点CのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
表示領域内の頂点を指定します。面積0の三角形は無視します。マスクや表示済みVRAMは変更しません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3D_Face faces[2]={{0,2,1},{0,3,2}};
Wire3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;plane.edge_masks=0;plane.edge_mask_count=0;
Wire3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3D_DrawLine2D(8,i,32,i);
for(i=24;i<=72;i++) Wire3D_DrawLine2D(40,i,88,i);
Wire3D_EraseTriangle2D(12,12,28,12,12,28);
Wire3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3D_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_faces.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:45
WIRE3D_DMG_HEIGHT = 96. Erase a triangle from staged pixels using integer scanline intersections. Supply viewport coordinates. Degenerate triangles are ignored; the occlusion mask and VRAM are unchanged.
kitaqgb/lib/wire3d_dmg.c:2933
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy)
{
w3ddmg_clear_triangle(ax, ay, bx, by, cx, cy);
}Wire3D_Init
LCD・タイル・背景マップを初期化し、モノクロのワイヤーフレーム表示領域を用意します。
書式
void Wire3D_Init();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
カメラ、背景書き込みキュー、ステージをリセットし、BGPを0xB4に設定します。描画前にBeginFrameを呼びます。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:47
WIRE3D_DMG_HEIGHT = 96. Take over the LCD: wait for VBlank, switch it off, clear 6144 tile bytes and the BG map, install HUD tiles and the 128x96 signed-tile viewport, then enable BG display with scroll zero and BGP=0xB4. Reset camera, BG queue and stage. Reserve 0xD000..0xDFFF for staging and retain its WRAM bank mapping. Call BeginFrame before drawing to initialize the occlusion state as well.
kitaqgb/lib/wire3d_dmg.c:2572
void Wire3DDMG_Init()
{
w3ddmg_u8 row;
w3ddmg_u8 col;
w3ddmg_u8 tid;
w3ddmg_u16 off;
w3ddmg_wait_vblank_start();
w3ddmg_reg_lcdc = 0;
w3ddmg_clear_vram_asm();
w3ddmg_load_hud_tiles();
w3ddmg_fill_bg_map_asm();
row = 0;
tid = 0x90;
while (row < WIRE3D_DMG_TILE_H)
{
off = (w3ddmg_u16)((w3ddmg_u16)row << 5);
col = 0;
while (col < WIRE3D_DMG_TILE_W)
{
w3ddmg_bg_map_9800[(w3ddmg_u16)(off + (w3ddmg_u16)col)] = tid;
tid = (w3ddmg_u8)(tid + WIRE3D_DMG_TILE_H);
col = (w3ddmg_u8)(col + 1);
}
row = (w3ddmg_u8)(row + 1);
tid = (w3ddmg_u8)(0x90 + row);
}
w3ddmg_reg_scx = 0;
w3ddmg_reg_scy = 0;
w3ddmg_reg_bgp = 0xB4;
w3ddmg_reg_lcdc = 0x81;
w3ddmg_bgq_count = 0;
Wire3DDMG_SetCamera(0, 0, 0, 0, 0, 0);
w3ddmg_clear_stage_asm();
}Wire3D_ProjectPoint
世界座標の点をカメラから見た画面座標へ変換します。
書式
w3ddmg_u8 Wire3D_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);引数
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
sx投影結果のX座標を書き込む、有効なu8領域へのポインタ。
sy投影結果のY座標を書き込む、有効なu8領域へのポインタ。
戻り値
投影成功時は1、深度で拒否した場合は0です。
使用条件と制限
奥行き8~255だけを受理します。範囲外なら0を返し、出力値は保持します。成功時は画面端に制限した座標を書き込みます。座標制限は線の幾何学的クリッピングではありません。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:50
WIRE3D_DMG_HEIGHT = 96. Project a world point with the current camera. Return zero for rejected depth, leaving outputs unchanged; success writes clamped 128x96 coordinates. Output pointers must be valid. Screen clamping is not geometric clipping.
kitaqgb/lib/wire3d_dmg.c:549
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy)
{
return w3ddmg_project_world(x, y, z, sx, sy);
}Wire3D_PutBgTile
背景マップの1セルへのタイル番号書き込みを予約します。
書式
void Wire3D_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);引数
x32×32背景マップ内のタイル単位のX座標。
y32×32背景マップ内のタイル単位のY座標。
tile背景セルに置く8ビットのタイル番号。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
座標は32×32マップのタイル単位です。48件まで予約でき、範囲外と超過分は無視します。EndFrameで反映する専用キューであり、一般のvramライブラリのキューとは別です。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:51
WIRE3D_DMG_HEIGHT = 96. Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map coordinates and submissions beyond 48 pending writes are silently ignored. This queue is separate from the general vram library queue.
kitaqgb/lib/wire3d_dmg.c:582
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3ddmg_bgq_count >= WIRE3D_DMG_BG_QUEUE_LIMIT) return;
w3ddmg_bgq_x[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = x;
w3ddmg_bgq_y[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = y;
w3ddmg_bgq_tile[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = tile;
w3ddmg_bgq_count = (w3ddmg_u8)(w3ddmg_bgq_count + 1);
}Wire3D_SelectEdgeMask
物体のY回転に対応する、事前計算済みの辺表示マスクを選びます。
書式
w3ddmg_u16 Wire3D_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
表示する辺に対応する16ビットマスク。表がなければ0xFFFFです。
使用条件と制限
96行設定で使います。Y回転の下位4ビットを16・8・4・2区分へ割り当て、その他の正の個数では先頭を使います。X/Z回転とカメラは無視します。表はRAMに置き、未指定なら0xFFFFを返します。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:52
WIRE3D_DMG_HEIGHT = 96. Select a precomputed mask from the low four bits of object yaw; pitch, roll and camera orientation are ignored. Use 16, 8, 4 or 2 bins; other positive counts select entry zero (counts >=16 use the first 16). Missing data returns 0xFFFF. Keep the mask table readable through the current data-pointer mapping.
kitaqgb/lib/wire3d_dmg.c:1109
w3ddmg_u16 Wire3DDMG_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
w3ddmg_u8 count;
w3ddmg_u8 bin;
if (model == 0) return 0xFFFF;
if (model->edge_masks == 0) return 0xFFFF;
count = model->edge_mask_count;
if (count == 0) return 0xFFFF;
(void)rx;
(void)rz;
bin = (w3ddmg_u8)(((w3ddmg_u8)ry) & 15);
if (count >= 16) return model->edge_masks[(__safe_index w3ddmg_u8)bin];
if (count == 8) bin = (w3ddmg_u8)(bin >> 1);
else if (count == 4) bin = (w3ddmg_u8)(bin >> 2);
else if (count == 2) bin = (w3ddmg_u8)(bin >> 3);
else bin = 0;
if (bin >= count) bin = (w3ddmg_u8)(count - 1);
return model->edge_masks[(__safe_index w3ddmg_u8)bin];
}Wire3D_SetCamera
後続の投影に使うカメラ位置と姿勢を保存します。
書式
void Wire3D_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll);引数
x世界座標でのカメラのX成分。
y世界座標でのカメラのY成分。
z世界座標でのカメラのZ成分。
pitchX軸の回転。16段階で1周します。
yawY軸の回転。16段階で1周します。
rollZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
角度は16段階で1周します。既に描いた線は移動しません。座標差をs16の範囲に収めてください。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:53
WIRE3D_DMG_HEIGHT = 96. Store camera position and angles for later draws; a full turn is 16 angle steps. No existing drawing is reprojected and no input range validation occurs.
kitaqgb/lib/wire3d_dmg.c:2659
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}Wire3D_SetPalette
モノクロ背景パレットBGPの4階調の割り当てを直接変更します。
書式
void Wire3D_SetPalette(w3ddmg_u8 bgp);引数
bgp4色の階調を2ビットずつ並べたBGPレジスタ値。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
2ビットずつのハードウェア形式をそのまま渡します。VBlank待ちは行いません。CGBのRGBパレットは別途設定します。サンプルは0xFCで白地に黒線を表示します。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。


ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d.h:54
WIRE3D_DMG_HEIGHT = 96. Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
kitaqgb/lib/wire3d_dmg.c:597
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp)
{
w3ddmg_reg_bgp = bgp;
}wire3d_cgb.h — CGB専用の色付きワイヤーフレーム
描画速度を生かす使い方
通常の128×96描画では、描画先アドレスを逐次更新します。遮蔽なしの線は色のビットプレーンを1本ごとに選び、画素数や誤差をCPUのレジスタで管理します。遮蔽を使う線も色のビットプレーンを1本ごとに選び、描画先とマスクのアドレスを逐次更新します。各画素では対応するマスクビットを確認し、覆われていない画素だけを書きます。160×144描画とFastMapは、それぞれ専用の描画経路です。 0度・90度・180度・270度の回転は、入力の範囲制限を行ってから座標の交換と符号変更で計算します。ゲーム全体の速度にはモデルの頂点数、辺の長さ、遮蔽、VRAM転送も影響します。各関数の所要時間と、1画像を完成させる時間を分けて計測してください。
CGB専用の色付きワイヤーフレームです。128×96のピクセル描画、160×144のタイル割り当て描画、輪郭タイルを並べるFastMapは別の初期化と転送手順を使います。モデルは頂点・辺・三角形面を呼び出し側が保持します。物理計算は行いません。
Vec3はx/y/z、Edgeは頂点番号a/b、Faceは三角形の頂点番号a/b/cを持ちます。Modelのvertices・edges・facesはそれらの配列、各countは要素数です。edge_facesは辺に隣接する面番号f0/f1で、FACE_NONEは面なしを表します。flagsのMODEL_HIDDEN_LINESは面を用いた辺選択を要求します。面の向きは投影後の符号付き面積で判定するため、頂点の並べ方が重要です。
Objectはmodelへの参照と、位置x/y/z・角度rx/ry/rz・倍率scale_q8・表示可否visibleを保持します。モデルの共有は可能ですが、表をコピーしたりROMバンクを自動で切り替えたりはしません。参照先を描画中に読み取れるように配置します。CGBのObjectには色番号colorもあります。
FastCubeのx/yは画面中心からの変位、zは大きさや描画順の選択に使います。FastCubesは角度を使わず、FastCubeFrameはrx/ry/rzから4種類のスタンプを選びます。これらの簡易表示を、カメラ投影を行うDrawModelと区別して使ってください。
Wire3DCGB_BeginFrame
現在の描画モードで新しい空のフレームを開始します。
書式
void Wire3DCGB_BeginFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
通常モードではステージとマスクを消し、全体転送を選択します。全画面ではタイル割り当てと区間情報をリセットします。隠線処理を解除しますが背景予約は保持します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:122
kitaqgb/lib/wire3d_cgb.c:8154
void Wire3DCGB_BeginFrame()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 1;
w3dcgb_clear_stage_asm();
w3dcgb_clear_occlusion_mask_asm();
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_BeginFrameFast
画面を消去して開始し、過去のsparse転送範囲を全画面として扱わせます。
書式
void Wire3DCGB_BeginFrameFast();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
BeginFrame相当の処理に加え、2つの過去範囲を192タイル全体へ広げます。次のsparse転送が古いバンクの狭い範囲を信用するのを防ぎます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:123
kitaqgb/lib/wire3d_cgb.c:8175
void Wire3DCGB_BeginFrameFast()
{
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 1;
w3dcgb_clear_stage_asm();
w3dcgb_clear_occlusion_mask_asm();
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_BeginFrameSparse
変更範囲を使う転送用のフレームを開始します。
書式
void Wire3DCGB_BeginFrameSparse();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
通常モードでもステージ全体を消去します。前のフレームの外側へ範囲が広がっても古いピクセルを持ち越しません。隠線設定を解除し、全画面モードでは専用の割り当てリセットを行います。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:124
kitaqgb/lib/wire3d_cgb.c:8200
void Wire3DCGB_BeginFrameSparse()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 0;
w3dcgb_dirty_count = 0;
/* One ASM pass is faster than repeated range-growth clears and guarantees
that newly exposed dirty tiles can never copy stale stage pixels. */
w3dcgb_clear_stage_asm();
#ifndef WIRE3DCGB_PAINTER_ONLY
/* A Z-sorted painter erases silhouettes directly in the stage and never
reads the separate front-to-back coverage mask. */
w3dcgb_clear_occlusion_mask_asm();
#endif
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_ClearFrameTiles
128×96の表示タイルとステージを消去して描画面を空にします。
書式
void Wire3DCGB_ClearFrameTiles();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
VBlankでLCDを停止し、両タイルバンクと属性をリセットします。sparse範囲の履歴は保持し、full_modeも変えません。全画面モードの再初期化には使いません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:121
kitaqgb/lib/wire3d_cgb.c:8240
void Wire3DCGB_ClearFrameTiles()
{
w3dcgb_u8 i;
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_stage_asm();
w3dcgb_clear_frame_tiles_vram_asm();
w3dcgb_fill_attr_map_asm();
w3dcgb_reg_lcdc = 0x81;
i = 0;
while (i < W3DCGB_DIRTY_TILE_LIMIT)
{
w3dcgb_dirty_flags[(__safe_index w3dcgb_u8)i] = 0;
w3dcgb_prev_dirty_flags[(__safe_index w3dcgb_u8)i] = 0;
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
}Wire3DCGB_ClearOcclusionMask
隠線判定用のマスクまたは全画面の区間履歴を空にします。
書式
void Wire3DCGB_ClearOcclusionMask();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
表示中・ステージのピクセルと隠線の有効設定は変更しません。現在モードに対応するマスクを消去します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
三角形のマスクで赤い横線の中央を隠し、圧縮輪郭のマスクで青い線を隠します。緑の輪郭が遮蔽領域の位置を示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:148
kitaqgb/lib/wire3d_cgb.c:1962
void Wire3DCGB_ClearOcclusionMask()
{
if (w3dcgb_full_mode != 0) w3dcgb_full_clear_spans_asm();
else w3dcgb_clear_occlusion_mask();
}Wire3DCGB_ClearSparseStageFast
ステージ全体を消去し、128×96全体を転送対象として記録します。
書式
void Wire3DCGB_ClearSparseStageFast();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
部分消去ではありません。ステージ内の全ピクセルを失います。隠線マスクを消したり、転送モードを選んだりする処理はありません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:125
kitaqgb/lib/wire3d_cgb.c:8227
void Wire3DCGB_ClearSparseStageFast()
{
w3dcgb_clear_stage_asm();
w3dcgb_mark_dirty_rect(0, 0,
(w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1),
(w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
}Wire3DCGB_DrawEdgeList2D
投影済みの符号付き8ビットの頂点変位から、最大64辺を描きます。
書式
void Wire3DCGB_DrawEdgeList2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count, const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y, w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);引数
edges辺の両端の頂点番号。構造体またはバイト対という宣言の形式に従います。
edge_count辺の本数。
vertex_x投影済み頂点のX変位を並べた配列。
vertex_y投影済み頂点のY変位を並べた配列。
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
NULL配列は無視します。頂点個数は検査せず、番号は呼び出し側で保証します。中心加算後の座標を画面内に収め、バイト変換の巻き戻りを防ぎます。色を保持します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:183
kitaqgb/lib/wire3d_cgb.c:8524
void Wire3DCGB_DrawEdgeList2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 old_color;
if (edges == 0) return;
if (vertex_x == 0) return;
if (vertex_y == 0) return;
if (edge_count > WIRE3DCGB_MODEL_EDGE_LIMIT) edge_count = WIRE3DCGB_MODEL_EDGE_LIMIT;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
if (w3dcgb_full_mode == 0)
{
w3dcgb_edge_list_ptr = edges;
w3dcgb_edge_x_ptr = vertex_x;
w3dcgb_edge_y_ptr = vertex_y;
w3dcgb_edge_remaining = edge_count;
w3dcgb_edge_center_x = x;
w3dcgb_edge_center_y = y;
w3dcgb_edge_list_asm();
w3dcgb_line_color = old_color;
return;
}
i = 0;
while (i < edge_count)
{
a = edges[(__safe_index w3dcgb_u8)i].a;
b = edges[(__safe_index w3dcgb_u8)i].b;
w3dcgb_line_x0 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_y0 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_x1 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)b]);
w3dcgb_line_y1 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)b]);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawEdgeListClipped2D
符号付きの投影済み頂点と辺を検証して、画面外の辺を切り詰めて描きます。
書式
void Wire3DCGB_DrawEdgeListClipped2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count, const w3dcgb_i16* vx, const w3dcgb_i16* vy, w3dcgb_u8 vertex_count, w3dcgb_i16 cx, w3dcgb_i16 cy, w3dcgb_u8 color);引数
edges辺の両端の頂点番号。構造体またはバイト対という宣言の形式に従います。
edge_count辺の本数。
vxクリッピング前の符号付き16ビットX変位配列。
vyクリッピング前の符号付き16ビットY変位配列。
vertex_count頂点配列の要素数。
cx表示領域内の中心X座標(ピクセル)。
cy表示領域内の中心Y座標(ピクセル)。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
頂点1~24・辺64以下で、全インデックスとポインタが必要です。各座標と中心は±2047以内。加算後に±2047を超える端点は線クリッパーで拒否される場合があります。色を保持し、共有画面作業領域を書き換えます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:138
kitaqgb/lib/wire3d_cgb.c:9806
void Wire3DCGB_DrawEdgeListClipped2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i16* vx, const w3dcgb_i16* vy, w3dcgb_u8 vertex_count,
w3dcgb_i16 cx, w3dcgb_i16 cy, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 inside;
w3dcgb_i16 x;
w3dcgb_i16 y;
w3dcgb_i16 min_x;
w3dcgb_i16 min_y;
w3dcgb_i16 max_x;
w3dcgb_i16 max_y;
if ((edges == 0) || (vx == 0) || (vy == 0)) return;
if ((cx < -2047) || (cx > 2047) || (cy < -2047) || (cy > 2047)) return;
if ((vertex_count == 0) || (vertex_count > WIRE3DCGB_MODEL_VERTEX_LIMIT) ||
(edge_count > WIRE3DCGB_MODEL_EDGE_LIMIT)) return;
i = 0;
while (i < edge_count)
{
if ((edges[(__safe_index w3dcgb_u8)i].a >= vertex_count) ||
(edges[(__safe_index w3dcgb_u8)i].b >= vertex_count)) return;
i = (w3dcgb_u8)(i + 1);
}
min_x = 32767;
min_y = 32767;
max_x = -32767;
max_y = -32767;
inside = 1;
i = 0;
while (i < vertex_count)
{
if ((vx[(__safe_index w3dcgb_u8)i] < -2047) ||
(vx[(__safe_index w3dcgb_u8)i] > 2047) ||
(vy[(__safe_index w3dcgb_u8)i] < -2047) ||
(vy[(__safe_index w3dcgb_u8)i] > 2047)) return;
x = (w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)i]);
y = (w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)i]);
if (x < min_x) min_x = x;
if (x > max_x) max_x = x;
if (y < min_y) min_y = y;
if (y > max_y) max_y = y;
/* Reuse projected-vertex scratch; drawing is non-reentrant. */
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = (w3dcgb_u8)(x - 64);
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = (w3dcgb_u8)(y - 48);
i = (w3dcgb_u8)(i + 1);
}
if ((w3dcgb_clip_outcode(min_x, min_y) | w3dcgb_clip_outcode(max_x, max_y)) != 0) inside = 0;
if (inside != 0)
{
if (w3dcgb_full_mode == 0) w3dcgb_mark_dirty_rect((w3dcgb_u8)min_x,
(w3dcgb_u8)min_y, (w3dcgb_u8)max_x, (w3dcgb_u8)max_y);
Wire3DCGB_DrawEdgeList2D(edges, edge_count, (const w3dcgb_i8*)w3dcgb_screen_x,
(const w3dcgb_i8*)w3dcgb_screen_y, 64, 48, color);
return;
}
i = 0;
while (i < edge_count)
{
a = edges[(__safe_index w3dcgb_u8)i].a;
b = edges[(__safe_index w3dcgb_u8)i].b;
Wire3DCGB_DrawLineClipped2D((w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)a]),
(w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)a]),
(w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)b]),
(w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)b]), color);
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_DrawFastCubeFrame
立方体スタンプの前フレームを消し、最大3個の配置と状態表示を背景マップへ転送します。
書式
void Wire3DCGB_DrawFastCubeFrame(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);引数
cubes簡易立方体の位置・角度・色の配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
hidden_enabled0なら隠れた辺も表示し、それ以外なら簡易的に隠します。
selected選択物体の元配列番号。スタンプ版では状態表示の番号です。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
32×32スタンプを8ピクセル単位で配置し、生のZで遠い順に並べます。Initで生成タイルを準備し、安定したFastMapのWRAM対応を保ちます。selectedの下位2ビットは状態表示の位置です。NULLは完全に無視、非NULLで0個なら古い表示を消します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// A complete cube-stamp frame removes the preceding stamp's old map cells.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cube[1];
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors();
cube[0].x=-24;cube[0].y=0;cube[0].z=96;
cube[0].rx=0;cube[0].ry=0;cube[0].rz=0;cube[0].color=1;
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_DrawFastCubeFrame(cube,1,1,0);
cube[0].x=24;
Wire3DCGB_DrawFastCubeFrame(cube,1,1,0);
sample_svbk=bank;result[0]=cube[0].x;result[7]=0xA55A;
while(1) {}
}期待する結果
古い立方体スタンプを消し、(72,32)を起点とする32×32の赤い立方体と、上端の緑の状態表示を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_stamp.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_stamp.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:158
kitaqgb/lib/wire3d_cgb.c:10800
void Wire3DCGB_DrawFastCubeFrame(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 i;
w3dcgb_u8 scene_count;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 c;
w3dcgb_u8 x;
if (cubes == 0) return;
scene_count = count;
if (scene_count > 3) scene_count = 3;
w3dcgb_fast_map_prepare();
i = 0;
while (i < w3dcgb_fast_prev_count)
{
w3dcgb_fast_map_clear_rect(
w3dcgb_fast_prev_x[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_y[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_w[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_h[(__safe_index w3dcgb_u8)i]);
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_fast_map_clear_rect(0, 0, 4, 1);
w3dcgb_fast_map_clear_rect(14, 0, 2, 1);
if (scene_count == 0)
{
w3dcgb_fast_prev_count = 0;
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
return;
}
a = 0;
b = 1;
c = 2;
if (scene_count == 1)
{
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
w3dcgb_fast_prev_count = 1;
}
else if (scene_count == 2)
{
if (cubes[0].z < cubes[1].z)
{
w3dcgb_fast_map_draw_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1), 1);
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
}
else
{
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
w3dcgb_fast_map_draw_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1), 1);
}
w3dcgb_fast_prev_count = 2;
}
else
{
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
if (cubes[b].z < cubes[c].z)
{
w3dcgb_u8 t;
t = b;
b = c;
c = t;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
w3dcgb_fast_map_draw_cube_one(&cubes[a], hidden_enabled, (w3dcgb_u8)(selected == a), a);
w3dcgb_fast_map_draw_cube_one(&cubes[b], hidden_enabled, (w3dcgb_u8)(selected == b), b);
w3dcgb_fast_map_draw_cube_one(&cubes[c], hidden_enabled, (w3dcgb_u8)(selected == c), c);
w3dcgb_fast_prev_count = 3;
}
x = (w3dcgb_u8)(selected & 3);
w3dcgb_fast_map_or_cell((w3dcgb_i16)x, (w3dcgb_i16)0, WIRE3DCGB_COLOR_HIGH, 15);
if (hidden_enabled != 0) w3dcgb_fast_map_or_cell((w3dcgb_i16)15, (w3dcgb_i16)0, WIRE3DCGB_COLOR_HIGH, 15);
else w3dcgb_fast_map_or_cell((w3dcgb_i16)14, (w3dcgb_i16)0, WIRE3DCGB_COLOR_LOW, 15);
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
}Wire3DCGB_DrawFastCubes
最大3個の立方体を、奥行きに応じた大きさの2D輪郭で素早く表現します。
書式
void Wire3DCGB_DrawFastCubes(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);引数
cubes簡易立方体の位置・角度・色の配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
hidden_enabled0なら隠れた辺も表示し、それ以外なら簡易的に隠します。
selected選択物体の元配列番号。スタンプ版では状態表示の番号です。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
生のZで遠い順に描き、カメラと回転は使いません。selectedは元配列の番号です。NULL・0個は無視し、配列を並べ替えません。描画後の色は元に戻しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Fast cube approximations use screen offsets and depth size bands, not a camera.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cubes[2];
void main() {
u8 i;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(i=0;i<2;i++){cubes[i].rx=0;cubes[i].ry=0;cubes[i].rz=0;}
cubes[0].x=-32;cubes[0].y=-8;cubes[0].z=90;cubes[0].color=1;
cubes[1].x=32;cubes[1].y=8;cubes[1].z=160;cubes[1].color=2;
Wire3DCGB_SetCamera(100,0,0,4,4,4); // FastCubes intentionally ignores this.
Wire3DCGB_DrawFastCubes(cubes,2,1,0);
Wire3DCGB_DrawFastStatus(1,0);
result[0]=Wire3DCGB_GetLineColor();
result[1]=cubes[0].z;result[2]=cubes[1].z;
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
左の近い赤い立方体を大きく、右の遠い青い立方体を小さく描きます。赤い立方体の中心の緑の十字が選択状態です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_cubes.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_cubes.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:156
kitaqgb/lib/wire3d_cgb.c:8765
void Wire3DCGB_DrawFastCubes(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 scene_count;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 c;
w3dcgb_u8 x;
if (cubes == 0) return;
scene_count = count;
if (scene_count == 0) return;
if (scene_count > 3) scene_count = 3;
a = 0;
b = 1;
c = 2;
if (scene_count == 1)
{
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
return;
}
if (scene_count == 2)
{
if (cubes[0].z < cubes[1].z)
{
w3dcgb_draw_fast_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1));
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
}
else
{
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
w3dcgb_draw_fast_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1));
}
return;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
if (cubes[b].z < cubes[c].z)
{
w3dcgb_u8 t;
t = b;
b = c;
c = t;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
w3dcgb_draw_fast_cube_one(&cubes[a], hidden_enabled, (w3dcgb_u8)(selected == a));
w3dcgb_draw_fast_cube_one(&cubes[b], hidden_enabled, (w3dcgb_u8)(selected == b));
w3dcgb_draw_fast_cube_one(&cubes[c], hidden_enabled, (w3dcgb_u8)(selected == c));
}Wire3DCGB_DrawFastProjectile
投影済みの中心に、奥行きと位相で変わる6辺の弾を描きます。
書式
void Wire3DCGB_DrawFastProjectile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_i16 z, w3dcgb_u8 phase, w3dcgb_u8 color);引数
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
z大きさの選択または投影に使う奥行き。
phase8カウントで向きが変わる回転位相。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
中心はX=10~117、Y=10~85です。4段階の大きさと(phase>>3)&3の向きを使います。カメラは適用せず、範囲をdirtyへ記録し、指定色を保持します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:159
kitaqgb/lib/wire3d_cgb.c:8833
void Wire3DCGB_DrawFastProjectile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_i16 z,
w3dcgb_u8 phase, w3dcgb_u8 color)
{
w3dcgb_u8 radius;
w3dcgb_u8 half;
w3dcgb_u8 depth;
w3dcgb_u8 ax;
w3dcgb_u8 ay;
w3dcgb_u8 bx;
w3dcgb_u8 by;
w3dcgb_u8 cx;
w3dcgb_u8 cy;
w3dcgb_u8 dx;
w3dcgb_u8 dy;
w3dcgb_u8 spin;
if ((x < 10) || (x > 117) || (y < 10) || (y > 85)) return;
radius = 3;
depth = 1;
if (z <= 160) { radius = 4; depth = 2; }
if (z <= 112) { radius = 5; depth = 3; }
if (z <= 64) { radius = 7; depth = 4; }
half = (w3dcgb_u8)(radius >> 1);
ax = x;
ay = (w3dcgb_u8)(y - radius);
bx = (w3dcgb_u8)(x - radius);
by = (w3dcgb_u8)(y + half);
cx = (w3dcgb_u8)(x + radius);
cy = by;
spin = (w3dcgb_u8)((phase >> 3) & 3);
dx = (w3dcgb_u8)(x + depth);
dy = (w3dcgb_u8)(y - depth);
if (spin == 1) dy = (w3dcgb_u8)(y + depth);
else if (spin == 2)
{
dx = (w3dcgb_u8)(x - depth);
dy = (w3dcgb_u8)(y + depth);
}
else if (spin == 3) dx = (w3dcgb_u8)(x - depth);
w3dcgb_mark_dirty_rect((w3dcgb_u8)(x - radius), (w3dcgb_u8)(y - radius),
(w3dcgb_u8)(x + radius), (w3dcgb_u8)(y + radius));
w3dcgb_line_color = color;
w3dcgb_draw_line_raw(ax, ay, bx, by);
w3dcgb_draw_line_raw(bx, by, cx, cy);
w3dcgb_draw_line_raw(cx, cy, ax, ay);
w3dcgb_draw_line_raw(ax, ay, dx, dy);
w3dcgb_draw_line_raw(bx, by, dx, dy);
w3dcgb_draw_line_raw(cx, cy, dx, dy);
}Wire3DCGB_DrawFastStatus
選択中の物体と隠線モードを示す、小さな線状の表示を描きます。
書式
void Wire3DCGB_DrawFastStatus(w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);引数
hidden_enabled0なら隠れた辺も表示し、それ以外なら簡易的に隠します。
selected選択物体の元配列番号。スタンプ版では状態表示の番号です。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
色3で描き、範囲もdirtyへ追加します。文字タイルではありません。selectedは0~2を指定し、値の検査は行いません。元の色は復元しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Fast cube approximations use screen offsets and depth size bands, not a camera.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cubes[2];
void main() {
u8 i;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(i=0;i<2;i++){cubes[i].rx=0;cubes[i].ry=0;cubes[i].rz=0;}
cubes[0].x=-32;cubes[0].y=-8;cubes[0].z=90;cubes[0].color=1;
cubes[1].x=32;cubes[1].y=8;cubes[1].z=160;cubes[1].color=2;
Wire3DCGB_SetCamera(100,0,0,4,4,4); // FastCubes intentionally ignores this.
Wire3DCGB_DrawFastCubes(cubes,2,1,0);
Wire3DCGB_DrawFastStatus(1,0);
result[0]=Wire3DCGB_GetLineColor();
result[1]=cubes[0].z;result[2]=cubes[1].z;
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
左の近い赤い立方体を大きく、右の遠い青い立方体を小さく描きます。赤い立方体の中心の緑の十字が選択状態です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_cubes.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_cubes.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:157
kitaqgb/lib/wire3d_cgb.c:8888
void Wire3DCGB_DrawFastStatus(w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 x;
w3dcgb_mark_dirty_rect(0, 0, 31, 7);
w3dcgb_mark_dirty_rect(116, 0, 127, 7);
x = (w3dcgb_u8)(4 + (selected * 8));
w3dcgb_line_color = WIRE3DCGB_COLOR_HIGH;
w3dcgb_draw_line_raw(x, 3, (w3dcgb_u8)(x + 5), 3);
w3dcgb_draw_line_raw(x, 6, (w3dcgb_u8)(x + 5), 6);
if (hidden_enabled != 0)
{
w3dcgb_draw_line_raw(118, 3, 124, 3);
w3dcgb_draw_line_raw(118, 6, 124, 6);
}
else
{
w3dcgb_draw_line_raw(118, 3, 124, 6);
w3dcgb_draw_line_raw(118, 6, 124, 3);
}
}Wire3DCGB_DrawLine2D
画面座標の両端を含む線を、現在のモードで描きます。
書式
void Wire3DCGB_DrawLine2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
端点は表示領域内に用意し、外側の座標にはDrawLineClipped2Dを使います。通常は色面をORし、dirty範囲を記録しません。全画面はタイルを割り当て色を置換します。色0はどちらも3です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:177
kitaqgb/lib/wire3d_cgb.c:8363
void Wire3DCGB_DrawLine2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by)
{
w3dcgb_line_x0 = ax;
w3dcgb_line_y0 = ay;
w3dcgb_line_x1 = bx;
w3dcgb_line_y1 = by;
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
}Wire3DCGB_DrawLine2DColor
画面上の線を、現在色を保持したまま指定色で描きます。
書式
void Wire3DCGB_DrawLine2DColor(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 color);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
DrawLine2Dと同じ座標・色合成・転送条件です。color&3を使いますが、0で線を消すことはできません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:178
kitaqgb/lib/wire3d_cgb.c:8376
void Wire3DCGB_DrawLine2DColor(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawLine2D(ax, ay, bx, by);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawLine3D
世界座標の両端をカメラで投影し、現在色の線を描きます。
書式
void Wire3DCGB_DrawLine3D(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz);引数
ax世界座標の始点のX成分。
ay世界座標の始点のY成分。
az世界座標の始点のZ成分。
bx世界座標の終点のX成分。
by世界座標の終点のY成分。
bz世界座標の終点のZ成分。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
片方でも深度範囲外なら線全体を省略します。投影は128×96基準です。通常の線はsparse範囲を記録しないため、全体転送か履歴無効化を使います。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:172
kitaqgb/lib/wire3d_cgb.c:8992
void Wire3DCGB_DrawLine3D(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz)
{
w3dcgb_u8 sx0;
w3dcgb_u8 sy0;
w3dcgb_u8 sx1;
w3dcgb_u8 sy1;
if (w3dcgb_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3dcgb_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DCGB_DrawLine2D(sx0, sy0, sx1, sy1);
}Wire3DCGB_DrawLine3DColor
3Dの線を一時的な指定色で描きます。
書式
void Wire3DCGB_DrawLine3DColor(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz, w3dcgb_u8 color);引数
ax世界座標の始点のX成分。
ay世界座標の始点のY成分。
az世界座標の始点のZ成分。
bx世界座標の終点のX成分。
by世界座標の終点のY成分。
bz世界座標の終点のZ成分。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
colorの下位2ビットを使い、終了時に元の色へ戻します。投影・深度拒否・転送範囲の条件はDrawLine3Dに従います。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:173
kitaqgb/lib/wire3d_cgb.c:9005
void Wire3DCGB_DrawLine3DColor(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawLine3D(ax, ay, az, bx, by, bz);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawLineClipped2D
符号付きの画面座標を表示領域へ切り詰め、指定色の線を描きます。
書式
void Wire3DCGB_DrawLineClipped2D(w3dcgb_i16 x0, w3dcgb_i16 y0, w3dcgb_i16 x1, w3dcgb_i16 y1, w3dcgb_u8 color);引数
x0最小側のX端点(表示領域内のピクセル)。
y0最小側のY端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
y1最大側のY端点(表示領域内のピクセル)。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
各入力は±2047以内です。最大8回の交点計算で切り詰めます。通常モードではdirty範囲も記録します。3D投影や奥行き方向のクリッピングは行わず、描画後は元の色へ戻します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:136
Signed endpoints, clipped before conversion to the rasterizer's u8 ABI.
kitaqgb/lib/wire3d_cgb.c:9745
void Wire3DCGB_DrawLineClipped2D(w3dcgb_i16 x0, w3dcgb_i16 y0,
w3dcgb_i16 x1, w3dcgb_i16 y1, w3dcgb_u8 color)
{
w3dcgb_u8 c0;
w3dcgb_u8 c1;
w3dcgb_u8 outside;
w3dcgb_u8 guard;
w3dcgb_i16 x;
w3dcgb_i16 y;
c0 = 0;
c1 = 0;
if ((((x0 | y0 | x1 | y1) & 0xFF80) != 0) || (y0 >= WIRE3DCGB_SCREEN_H) || (y1 >= WIRE3DCGB_SCREEN_H))
{
if ((x0 < -2047) || (x0 > 2047) || (y0 < -2047) || (y0 > 2047) ||
(x1 < -2047) || (x1 > 2047) || (y1 < -2047) || (y1 > 2047)) return;
c0 = w3dcgb_clip_outcode(x0, y0);
c1 = w3dcgb_clip_outcode(x1, y1);
}
guard = 0;
while (guard < 8)
{
if ((c0 | c1) == 0)
{
/* The existing ASM rasterizer handles the hot, in-window path. */
if (w3dcgb_full_mode == 0) w3dcgb_mark_dirty_line((w3dcgb_u8)x0, (w3dcgb_u8)y0,
(w3dcgb_u8)x1, (w3dcgb_u8)y1);
Wire3DCGB_DrawLine2DColor((w3dcgb_u8)x0, (w3dcgb_u8)y0,
(w3dcgb_u8)x1, (w3dcgb_u8)y1, color);
return;
}
if ((c0 & c1) != 0) return;
outside = c0;
if (outside == 0) outside = c1;
if ((outside & 12) != 0)
{
y = 0;
if ((outside & 8) != 0) y = WIRE3DCGB_SCREEN_H - 1;
if (((outside & 8) != 0) && (w3dcgb_full_mode != 0)) y = WIRE3DCGB_FULL_SCREEN_H - 1;
x = (w3dcgb_i16)(x0 + w3dcgb_clip_muldiv((w3dcgb_i16)(x1 - x0),
(w3dcgb_i16)(y - y0), (w3dcgb_i16)(y1 - y0)));
}
else
{
x = 0;
if ((outside & 2) != 0) x = WIRE3DCGB_SCREEN_W - 1;
if (((outside & 2) != 0) && (w3dcgb_full_mode != 0)) x = WIRE3DCGB_FULL_SCREEN_W - 1;
y = (w3dcgb_i16)(y0 + w3dcgb_clip_muldiv((w3dcgb_i16)(y1 - y0),
(w3dcgb_i16)(x - x0), (w3dcgb_i16)(x1 - x0)));
}
if (outside == c0) { x0 = x; y0 = y; c0 = w3dcgb_clip_outcode(x0, y0); }
else { x1 = x; y1 = y; c1 = w3dcgb_clip_outcode(x1, y1); }
guard = (w3dcgb_u8)(guard + 1);
}
}Wire3DCGB_DrawLineList2DColor
4バイトで1本を表す画面座標の線リストを、指定色でまとめて描きます。
書式
void Wire3DCGB_DrawLineList2DColor(const w3dcgb_u8* line_xy, w3dcgb_u8 line_count, w3dcgb_u8 color);引数
line_xyx0,y0,x1,y1を1組とするバイト配列。
line_count線レコードの個数。最大64本を処理します。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
各レコードはx0,y0,x1,y1です。最大64本、NULLは無視します。端点を表示領域内に指定し、描画後は元の色へ戻します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:186
kitaqgb/lib/wire3d_cgb.c:8571
void Wire3DCGB_DrawLineList2DColor(const w3dcgb_u8* line_xy,
w3dcgb_u8 line_count, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 offset;
w3dcgb_u8 old_color;
if (line_xy == 0) return;
if (line_count > WIRE3DCGB_MODEL_EDGE_LIMIT) line_count = WIRE3DCGB_MODEL_EDGE_LIMIT;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
i = 0;
offset = 0;
while (i < line_count)
{
w3dcgb_line_x0 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_y0 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_x1 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_y1 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawMaskedModel2D
投影済みの頂点と辺マスクから、有効な辺だけを描きます。
書式
void Wire3DCGB_DrawMaskedModel2D(const Wire3DCGB_Model* model, const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y, const w3dcgb_u8* edge_mask, w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
vertex_x投影済み頂点のX変位を並べた配列。
vertex_y投影済み頂点のY変位を並べた配列。
edge_mask下位ビットから順に各辺の表示可否を表すバイト配列。
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
1バイト8辺で下位ビットから使います。全ポインタとインデックスを有効にし、0辺でもedge_mask[0]を用意します。個数制限はなく、中心加算後の座標を画面内に保ちます。色を復元します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:179
kitaqgb/lib/wire3d_cgb.c:8392
void Wire3DCGB_DrawMaskedModel2D(const Wire3DCGB_Model* model,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
const w3dcgb_u8* edge_mask,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 bit;
w3dcgb_u8 mask_index;
w3dcgb_u8 mask;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
i = 0;
bit = 1;
mask_index = 0;
mask = edge_mask[0];
while (i < model->edge_count)
{
if ((mask & bit) != 0)
{
a = model->edges[(__safe_index w3dcgb_u8)i].a;
b = model->edges[(__safe_index w3dcgb_u8)i].b;
w3dcgb_line_x0 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_y0 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_x1 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)b]);
w3dcgb_line_y1 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)b]);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
}
i = (w3dcgb_u8)(i + 1);
bit = (w3dcgb_u8)(bit << 1);
if ((bit == 0) && (i < model->edge_count))
{
bit = 1;
mask_index = (w3dcgb_u8)(mask_index + 1);
mask = edge_mask[(__safe_index w3dcgb_u8)mask_index];
}
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawModel
3Dモデルを指定した位置・回転で等倍描画します。
書式
void Wire3DCGB_DrawModel(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
DrawModelScaledへ倍率256を渡します。通常の128×96ステージ用で、sparse範囲は記録しません。全体転送を使うか、履歴を無効化して転送してください。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:128
kitaqgb/lib/wire3d_cgb.c:9015
void Wire3DCGB_DrawModel(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz)
{
Wire3DCGB_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}Wire3DCGB_DrawModelColor
等倍モデルを一時的な指定色で描きます。
書式
void Wire3DCGB_DrawModelColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_u8 color);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
DrawModelScaledの制限に従います。描画後は呼び出し前の色に戻します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:130
kitaqgb/lib/wire3d_cgb.c:9021
void Wire3DCGB_DrawModelColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_u8 color)
{
Wire3DCGB_DrawModelScaledColor(model, x, y, z, rx, ry, rz, 256, color);
}Wire3DCGB_DrawModelScaled
モデルを拡大縮小・回転・投影し、選択された辺を現在色で描きます。
書式
void Wire3DCGB_DrawModelScaled(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
頂点24・面16まで処理しますがedge_countを64には制限しません。倍率256は等倍、0以下も等倍です。近面をまたぐ辺のクリッピングはありません。共有投影領域を上書きし、通常ステージに色面をORします。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:129
kitaqgb/lib/wire3d_cgb.c:9396
void Wire3DCGB_DrawModelScaled(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8)
{
w3dcgb_u8 i;
w3dcgb_u8 count;
w3dcgb_u8 edge_count;
w3dcgb_u8 face_count;
w3dcgb_u8 camera_unrotated;
w3dcgb_u8 unit_scale;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3DCGB_MODEL_VERTEX_LIMIT) count = WIRE3DCGB_MODEL_VERTEX_LIMIT;
unit_scale = 0;
if (scale_q8 == 256) unit_scale = 1;
camera_unrotated = 0;
if ((w3dcgb_cam_yaw == 0) && (w3dcgb_cam_pitch == 0) && (w3dcgb_cam_roll == 0))
{
camera_unrotated = 1;
}
i = 0;
while (i < count)
{
const Wire3DCGB_Vec3* v;
w3dcgb_i16 vx;
w3dcgb_i16 vy;
w3dcgb_i16 vz;
w3dcgb_u8 sx;
w3dcgb_u8 sy;
w3dcgb_u8 projected;
v = &model->vertices[(__safe_index w3dcgb_u8)i];
if (unit_scale != 0)
{
vx = v->x;
vy = v->y;
vz = v->z;
}
else
{
vx = (w3dcgb_i16)((v->x * scale_q8) >> 8);
vy = (w3dcgb_i16)((v->y * scale_q8) >> 8);
vz = (w3dcgb_i16)((v->z * scale_q8) >> 8);
}
if (ry != 0) w3dcgb_rotate_y(&vx, &vz, (w3dcgb_i8)ry);
if (rx != 0) w3dcgb_rotate_x(&vy, &vz, (w3dcgb_i8)rx);
if (rz != 0) w3dcgb_rotate_z(&vx, &vy, (w3dcgb_i8)rz);
vx = (w3dcgb_i16)(vx + x);
vy = (w3dcgb_i16)(vy + y);
vz = (w3dcgb_i16)(vz + z);
if (camera_unrotated != 0)
{
vx = (w3dcgb_i16)(vx - w3dcgb_cam_x);
vy = (w3dcgb_i16)(vy - w3dcgb_cam_y);
vz = (w3dcgb_i16)(vz - w3dcgb_cam_z);
projected = w3dcgb_project_camera_space(vx, vy, vz, &sx, &sy);
}
else
{
projected = w3dcgb_project_world(vx, vy, vz, &sx, &sy);
}
if (projected != 0)
{
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = sx;
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = sy;
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 1;
}
else
{
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 0;
}
i = (w3dcgb_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3DCGB_MODEL_FACE_LIMIT) face_count = WIRE3DCGB_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3DCGB_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3dcgb_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
i = 0;
while (i < edge_count)
{
const Wire3DCGB_Edge* e;
e = &model->edges[(__safe_index w3dcgb_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3dcgb_screen_visible[(__safe_index w3dcgb_u8)e->a] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)e->b] &&
w3dcgb_is_edge_visible(model, i, face_count))
{
w3dcgb_draw_model_line(
w3dcgb_screen_x[(__safe_index w3dcgb_u8)e->a],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)e->a],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)e->b],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)e->b]);
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_DrawModelScaledColor
モデルの倍率と一時的な線色を指定して描きます。
書式
void Wire3DCGB_DrawModelScaledColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8, w3dcgb_u8 color);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
colorの下位2ビットを使い、描画後は元の色に戻します。通常のモデル描画と同じ頂点・面・転送範囲の条件に従います。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:131
kitaqgb/lib/wire3d_cgb.c:9510
void Wire3DCGB_DrawModelScaledColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawModelScaled(model, x, y, z, rx, ry, rz, scale_q8);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawPoint2D
現在色で1ピクセルを書き込みます。
書式
void Wire3DCGB_DrawPoint2D(w3dcgb_u8 x, w3dcgb_u8 y);引数
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
通常モードは128×96を検査し、dirty範囲を記録して色を置換します。色0なら消去できます。全画面では同一点の線として描くため0は色3、不正座標はoverflowを設定します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:174
kitaqgb/lib/wire3d_cgb.c:8283
void Wire3DCGB_DrawPoint2D(w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 tile;
if (w3dcgb_full_mode != 0)
{
w3dcgb_line_x0 = x;
w3dcgb_line_y0 = y;
w3dcgb_line_x1 = x;
w3dcgb_line_y1 = y;
w3dcgb_full_line_fast_asm();
return;
}
if (x >= WIRE3DCGB_SCREEN_W) return;
if (y >= WIRE3DCGB_SCREEN_H) return;
if (w3dcgb_full_frame_transfer == 0)
{
tile = (w3dcgb_u8)(((x >> 3) * W3DCGB_TILE_H) + (y >> 3));
if (tile < w3dcgb_dirty_min_tile) w3dcgb_dirty_min_tile = tile;
if (tile > w3dcgb_dirty_max_tile) w3dcgb_dirty_max_tile = tile;
}
w3dcgb_plot_x = x;
w3dcgb_plot_y = y;
w3dcgb_plot_stage_asm();
}Wire3DCGB_DrawScene
最大8物体を近い順に描き、手前のモデルの面で後方の線を隠します。
書式
void Wire3DCGB_DrawScene(Wire3DCGB_Object* objects, w3dcgb_u8 count);引数
objects位置・回転・倍率・visibleを持つ物体配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
呼び出し側の配列は並べ替えません。通常128×96用です。color=0はその時点の色を引き継ぎます。マスクを消してから描き、終了時に元の色を戻して隠線設定を解除します。モデルの面・頂点番号は有効なものを用意します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// A near square hides the middle of a farther blue line, regardless of array order.
#include "wire_cgb_example.h"
#include "wire_cgb_plane.h"
__prg_rom Wire3DCGB_Vec3 back_vertices[2]={{-96,0,0},{96,0,0}};
__prg_rom Wire3DCGB_Edge back_edges[1]={{0,1}};
Wire3DCGB_Model back;
Wire3DCGB_Object objects[2];
void main() {
u8 i;
wire_results_clear();wire_plane_setup();
back.vertices=back_vertices;back.edges=back_edges;back.faces=0;back.edge_faces=0;
back.vertex_count=2;back.edge_count=1;back.face_count=0;back.flags=0;
for(i=0;i<2;i++) {
objects[i].x=0;objects[i].y=0;objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256;objects[i].visible=1;
}
objects[0].model=&back;objects[0].z=128;objects[0].color=2;
objects[1].model=&plane;objects[1].z=64;objects[1].color=1;
Wire3DCGB_Init();wire_colors();Wire3DCGB_BeginFrame();Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawScene(objects,2);
result[0]=objects[0].z;result[1]=objects[1].z;result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
手前の赤い正方形(46,30)~(82,66)が、後方の青い横線の中央を隠します。青線は左右だけに見えることを期待します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_scene.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_scene.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:155
kitaqgb/lib/wire3d_cgb.c:8918
void Wire3DCGB_DrawScene(Wire3DCGB_Object* objects, w3dcgb_u8 count)
{
w3dcgb_u8 scene_count;
w3dcgb_u8 i;
w3dcgb_u8 old_color;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3DCGB_SCENE_OBJECT_LIMIT) scene_count = WIRE3DCGB_SCENE_OBJECT_LIMIT;
i = 0;
while (i < scene_count)
{
w3dcgb_scene_order[(__safe_index w3dcgb_u8)i] = i;
w3dcgb_scene_depth[(__safe_index w3dcgb_u8)i] = w3dcgb_scene_object_depth(&objects[(__safe_index w3dcgb_u8)i]);
i = (w3dcgb_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3dcgb_u8 j;
j = (w3dcgb_u8)(i + 1);
while (j < scene_count)
{
w3dcgb_u8 oi;
w3dcgb_u8 oj;
oi = w3dcgb_scene_order[(__safe_index w3dcgb_u8)i];
oj = w3dcgb_scene_order[(__safe_index w3dcgb_u8)j];
if (w3dcgb_scene_depth[(__safe_index w3dcgb_u8)oj] < w3dcgb_scene_depth[(__safe_index w3dcgb_u8)oi])
{
w3dcgb_scene_order[(__safe_index w3dcgb_u8)i] = oj;
w3dcgb_scene_order[(__safe_index w3dcgb_u8)j] = oi;
}
j = (w3dcgb_u8)(j + 1);
}
i = (w3dcgb_u8)(i + 1);
}
old_color = w3dcgb_line_color;
w3dcgb_clear_occlusion_mask();
w3dcgb_occlusion_active = 0;
i = 0;
while (i < scene_count)
{
Wire3DCGB_Object* obj;
obj = &objects[(__safe_index w3dcgb_u8)w3dcgb_scene_order[(__safe_index w3dcgb_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
if (i == 0) w3dcgb_occlusion_active = 0;
else w3dcgb_occlusion_active = 1;
if (obj->color != 0) Wire3DCGB_SetLineColor(obj->color);
Wire3DCGB_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, (w3dcgb_i8)obj->rx, (w3dcgb_i8)obj->ry, (w3dcgb_i8)obj->rz, obj->scale_q8);
w3dcgb_occlusion_active = 0;
w3dcgb_mark_model_occluder(obj->model);
}
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_occlusion_active = 0;
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawTinyModel2D
向きを示す6ピクセルの小さな記号を描きます。
書式
void Wire3DCGB_DrawTinyModel2D(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 turn, w3dcgb_u8 color);引数
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
turn下位5ビットで小さな記号の向きを選ぶ値。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
中心はX=3~124、Y=3~92です。turnの下位5ビットを3区分で使います。一般の3D回転ではなく通常ステージ専用です。線色を復元します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:175
kitaqgb/lib/wire3d_cgb.c:8313
void Wire3DCGB_DrawTinyModel2D(w3dcgb_u8 x, w3dcgb_u8 y,
w3dcgb_u8 turn, w3dcgb_u8 color)
{
w3dcgb_u8 nose_x;
w3dcgb_u8 tail_x;
w3dcgb_u8 old_color;
if ((x < 3) || (x > 124) || (y < 3) || (y > (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 4))) return;
nose_x = x;
tail_x = x;
turn = (w3dcgb_u8)(turn & 31);
if (turn < 12)
{
nose_x = (w3dcgb_u8)(x + 1);
tail_x = (w3dcgb_u8)(x - 1);
}
else if (turn >= 20)
{
nose_x = (w3dcgb_u8)(x - 1);
tail_x = (w3dcgb_u8)(x + 1);
}
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
w3dcgb_plot_x = nose_x;
w3dcgb_plot_y = (w3dcgb_u8)(y - 2);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = x;
w3dcgb_plot_y = (w3dcgb_u8)(y - 1);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = (w3dcgb_u8)(x - 2);
w3dcgb_plot_y = (w3dcgb_u8)(y + 1);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = (w3dcgb_u8)(x + 2);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = x;
w3dcgb_plot_y = y;
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = tail_x;
w3dcgb_plot_y = (w3dcgb_u8)(y + 2);
w3dcgb_plot_stage_asm();
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawWhiteBorderFast
128×96ステージの外周に色番号2の1ピクセル枠を描きます。
書式
void Wire3DCGB_DrawWhiteBorderFast();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
実際の色はパレットによって決まります。サンプルでは青です。内部とWRAMバンクを保持し、dirty範囲は記録しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// A cleared 24-pixel strip and a palette-index-2 border have distinct purposes.
#include "wire_cgb_example.h"
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=0;y<96;y++) Wire3DCGB_DrawLine2D(0,y,31,y);
Wire3DCGB_EraseLeftGuard24Fast();
// With this example's palette, color index 2 is blue rather than white.
Wire3DCGB_DrawWhiteBorderFast();
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
左24列の消去と外周描画を比較します。内側の左24列は黒、残る帯は赤、色番号2の外周は青です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_guard.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_guard.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:147
kitaqgb/lib/wire3d_cgb.c:9369
void Wire3DCGB_DrawWhiteBorderFast()
{
w3dcgb_draw_white_border_asm();
}Wire3DCGB_EnableAtomicMaps
128×96表示を2つの背景マップの切り替えで提示するよう設定します。
書式
void Wire3DCGB_EnableAtomicMaps();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
初期化時に1回呼びます。全画面または設定済みなら無視します。LCDを停止して9800を9C00へ複写し、それぞれの属性を別のタイルバンクへ割り当てます。両マップを予約し、HUD予約も両方へ反映します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:144
Startup-only: reserves both BG maps; queued HUD tiles are mirrored.
kitaqgb/lib/wire3d_cgb.c:7912
void Wire3DCGB_EnableAtomicMaps()
{
if ((w3dcgb_full_mode != 0) || (w3dcgb_atomic_maps != 0)) return;
__asm {
w3dam_wait:
LDH_A_MEM 68
CP_IMM 144
JR_C w3dam_wait
LDH_A_MEM 64
PUSH_AF
XOR_A
LDH_MEM_A 64
LDH_MEM_A 79
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 4
w3dam_copy_page:
LD_C_IMM 0
w3dam_copy_byte:
LD_A_HL
INC_HL
LD_DE_A
INC_DE
DEC_C
JR_NZ w3dam_copy_byte
DEC_B
JR_NZ w3dam_copy_page
LDH_A_MEM 79
AND_IMM 1
JR_NZ w3dam_attrs
LD_A_IMM 1
LDH_MEM_A 79
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 4
JR w3dam_copy_page
w3dam_attrs:
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 12
w3dam_row:
LD_C_IMM 16
w3dam_cell:
XOR_A
LDI_HL_A
LD_A_IMM 8
LD_DE_A
INC_DE
DEC_C
JR_NZ w3dam_cell
LD_A_L
ADD_A_IMM 16
LD_L_A
JR_NC w3dam_hl_ready
INC_H
w3dam_hl_ready:
LD_A_E
ADD_A_IMM 16
LD_E_A
JR_NC w3dam_de_ready
INC_D
w3dam_de_ready:
DEC_B
JR_NZ w3dam_row
LD_A_IMM 1
LD_MEM_A w3dcgb_atomic_maps
XOR_A
LDH_MEM_A 79
POP_AF
OR_IMM 8
LDH_MEM_A 64
RET
}
}Wire3DCGB_EnableDoubleSpeed
CGBのCPUを倍速動作へ切り替えます。
書式
void Wire3DCGB_EnableDoubleSpeed();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
既に倍速なら何もしません。それ以外はKEY1で切り替えを要求しSTOPを実行します。CGB専用であり、DMGへの代替処理はありません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:116
kitaqgb/lib/wire3d_cgb.c:1081
void Wire3DCGB_EnableDoubleSpeed()
{
if ((w3dcgb_reg_key1 & 0x80) != 0) return;
w3dcgb_reg_key1 = 1;
w3dcgb_cgb_speed_switch_asm();
}Wire3DCGB_EndFrame
完成したフレームを転送して表示します。
書式
void Wire3DCGB_EndFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
通常モードは背景予約を処理してステージ全体を送ります。全画面では割り当て済みタイルを送り、HUD予約は処理しません。LCD有効状態が必要で、処理が複数フレームにまたがる場合があります。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:189
kitaqgb/lib/wire3d_cgb.c:9881
void Wire3DCGB_EndFrame()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
if (w3dcgb_bgq_count != 0)
{
w3dcgb_wait_vblank_start();
w3dcgb_flush_bg_queue();
}
w3dcgb_transfer_stage_asm();
w3dcgb_present_stage_asm();
}Wire3DCGB_EndFrameFast
背景予約を処理せず、完成したフレーム全体を転送して表示します。
書式
void Wire3DCGB_EndFrameFast();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
予約したHUD変更はキューに残ります。通常・全画面の各転送条件と完全版ランタイムが必要です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:190
kitaqgb/lib/wire3d_cgb.c:9902
void Wire3DCGB_EndFrameFast()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
w3dcgb_transfer_stage_asm();
w3dcgb_present_stage_asm();
}Wire3DCGB_EndFrameSparse
新しいVBlankを待ち、必要範囲を転送して表示します。
書式
void Wire3DCGB_EndFrameSparse();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
通常モードはEndFrameSparseNowを呼び、全画面では専用の全体転送を使います。LCDを有効にし、1フレーム内で終わるとは仮定しないでください。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:191
kitaqgb/lib/wire3d_cgb.c:9917
void Wire3DCGB_EndFrameSparse()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
w3dcgb_wait_vblank_start();
Wire3DCGB_EndFrameSparseNow();
}Wire3DCGB_EndFrameSparseNow
初回のVBlank待ちを省き、背景予約と現在・2回前の範囲を転送して表示します。
書式
void Wire3DCGB_EndFrameSparseNow();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
内部のHUD復元・DMA・表示切り替えでは待つ場合があります。LCDを有効にし、DMA中のSVBK/VBKを外部から変更しないでください。全画面は専用経路へ進みます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:192
kitaqgb/lib/wire3d_cgb.c:9935
void Wire3DCGB_EndFrameSparseNow()
{
#ifndef WIRE3DCGB_MINIMAL_RUNTIME
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
#endif
/* Atomic mode never writes a displayed tile and does not need a
frame-long VBlank wait before starting an off-screen upload. */
if (w3dcgb_atomic_maps == 0) w3dcgb_restore_hud_blank_tile_asm();
if (w3dcgb_bgq_count != 0) w3dcgb_flush_bg_queue();
w3dcgb_transfer_dirty_tiles();
w3dcgb_present_stage_asm();
}Wire3DCGB_EraseLeftGuard24Fast
通常ステージの左24列を一括で消去します。
書式
void Wire3DCGB_EraseLeftGuard24Fast();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
WRAMバンクを保存・復元します。dirty範囲を記録しないため、sparse転送では消去したタイルを別途含めます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// A cleared 24-pixel strip and a palette-index-2 border have distinct purposes.
#include "wire_cgb_example.h"
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=0;y<96;y++) Wire3DCGB_DrawLine2D(0,y,31,y);
Wire3DCGB_EraseLeftGuard24Fast();
// With this example's palette, color index 2 is blue rather than white.
Wire3DCGB_DrawWhiteBorderFast();
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
左24列の消去と外周描画を比較します。内側の左24列は黒、残る帯は赤、色番号2の外周は青です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_guard.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_guard.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:146
kitaqgb/lib/wire3d_cgb.c:9200
void Wire3DCGB_EraseLeftGuard24Fast()
{
w3dcgb_erase_left_guard24_asm();
}Wire3DCGB_EraseModelFaces
投影したモデルの手前向きの面から、ステージのピクセルを消去します。
書式
void Wire3DCGB_EraseModelFaces(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
隠線フラグと面データが必要です。頂点24・面16まで処理します。通常のC側ステージ消去を使うため、呼び出し側でWRAMバンク2を選択します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Projected model faces erase a square opening; redraw its outline in green.
#include "wire_cgb_example.h"
#include "wire_cgb_plane.h"
void main() {
u8 y;u8 bank;
wire_results_clear();wire_plane_setup();Wire3DCGB_Init();wire_colors();Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=8;y<=88;y++) Wire3DCGB_DrawLine2D(24,y,104,y);
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_EraseModelFaces(&plane,0,0,64,0,0,0,256);
sample_svbk=bank;
Wire3DCGB_DrawModelColor(&plane,0,0,64,0,0,0,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_faces.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:132
kitaqgb/lib/wire3d_cgb.c:9033
void Wire3DCGB_EraseModelFaces(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8)
{
w3dcgb_u8 i;
w3dcgb_u8 count;
w3dcgb_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3DCGB_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3DCGB_MODEL_VERTEX_LIMIT) count = WIRE3DCGB_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DCGB_Vec3* v;
w3dcgb_i16 vx;
w3dcgb_i16 vy;
w3dcgb_i16 vz;
w3dcgb_u8 sx;
w3dcgb_u8 sy;
v = &model->vertices[(__safe_index w3dcgb_u8)i];
vx = (w3dcgb_i16)((v->x * scale_q8) >> 8);
vy = (w3dcgb_i16)((v->y * scale_q8) >> 8);
vz = (w3dcgb_i16)((v->z * scale_q8) >> 8);
w3dcgb_rotate_y(&vx, &vz, (w3dcgb_i8)ry);
w3dcgb_rotate_x(&vy, &vz, (w3dcgb_i8)rx);
w3dcgb_rotate_z(&vx, &vy, (w3dcgb_i8)rz);
vx = (w3dcgb_i16)(vx + x);
vy = (w3dcgb_i16)(vy + y);
vz = (w3dcgb_i16)(vz + z);
if (w3dcgb_project_world(vx, vy, vz, &sx, &sy))
{
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = sx;
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = sy;
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 1;
}
else
{
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 0;
}
i = (w3dcgb_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3DCGB_MODEL_FACE_LIMIT) face_count = WIRE3DCGB_MODEL_FACE_LIMIT;
w3dcgb_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DCGB_Face* f;
f = &model->faces[(__safe_index w3dcgb_u8)i];
if (w3dcgb_face_visible[(__safe_index w3dcgb_u8)i] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->a] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->b] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->c])
{
w3dcgb_clear_triangle(
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->a],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->a],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->b],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->b],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->c],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->c]);
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_ErasePackedSilhouette2D
圧縮した輪郭の内側を、通常ステージの両色面から消去します。
書式
void Wire3DCGB_ErasePackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count, w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);引数
profile1行1バイトの輪郭配列。0xFFは行を省略します。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
y0中心Yから先頭行までの符号付きずれ。
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
最大29行、配置・2ピクセルの余裕・形式はMarkPackedSilhouette2Dと共通です。ASMがWRAMバンク2を保存・復元します。dirty範囲は記録しないため転送対象を別途確保します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の塗りつぶしから、三角形・横一列・矩形・圧縮輪郭を消して黒い穴を作ります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:153
kitaqgb/lib/wire3d_cgb.c:2038
void Wire3DCGB_ErasePackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 i;
w3dcgb_u8 packed;
w3dcgb_u8 half_width;
w3dcgb_i16 center;
w3dcgb_i16 left;
w3dcgb_i16 right;
w3dcgb_i16 row;
if (profile == 0) return;
if (count > 29) count = 29;
i = 0;
while (i < count)
{
packed = profile[(__safe_index w3dcgb_u8)i];
if (packed != 0xFF)
{
row = (w3dcgb_i16)((w3dcgb_i16)y + (w3dcgb_i16)y0 + (w3dcgb_i16)i);
if ((row >= 0) && (row < WIRE3DCGB_SCREEN_H))
{
center = (w3dcgb_i16)((w3dcgb_i16)x + (w3dcgb_i16)(packed >> 4) - 7);
half_width = (w3dcgb_u8)(packed & 15);
left = (w3dcgb_i16)(center - (w3dcgb_i16)half_width - 2);
right = (w3dcgb_i16)(center + (w3dcgb_i16)half_width + 2);
if ((right >= 0) && (left < WIRE3DCGB_SCREEN_W))
{
if (left < 0) left = 0;
if (right >= WIRE3DCGB_SCREEN_W) right = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mask_span_y = (w3dcgb_u8)row;
w3dcgb_mask_span_min_x = (w3dcgb_u8)left;
w3dcgb_mask_span_max_x = (w3dcgb_u8)right;
w3dcgb_stage_clear_span_asm();
}
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_EraseRect2D
矩形内の通常ステージのピクセルを消去します。
書式
void Wire3DCGB_EraseRect2D(w3dcgb_u8 x0, w3dcgb_u8 y0, w3dcgb_u8 x1, w3dcgb_u8 y1);引数
x0最小側のX端点(表示領域内のピクセル)。
y0最小側のY端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
y1最大側のY端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
座標を並べ替え128×96へ制限します。高さ全体の左16/24列は専用ASMを使いますが、一般の矩形はC処理なので呼び出し側でWRAMバンク2を選択します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の塗りつぶしから、三角形・横一列・矩形・圧縮輪郭を消して黒い穴を作ります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:145
kitaqgb/lib/wire3d_cgb.c:9146
void Wire3DCGB_EraseRect2D(w3dcgb_u8 x0, w3dcgb_u8 y0, w3dcgb_u8 x1, w3dcgb_u8 y1)
{
w3dcgb_u8 t;
w3dcgb_u8 y;
if (x0 > x1)
{
t = x0;
x0 = x1;
x1 = t;
}
if (y0 > y1)
{
t = y0;
y0 = y1;
y1 = t;
}
if (x0 >= WIRE3DCGB_SCREEN_W) return;
if (y0 >= WIRE3DCGB_SCREEN_H) return;
if (x1 >= WIRE3DCGB_SCREEN_W) x1 = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
if (y1 >= WIRE3DCGB_SCREEN_H) y1 = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
if ((x0 == 0) &&
(x1 == 15) &&
(y0 == 0) &&
(y1 == (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1)))
{
w3dcgb_erase_left_guard16_asm();
w3dcgb_mark_dirty_rect(0, 0, 15, (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
return;
}
if ((x0 == 0) &&
(x1 == 23) &&
(y0 == 0) &&
(y1 == (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1)))
{
w3dcgb_erase_left_guard24_asm();
w3dcgb_mark_dirty_rect(0, 0, 23, (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
return;
}
y = y0;
while (y <= y1)
{
w3dcgb_stage_clear_span(y, x0, x1);
if (y == y1) break;
y = (w3dcgb_u8)(y + 1);
}
}Wire3DCGB_EraseSpan2D
1行の横区間を、WRAMバンクを保存・復元しながら消去します。
書式
void Wire3DCGB_EraseSpan2D(w3dcgb_u8 y, w3dcgb_u8 x0, w3dcgb_u8 x1);引数
y消去する行のY座標(ピクセル)。
x0最小側のX端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
Xを並べ替え、右端を127へ制限します。行または左端が範囲外なら無視します。該当タイルも転送対象に記録します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の塗りつぶしから、三角形・横一列・矩形・圧縮輪郭を消して黒い穴を作ります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:134
kitaqgb/lib/wire3d_cgb.c:9123
void Wire3DCGB_EraseSpan2D(w3dcgb_u8 y, w3dcgb_u8 x0, w3dcgb_u8 x1)
{
w3dcgb_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
if ((y >= WIRE3DCGB_SCREEN_H) || (x0 >= WIRE3DCGB_SCREEN_W)) return;
if (x1 >= WIRE3DCGB_SCREEN_W) x1 = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mark_dirty_rect(x0, y, x1, y);
w3dcgb_mask_span_y = y;
w3dcgb_mask_span_min_x = x0;
w3dcgb_mask_span_max_x = x1;
w3dcgb_stage_clear_span_asm();
}Wire3DCGB_EraseTriangle2D
三角形の内部を通常ステージの両色面から消去します。
書式
void Wire3DCGB_EraseTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
cx頂点CのX座標(表示領域内のピクセル)。
cy頂点CのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
横区間を表示領域へ制限し、触れたタイルをdirtyにします。呼び出し側でステージのWRAMバンク2を選択します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の塗りつぶしから、三角形・横一列・矩形・圧縮輪郭を消して黒い穴を作ります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:133
kitaqgb/lib/wire3d_cgb.c:9113
void Wire3DCGB_EraseTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy)
{
w3dcgb_clear_triangle(ax, ay, bx, by, cx, cy);
}Wire3DCGB_FastMapBegin
FastMapの16×12セルのタイル番号と属性を空にします。
書式
void Wire3DCGB_FastMapBegin();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
シャドーマップの16~31列は保持します。ハードウェア初期化と転送は行いません。完全版ランタイムと安定したWRAM対応が必要です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:160
kitaqgb/lib/wire3d_cgb.c:10137
void Wire3DCGB_FastMapBegin()
{
w3dcgb_fast_map_ready = 1;
w3dcgb_fast_map_clear_rect(0, 0, 16, 12);
}Wire3DCGB_FastMapBeginTilesOnly
FastMapの16×12セルのタイル番号だけを消去します。
書式
void Wire3DCGB_FastMapBeginTilesOnly();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
属性と16~31列は保持します。背景色の設定を残して番号を入れ替える場合に使います。転送は別の操作です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:161
kitaqgb/lib/wire3d_cgb.c:10146
void Wire3DCGB_FastMapBeginTilesOnly()
{
w3dcgb_fast_map_ready = 1;
w3dcgb_fm_x0 = 0;
w3dcgb_fm_y0 = 0;
w3dcgb_fm_count_x = 16;
w3dcgb_fm_count_y = 12;
w3dcgb_fast_map_clear_rect_asm();
}Wire3DCGB_FastMapCell
1セルへ、指定色の上・下・左・右の輪郭を追加します。
書式
void Wire3DCGB_FastMapCell(w3dcgb_u8 tx, w3dcgb_u8 ty, w3dcgb_u8 color, w3dcgb_u8 mask);引数
txタイル単位のX座標。
tyタイル単位のY座標。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
maskセルの辺指定。1=上、2=下、4=左、8=右。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
maskの1/2/4/8が上/下/左/右です。同色の辺は合成し、違う色は置換します。mask=0は無視、color=0は2、3超は3です。16×12の範囲に描きます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:162
kitaqgb/lib/wire3d_cgb.c:10160
void Wire3DCGB_FastMapCell(w3dcgb_u8 tx, w3dcgb_u8 ty, w3dcgb_u8 color, w3dcgb_u8 mask)
{
w3dcgb_fast_map_or_cell((w3dcgb_i16)tx, (w3dcgb_i16)ty, color, mask);
}Wire3DCGB_FastMapFlush
シャドーマップの先頭12行のタイル番号と属性をVRAMへ転送します。
書式
void Wire3DCGB_FastMapFlush();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
1行32列をマップ9800へ送ります。LCD有効時は新しいVBlankを待ち、停止中は直ちに進みます。GDMAに必要な安全な期間とWRAM対応を確保します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:164
kitaqgb/lib/wire3d_cgb.c:10208
void Wire3DCGB_FastMapFlush()
{
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
}Wire3DCGB_FastMapFlushTilesOnly
シャドーマップの先頭12行のタイル番号だけをVRAMへ転送します。
書式
void Wire3DCGB_FastMapFlushTilesOnly();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
属性は変更しません。LCD有効時は新しいVBlankを待ちます。1行32列を転送し、完全版ランタイムと安全なGDMA期間が必要です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:165
kitaqgb/lib/wire3d_cgb.c:10217
void Wire3DCGB_FastMapFlushTilesOnly()
{
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_tiles_gdma_asm();
}Wire3DCGB_FastMapRect
タイル単位の矩形へ、指定した側の輪郭を配置します。
書式
void Wire3DCGB_FastMapRect(w3dcgb_u8 tx0, w3dcgb_u8 ty0, w3dcgb_u8 tx1, w3dcgb_u8 ty1, w3dcgb_u8 color, w3dcgb_u8 sides);引数
tx0タイル単位のX座標。
ty0タイル単位のY座標。
tx1タイル単位のX座標。
ty1タイル単位のY座標。
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
sides矩形の辺指定。1=上、2=下、4=左、8=右。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
座標を並べ替え16×12へ制限します。sidesの1/2/4/8が上/下/左/右です。内部は保持します。角を区別するには縦横2セル以上を使います。重なる角は順に上書きされます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:163
kitaqgb/lib/wire3d_cgb.c:10170
void Wire3DCGB_FastMapRect(w3dcgb_u8 tx0, w3dcgb_u8 ty0, w3dcgb_u8 tx1, w3dcgb_u8 ty1, w3dcgb_u8 color, w3dcgb_u8 sides)
{
w3dcgb_u8 t;
if (tx0 > tx1)
{
t = tx0;
tx0 = tx1;
tx1 = t;
}
if (ty0 > ty1)
{
t = ty0;
ty0 = ty1;
ty1 = t;
}
if (tx0 >= 16) return;
if (ty0 >= 12) return;
if (tx1 >= 16) tx1 = 15;
if (ty1 >= 12) ty1 = 11;
if (color == 0) color = WIRE3DCGB_COLOR_MAIN;
if (color > 3) color = 3;
w3dcgb_fm_x0 = tx0;
w3dcgb_fm_y0 = ty0;
w3dcgb_fm_x1 = tx1;
w3dcgb_fm_y1 = ty1;
w3dcgb_fm_count_x = (w3dcgb_u8)(tx1 - tx0 + 1);
w3dcgb_fm_count_y = (w3dcgb_u8)(ty1 - ty0 + 1);
w3dcgb_fm_tile_base = (w3dcgb_u8)(W3DCGB_FAST_TILE_BASE + ((color - 1) << 4));
w3dcgb_fm_sides = (w3dcgb_u8)(sides & 15);
w3dcgb_fast_map_rect_asm();
}Wire3DCGB_GetFullScreenOverflow
全画面描画で座標不正またはタイル不足が起きたかを返します。
書式
w3dcgb_u8 Wire3DCGB_GetFullScreenOverflow();戻り値
異常なしは0、タイル不足または座標不正の検出後は1。
使用条件と制限
一度設定されると、そのフレームの後続の高速描画も抑止されます。BeginFrameで解除します。ハードウェア全体の状態ではありません。
全画面のタイル割り当ては、隠線・overflowによるピクセル書き込みの抑止より先に進みます。そのため、黒いままのタイルも個数に含まれます。異常後の描画呼び出しでも、個数が増える場合があります。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// One point per tile exposes the 127-tile limit without relying on text alone.
#include "wire_cgb_example.h"
void main() {
u8 i;
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame(); Wire3DCGB_SetLineColor(3);
for(i=0;i<128;i++) Wire3DCGB_DrawPoint2D((i%20)*8+4,(i/20)*8+4);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
異なるタイルに1点ずつ、赤い点を127個並べます。128個目の割り当てを試すとタイル数127のままoverflowが1になります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_capacity.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_capacity.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:194
kitaqgb/lib/wire3d_cgb.c:9965
w3dcgb_u8 Wire3DCGB_GetFullScreenOverflow()
{
return w3dcgb_full_overflow;
}Wire3DCGB_GetFullScreenTileCount
現在フレームで割り当てた全画面用タイル数を返します。
書式
w3dcgb_u8 Wire3DCGB_GetFullScreenTileCount();戻り値
割り当て済みタイル数(0~127)。
使用条件と制限
最大127です。バイト数、空きVRAM容量、表示ピクセル数ではありません。完全版ランタイムで使います。
全画面のタイル割り当ては、隠線・overflowによるピクセル書き込みの抑止より先に進みます。そのため、黒いままのタイルも個数に含まれます。異常後の描画呼び出しでも、個数が増える場合があります。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// One point per tile exposes the 127-tile limit without relying on text alone.
#include "wire_cgb_example.h"
void main() {
u8 i;
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame(); Wire3DCGB_SetLineColor(3);
for(i=0;i<128;i++) Wire3DCGB_DrawPoint2D((i%20)*8+4,(i/20)*8+4);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
異なるタイルに1点ずつ、赤い点を127個並べます。128個目の割り当てを試すとタイル数127のままoverflowが1になります。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_capacity.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_capacity.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:193
kitaqgb/lib/wire3d_cgb.c:9956
w3dcgb_u8 Wire3DCGB_GetFullScreenTileCount()
{
return w3dcgb_full_tile_count;
}Wire3DCGB_GetLineColor
現在保存されている線の色番号を読み取ります。
書式
w3dcgb_u8 Wire3DCGB_GetLineColor();戻り値
現在保存されている線色の値。
使用条件と制限
状態を変更しません。通常は0~3です。FastCubesやFastProjectileには有効な色1~3を渡してください。これらの内部処理は渡した値を直接保存する場合があります。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:120
kitaqgb/lib/wire3d_cgb.c:1147
w3dcgb_u8 Wire3DCGB_GetLineColor()
{
return w3dcgb_line_color;
}Wire3DCGB_Init
CGBの128×96ピクセル描画を初期化します。
書式
void Wire3DCGB_Init();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍速へ切り替え、LCDを停止して両タイルバンクとマップを設定します。HUD・生成タイル、既定パレット、色2、カメラ0を用意します。描画前にBeginFrameを呼びます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:113
kitaqgb/lib/wire3d_cgb.c:7994
void Wire3DCGB_Init()
{
w3dcgb_u8 row;
w3dcgb_u8 col;
w3dcgb_u8 tid;
w3dcgb_u8 tile_index;
w3dcgb_u16 off;
w3dcgb_full_mode = 0;
w3dcgb_atomic_maps = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_load_hud_tiles();
#ifndef WIRE3DCGB_MINIMAL_RUNTIME
w3dcgb_load_fast_map_tiles();
w3dcgb_load_fast_stamp_tiles();
#endif
w3dcgb_fill_bg_map_asm();
w3dcgb_fill_attr_map_asm();
row = 0;
while (row < W3DCGB_TILE_H)
{
off = (w3dcgb_u16)((w3dcgb_u16)row << 5);
tile_index = row;
col = 0;
while (col < W3DCGB_TILE_W)
{
if (tile_index < 112) tid = (w3dcgb_u8)(0x90 + tile_index);
else tid = (w3dcgb_u8)(tile_index - 112);
w3dcgb_bg_map_9800[(w3dcgb_u16)(off + (w3dcgb_u16)col)] = tid;
tile_index = (w3dcgb_u8)(tile_index + W3DCGB_TILE_H);
col = (w3dcgb_u8)(col + 1);
}
row = (w3dcgb_u8)(row + 1);
}
w3dcgb_select_blank_render_bank_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_reg_lcdc = 0x81;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_dirty_min_tile = 0xFF;
w3dcgb_dirty_max_tile = 0;
// Both back banks may contain generated fast-map/stamp tiles. Refresh
// the whole pixel viewport on their first sparse use, including empty rows.
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_display_tile_bank = 1;
w3dcgb_pending_tile_bank = 1;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_clear_stage_asm();
}Wire3DCGB_InitFastMapLite
輪郭タイルを並べるFastMap描画を初期化します。
書式
void Wire3DCGB_InitFastMapLite();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
HUDと48個の輪郭タイルを読み込みます。通常のピクセル表示用マップと立方体スタンプは用意しません。FastMap系の関数で描画・転送してください。完全版ランタイムが必要です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}期待する結果
赤い角、青い大きな矩形、緑の1タイルの枠を描きます。最初に置いた左上の赤いセルは消え、属性を保持する更新も確認できます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:115
kitaqgb/lib/wire3d_cgb.c:8108
void Wire3DCGB_InitFastMapLite()
{
w3dcgb_atomic_maps = 0;
w3dcgb_full_mode = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_load_hud_tiles();
w3dcgb_load_fast_map_tiles_lite_asm();
w3dcgb_fill_bg_map_asm();
w3dcgb_fill_attr_map_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_reg_lcdc = 0x81;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_dirty_min_tile = 0xFF;
w3dcgb_dirty_max_tile = 0;
w3dcgb_prev_dirty_min_tile = 0xFF;
w3dcgb_prev_dirty_max_tile = 0;
w3dcgb_fast_map_ready = 0;
w3dcgb_display_tile_bank = 0;
w3dcgb_pending_tile_bank = 0;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_clear_stage_asm();
}Wire3DCGB_InitFullScreen
160×144ピクセルの画面を、使用タイルを割り当てる方式で初期化します。
書式
void Wire3DCGB_InitFullScreen();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
両タイルバンク、マップ9800、カメラ、パレット、割り当て表を設定します。HUDタイルは読み込みません。1フレームの使用上限は127タイルです。描画中はWRAMバンクの対応を保持します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// A large frame demonstrates the 160x144 renderer and its allocated-tile count.
#include "wire_cgb_example.h"
void main() {
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,151,8);
Wire3DCGB_DrawLine2D(8,8,8,135);
Wire3DCGB_DrawLine2DColor(8,135,151,135,2);
Wire3DCGB_DrawLine2DColor(151,8,151,135,3);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrameFast();
result[7]=0xA55A;
while(1) {}
}期待する結果
画面座標(8,8)~(151,135)の大きな枠を描きます。上と左が赤、下が青、右が緑で、64タイルを使用しoverflowは0です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_full.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_full.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:114
kitaqgb/lib/wire3d_cgb.c:8070
void Wire3DCGB_InitFullScreen()
{
w3dcgb_atomic_maps = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_full_clear_map_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_display_tile_bank = 0;
w3dcgb_pending_tile_bank = 0;
w3dcgb_full_tile_count = 0;
w3dcgb_full_prev_tile_count = 0;
w3dcgb_full_overflow = 0;
w3dcgb_full_mode = 1;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_full_begin_asm();
w3dcgb_reg_lcdc = 0x91;
}Wire3DCGB_InvalidateFrameHistory
現在と過去2つの転送範囲を192タイル全体へ広げます。
書式
void Wire3DCGB_InvalidateFrameHistory();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
ピクセルは消しません。範囲を記録しない描画関数の後や、バンクの内容を信用できない場合に、sparse転送へ全域を含めさせます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:142
kitaqgb/lib/wire3d_cgb.c:9712
void Wire3DCGB_InvalidateFrameHistory()
{
w3dcgb_dirty_min_tile = 0;
w3dcgb_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
}Wire3DCGB_MarkDirtyRect2D
指定したピクセル矩形を、後の転送が必要な範囲へ追加します。
書式
void Wire3DCGB_MarkDirtyRect2D(w3dcgb_u8 min_x, w3dcgb_u8 min_y, w3dcgb_u8 max_x, w3dcgb_u8 max_y);引数
min_x最小側のX端点(表示領域内のピクセル)。
min_y最小側のY端点(表示領域内のピクセル)。
max_x最大側のX端点(表示領域内のピクセル)。
max_y最大側のX端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
描画は行いません。最小・最大の順で指定し、上限127/95を超える値は端へ制限します。128×96用のタイル範囲であり、全画面の割り当て表は操作しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}期待する結果
前の赤と青の枠を消し、最後の緑の枠(80,8)~(112,32)だけを表示します。sparse転送と履歴更新を確認する例です。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:126
kitaqgb/lib/wire3d_cgb.c:1288
void Wire3DCGB_MarkDirtyRect2D(w3dcgb_u8 min_x, w3dcgb_u8 min_y, w3dcgb_u8 max_x, w3dcgb_u8 max_y)
{
w3dcgb_mark_dirty_rect(min_x, min_y, max_x, max_y);
}Wire3DCGB_MarkPackedSilhouette2D
圧縮した行別の輪郭を、128×96の隠線マスクへ登録します。
書式
void Wire3DCGB_MarkPackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count, w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);引数
profile1行1バイトの輪郭配列。0xFFは行を省略します。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
y0中心Yから先頭行までの符号付きずれ。
x表示領域内の中心X座標(ピクセル)。
y表示領域内の中心Y座標(ピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
最大29行です。上位4ビット−7が中心ずれ、下位4ビットが半幅、0xFFは行を省略します。y+y0から配置し、左右に2ピクセルを加えて切り詰めます。NULLは無視し、隠線設定や全画面区間は変更しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
三角形のマスクで赤い横線の中央を隠し、圧縮輪郭のマスクで青い線を隠します。緑の輪郭が遮蔽領域の位置を示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:151
kitaqgb/lib/wire3d_cgb.c:1991
void Wire3DCGB_MarkPackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 i;
w3dcgb_u8 packed;
w3dcgb_u8 half_width;
w3dcgb_i16 center;
w3dcgb_i16 left;
w3dcgb_i16 right;
w3dcgb_i16 row;
if (profile == 0) return;
if (count > 29) count = 29;
i = 0;
while (i < count)
{
packed = profile[(__safe_index w3dcgb_u8)i];
if (packed != 0xFF)
{
row = (w3dcgb_i16)((w3dcgb_i16)y + (w3dcgb_i16)y0 + (w3dcgb_i16)i);
if ((row >= 0) && (row < WIRE3DCGB_SCREEN_H))
{
center = (w3dcgb_i16)((w3dcgb_i16)x + (w3dcgb_i16)(packed >> 4) - 7);
half_width = (w3dcgb_u8)(packed & 15);
/* Two-pixel guard covers rasterized endpoints at 22.5-degree views. */
left = (w3dcgb_i16)(center - (w3dcgb_i16)half_width - 2);
right = (w3dcgb_i16)(center + (w3dcgb_i16)half_width + 2);
if ((right >= 0) && (left < WIRE3DCGB_SCREEN_W))
{
if (left < 0) left = 0;
if (right >= WIRE3DCGB_SCREEN_W) right = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mask_span_y = (w3dcgb_u8)row;
w3dcgb_mask_span_min_x = (w3dcgb_u8)left;
w3dcgb_mask_span_max_x = (w3dcgb_u8)right;
w3dcgb_mask_set_span_asm();
}
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_MarkTriangle2D
三角形を、後から描く線を隠す領域として登録します。
書式
void Wire3DCGB_MarkTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
cx頂点CのX座標(表示領域内のピクセル)。
cy頂点CのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
ピクセルは描かず、隠線処理も自動では有効にしません。頂点は表示領域内に指定します。左右に1ピクセルの余裕を加え、通常モードは127、全画面は159で右端を制限します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
三角形のマスクで赤い横線の中央を隠し、圧縮輪郭のマスクで青い線を隠します。緑の輪郭が遮蔽領域の位置を示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:150
kitaqgb/lib/wire3d_cgb.c:1980
void Wire3DCGB_MarkTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy)
{
w3dcgb_mark_triangle(ax, ay, bx, by, cx, cy);
}Wire3DCGB_ProjectAxis48
1成分を奥行きで割り、焦点距離48相当の画面上の変位へ近似変換します。
書式
w3dcgb_i16 Wire3DCGB_ProjectAxis48(w3dcgb_i16 value, w3dcgb_i16 z);引数
value投影する符号付き16ビットの成分。
z大きさの選択または投影に使う奥行き。
戻り値
近似投影した符号付き16ビットの変位。
使用条件と制限
カメラや画面中心は加えません。小さい・負のZは4にし、Zが255を超える間は深度と成分を一緒に右シフトします。成分の大きさは160へ制限します。近面・遠面での拒否はありません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}期待する結果
赤い上下2辺、青い正方形、左端で切れた緑の枠、緑の6ピクセル記号、赤い弾を描き、各リスト形式を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:141
kitaqgb/lib/wire3d_cgb.c:9702
w3dcgb_i16 Wire3DCGB_ProjectAxis48(w3dcgb_i16 value, w3dcgb_i16 z)
{
w3dcgb_projection_value = value;
w3dcgb_projection_z = z;
w3dcgb_project_axis48_asm();
return w3dcgb_projection_result;
}Wire3DCGB_ProjectPoint
世界座標の点をカメラから128×96の画面座標へ投影します。
書式
w3dcgb_u8 Wire3DCGB_ProjectPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);引数
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
sx投影結果のX座標を書き込む、有効なu8領域へのポインタ。
sy投影結果のY座標を書き込む、有効なu8領域へのポインタ。
戻り値
投影成功時は1、深度で拒否した場合は0です。
使用条件と制限
深度8~255を受理し、失敗時は0で出力を保持します。成功時は画面内へ制限した座標を書きます。全画面160×144用の投影ではありません。完全版ランタイムが必要です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:169
kitaqgb/lib/wire3d_cgb.c:589
w3dcgb_u8 Wire3DCGB_ProjectPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy)
{
return w3dcgb_project_world(x, y, z, sx, sy);
}Wire3DCGB_ProjectPointNoRotation
カメラ位置だけを差し引いて、回転計算なしで点を投影します。
書式
w3dcgb_u8 Wire3DCGB_ProjectPointNoRotation(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);引数
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
sx投影結果のX座標を書き込む、有効なu8領域へのポインタ。
sy投影結果のY座標を書き込む、有効なu8領域へのポインタ。
戻り値
投影成功時は1、深度で拒否した場合は0です。
使用条件と制限
カメラ角度がすべて0の用途に使います。128×96へ投影し、深度拒否時は出力を保持します。カメラに角度を設定してもこの関数は無視します。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:171
Fast path for games whose camera pitch, yaw, and roll stay at zero.
kitaqgb/lib/wire3d_cgb.c:598
w3dcgb_u8 Wire3DCGB_ProjectPointNoRotation(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z,
w3dcgb_u8* sx, w3dcgb_u8* sy)
{
return w3dcgb_project_camera_space((w3dcgb_i16)(x - w3dcgb_cam_x),
(w3dcgb_i16)(y - w3dcgb_cam_y),
(w3dcgb_i16)(z - w3dcgb_cam_z), sx, sy);
}Wire3DCGB_PutBgTile
背景セルのタイル番号変更をEndFrameまで予約します。
書式
void Wire3DCGB_PutBgTile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 tile);引数
x32×32背景マップ内のタイル単位のX座標。
y32×32背景マップ内のタイル単位のY座標。
tile背景セルに置く8ビットのタイル番号。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
32×32マップ・48件までで範囲外や超過分は無視します。属性は予約しません。atomic-map設定では両背景マップへ番号を反映します。全画面EndFrameはHUDキューを処理しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:188
kitaqgb/lib/wire3d_cgb.c:1067
void Wire3DCGB_PutBgTile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3dcgb_bgq_count >= W3DCGB_BG_QUEUE_LIMIT) return;
w3dcgb_bgq_x[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = x;
w3dcgb_bgq_y[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = y;
w3dcgb_bgq_tile[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = tile;
w3dcgb_bgq_count = (w3dcgb_u8)(w3dcgb_bgq_count + 1);
}WIRE3DCGB_RGB15
赤・緑・青の5ビット成分をCGBの15ビット色へ組み合わせます。
書式
#define WIRE3DCGB_RGB15(r5, g5, b5) ((w3dcgb_u16)((w3dcgb_u16)((r5) & 31) | ((w3dcgb_u16)((g5) & 31) << 5) | ((w3dcgb_u16)((b5) & 31) << 10)))引数
r5赤の成分(0~31)。
g5緑の成分(0~31)。
b5青の成分(0~31)。
戻り値
3成分を組み合わせたu16のRGB15値。
使用条件と制限
各引数は1回だけ評価します。下位5ビットを使うので範囲外は飽和せず巻き戻ります。赤がbit0~4、緑が5~9、青が10~14です。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:50
Wire3DCGB_SetCamera
投影に使うカメラの位置と16段階の姿勢を保存します。
書式
void Wire3DCGB_SetCamera(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 pitch, w3dcgb_i8 yaw, w3dcgb_i8 roll);引数
x世界座標でのカメラのX成分。
y世界座標でのカメラのY成分。
z世界座標でのカメラのZ成分。
pitchX軸の回転。16段階で1周します。
yawY軸の回転。16段階で1周します。
rollZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
角度は下位4ビットを使います。描画済みのピクセルやLCDのスクロール位置を変更しません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
赤・青・緑の等倍と縮小モデル、交差する3D線を比較します。カメラ付き投影と回転省略投影もRAMの値で確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:127
kitaqgb/lib/wire3d_cgb.c:8266
void Wire3DCGB_SetCamera(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 pitch, w3dcgb_i8 yaw, w3dcgb_i8 roll)
{
w3dcgb_cam_x = x;
w3dcgb_cam_y = y;
w3dcgb_cam_z = z;
w3dcgb_cam_pitch = pitch;
w3dcgb_cam_yaw = yaw;
w3dcgb_cam_roll = roll;
}Wire3DCGB_SetLineColor
以後の線描画に使う色番号を保存します。
書式
void Wire3DCGB_SetLineColor(w3dcgb_u8 color);引数
color描画色番号。通常は1=赤、2=青、3=緑をサンプルのパレットで使います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
colorの下位2ビットを保存します。通常の線はビット面をOR合成し、色1と2の交点は3になります。全画面の線は置換です。線の色0は3として扱い、消去にはなりません。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:119
kitaqgb/lib/wire3d_cgb.c:1140
void Wire3DCGB_SetLineColor(w3dcgb_u8 color)
{
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
}Wire3DCGB_SetOcclusionActive
後続の線に隠線判定を適用するかを設定します。
書式
void Wire3DCGB_SetOcclusionActive(w3dcgb_u8 active);引数
active0で隠線判定を無効、それ以外で有効にします。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
0を無効、それ以外を1として保存します。マスクの消去や面の登録は別途行います。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
三角形のマスクで赤い横線の中央を隠し、圧縮輪郭のマスクで青い線を隠します。緑の輪郭が遮蔽領域の位置を示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:149
kitaqgb/lib/wire3d_cgb.c:1970
void Wire3DCGB_SetOcclusionActive(w3dcgb_u8 active)
{
if (active == 0) w3dcgb_occlusion_active = 0;
else w3dcgb_occlusion_active = 1;
}Wire3DCGB_SetPaletteRGB15
線の4色と、スタンプを着色する背景パレットを設定します。
書式
void Wire3DCGB_SetPaletteRGB15(w3dcgb_u16 color0, w3dcgb_u16 color1, w3dcgb_u16 color2, w3dcgb_u16 color3);引数
color0パレット番号0のRGB15色。
color1パレット番号1のRGB15色。
color2パレット番号2のRGB15色。
color3パレット番号3のRGB15色。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
パレット0に4色を設定し、パレット1~3は共通の背景色と各線色で構成します。RGB15形式を渡します。LCD停止中などパレットへ安全に書き込める期間に呼びます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:118
kitaqgb/lib/wire3d_cgb.c:1112
void Wire3DCGB_SetPaletteRGB15(w3dcgb_u16 color0, w3dcgb_u16 color1, w3dcgb_u16 color2, w3dcgb_u16 color3)
{
w3dcgb_write_bg_color(0, color0);
w3dcgb_write_bg_color(1, color1);
w3dcgb_write_bg_color(2, color2);
w3dcgb_write_bg_color(3, color3);
w3dcgb_write_bg_color(4, color0);
w3dcgb_write_bg_color(5, color1);
w3dcgb_write_bg_color(6, color1);
w3dcgb_write_bg_color(7, color1);
w3dcgb_write_bg_color(8, color0);
w3dcgb_write_bg_color(9, color2);
w3dcgb_write_bg_color(10, color2);
w3dcgb_write_bg_color(11, color2);
w3dcgb_write_bg_color(12, color0);
w3dcgb_write_bg_color(13, color3);
w3dcgb_write_bg_color(14, color3);
w3dcgb_write_bg_color(15, color3);
}Wire3DCGB_SetScreenOffset
表示のスクロールレジスタSCX・SCYを直接設定します。
書式
void Wire3DCGB_SetScreenOffset(w3dcgb_u8 scx, w3dcgb_u8 scy);引数
scxSCXへ書くスクロール量(ピクセル)。
scySCYへ書くスクロール量(ピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
画面への表示位置だけを動かし、投影座標やカメラは変更しません。VBlankを待たないため、表示中に変更する時期は呼び出し側で選びます。
CGB専用です。通常ステージはWRAMバンク2のD300~DEFFを予約します。共有状態を使うため割り込みから再入しないでください。サンプルは固定スタックと完全版ランタイムを使います。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 赤い枠に青い斜線、緑の点と横線、青いL字、右上に緑の数字0を描きます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_cgb.h:117
kitaqgb/lib/wire3d_cgb.c:1091
void Wire3DCGB_SetScreenOffset(w3dcgb_u8 scx, w3dcgb_u8 scy)
{
w3dcgb_reg_scx = scx;
w3dcgb_reg_scy = scy;
}Wire3Dの描画時間・88行プロファイル・独立した更新時計
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
88行の全面転送はDMGで1,408バイト、CGBで2,816バイトです。DMGの120行比で26.67%、96行比で8.33%のデータ削減ですが、fpsの改善率とは異なります。DMGは列ごとに256バイトの間隔を維持し、CGBは11タイルの列を密に配置します。CGBは全画面モードとの互換性のため3,072バイトのステージ予約を維持します。
ProjectCameraPointはカメラ位置の減算・回転を行わず、カメラ座標の点を投影します。Z=8..255以外では0を返し、出力を変更しません。X/Yを±120に制限し、逆深度表と符号付き積の32による切り捨て除算で画面端へ制限します。これは線の幾何学的クリッピングではありません。汎用CGB ProjectPointの回転による±220制限はゼロ角でも保持します。
DMG EndFrameNowは最初の新しいVBlank待ちを省き、BGキュー、転送モード、dirty履歴を処理します。CGB EndFrameSparseNowも同期処理です。NowでもSTAT待ち、DMA完了待ち、安全な表示切替は必要です。DMGの更新は原子的ではありません。CGBのdirty転送では現在と転送先バンクの2フレーム前の範囲を消去対象に含めます。低レベルCGB線描画はdirty登録が必要です。
CGBの初回sparse表示では、初期化用タイルが残らないよう、各転送先バンクを最初に使う2フレームは全描画範囲を更新します。初回の負荷も測定に含みます。通常のDrawLine2Dはsparse範囲を登録しないため、DrawLineClipped2Dか明示的なMarkDirtyRect2Dを使ってください。
独自の動く箱、バー計器、四音のBGMを使った実行例を用意しました。audio_vblankのVBlank時計から経過tickで移動を更新し、計器は短い安全なVBlank処理で更新します。レンダラーは共有状態を持つためISRから呼びません。wire3d_audio_vblank.cは音声キューを固定WRAMの80バイトへ置き、制御処理をROMバンク5に配置します。通常のaudio_vblank.cと重複してコンパイルしないでください。CGB例はWIRE3DCGB_STAGE_BANK2_ALLOCATIONを使います。8ビットtickの差は255フレーム以内に読み取る必要があります。
計測ツールは描画完了後の転送・表示完了マーカーを数えます。PPU dot(4,194,304/秒、70,224/フレーム)で、クリア、投影、線描画、転送+表示待ち、外側の待ちを記録し、平均・中央値・最大間隔・期限超過数を出します。転送+表示の時間には内部のSTAT/DMA/VBlank待ちが含まれます。CGB倍速CPUのサイクル数をそのままDMGと比較しません。平均fpsの一致だけで処理落ちなしとは判定できません。実機未検証です。
自作例の7構成で各48回、計336回の転送後にステージとVRAMを比較し、84回の空フレームで残像消去を確認しました。投影境界は5構成で計25,700ケースを確認。Rust版でも5構成が48フレーム完走し、BGM出力を確認しています。4 PPUフレームの更新期限を超える場合があり、平均fpsだけで処理落ちなしとは判断できません。エミュレータでの確認で、実機保証ではありません。
KOKURAで自作DMG/CGB例を各400フレーム実行し、SARAKURAの解析と厳密なスキーマ検証を通しました。各構成のエラーは0件、警告は待機ループ検出の1件です。DAC・ミキサー変更時のポップ音リスクなど情報3件も残しています。音声の波形品質や実機での挙動を保証する結果ではありません。
公開例・画像・検証データは独自に作成した図形、計器、音型だけを使用します。市販ゲームのROM、抽出したコード、図形、画面、音楽、状態ファイルは含みません。
| 設定 | fps | 中央値 / 最悪(PPUフレーム) | 期限超過 / 47 |
|---|---|---|---|
clocked_dmg_88_sparse | 13.852 | 4.449 / 4.732 | 35 |
clocked_dmg_96_sparse | 13.186 | 4.669 / 4.891 | 39 |
clocked_dmg_120_sparse | 13.434 | 4.453 / 7.176 | 35 |
clocked_cgb_88_sparse | 15.012 | 4.000 / 5.000 | 28 |
clocked_cgb_96_sparse | 15.092 | 4.000 / 5.000 | 26 |
clocked_cgb_88_full | 12.532 | 5.000 / 6.000 | 41 |
clocked_cgb_96_full | 12.588 | 5.000 / 5.020 | 41 |


Wire3DDMG_ProjectCameraPoint
ProjectCameraPointはカメラ位置の減算・回転を行わず、カメラ座標の点を投影します。Z=8..255以外では0を返し、出力を変更しません。X/Yを±120に制限し、逆深度表と符号付き積の32による切り捨て除算で画面端へ制限します。これは線の幾何学的クリッピングではありません。汎用CGB ProjectPointの回転による±220制限はゼロ角でも保持します。
書式
w3ddmg_u8 Wire3DDMG_ProjectCameraPoint(w3ddmg_i16 x,w3ddmg_i16 y,w3ddmg_i16 z,w3ddmg_u8* sx,w3ddmg_u8* sy);引数
xカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
yカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
zカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
sxカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
syカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
戻り値
1は投影成功、0は深度による拒否です。拒否時にsxとsyは変わりません。
使用条件と制限
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
独自の動く箱、バー計器、四音のBGMを使った実行例を用意しました。audio_vblankのVBlank時計から経過tickで移動を更新し、計器は短い安全なVBlank処理で更新します。レンダラーは共有状態を持つためISRから呼びません。wire3d_audio_vblank.cは音声キューを固定WRAMの80バイトへ置き、制御処理をROMバンク5に配置します。通常のaudio_vblank.cと重複してコンパイルしないでください。CGB例はWIRE3DCGB_STAGE_BANK2_ALLOCATIONを使います。8ビットtickの差は255フレーム以内に読み取る必要があります。
計測ツールは描画完了後の転送・表示完了マーカーを数えます。PPU dot(4,194,304/秒、70,224/フレーム)で、クリア、投影、線描画、転送+表示待ち、外側の待ちを記録し、平均・中央値・最大間隔・期限超過数を出します。転送+表示の時間には内部のSTAT/DMA/VBlank待ちが含まれます。CGB倍速CPUのサイクル数をそのままDMGと比較しません。平均fpsの一致だけで処理落ちなしとは判定できません。実機未検証です。
使用例
#define WIRE3D_DMG_HEIGHT 88
#include "wire3d_dmg.h"
void project_one() {
u8 sx; u8 sy;
if (Wire3DDMG_ProjectCameraPoint(-24, 20, 96, &sx, &sy)) {
// Use the projected coordinates in a main-thread drawing routine.
}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
自作例の7構成で各48回、計336回の転送後にステージとVRAMを比較し、84回の空フレームで残像消去を確認しました。投影境界は5構成で計25,700ケースを確認。Rust版でも5構成が48フレーム完走し、BGM出力を確認しています。4 PPUフレームの更新期限を超える場合があり、平均fpsだけで処理落ちなしとは判断できません。エミュレータでの確認で、実機保証ではありません。
実装とソース
kitaqgb/lib/wire3d_dmg.h:146
Validate depth and project coordinates already in camera space.
kitaqgb/lib/wire3d_dmg.c:720
w3ddmg_u8 Wire3DDMG_ProjectCameraPoint(w3ddmg_i16 x,w3ddmg_i16 y,w3ddmg_i16 z,w3ddmg_u8* sx,w3ddmg_u8* sy)
{
return w3ddmg_project_camera_space(x,y,z,sx,sy);
}Wire3DCGB_ProjectCameraPoint
ProjectCameraPointはカメラ位置の減算・回転を行わず、カメラ座標の点を投影します。Z=8..255以外では0を返し、出力を変更しません。X/Yを±120に制限し、逆深度表と符号付き積の32による切り捨て除算で画面端へ制限します。これは線の幾何学的クリッピングではありません。汎用CGB ProjectPointの回転による±220制限はゼロ角でも保持します。
書式
w3dcgb_u8 Wire3DCGB_ProjectCameraPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);引数
xカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
yカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
zカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
sxカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
syカメラ座標の符号付き16ビットX/Y/Z。sxとsyは書き込み可能なu8への有効なポインタです。
戻り値
1は投影成功、0は深度による拒否です。拒否時にsxとsyは変わりません。
使用条件と制限
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
独自の動く箱、バー計器、四音のBGMを使った実行例を用意しました。audio_vblankのVBlank時計から経過tickで移動を更新し、計器は短い安全なVBlank処理で更新します。レンダラーは共有状態を持つためISRから呼びません。wire3d_audio_vblank.cは音声キューを固定WRAMの80バイトへ置き、制御処理をROMバンク5に配置します。通常のaudio_vblank.cと重複してコンパイルしないでください。CGB例はWIRE3DCGB_STAGE_BANK2_ALLOCATIONを使います。8ビットtickの差は255フレーム以内に読み取る必要があります。
計測ツールは描画完了後の転送・表示完了マーカーを数えます。PPU dot(4,194,304/秒、70,224/フレーム)で、クリア、投影、線描画、転送+表示待ち、外側の待ちを記録し、平均・中央値・最大間隔・期限超過数を出します。転送+表示の時間には内部のSTAT/DMA/VBlank待ちが含まれます。CGB倍速CPUのサイクル数をそのままDMGと比較しません。平均fpsの一致だけで処理落ちなしとは判定できません。実機未検証です。
使用例
#define WIRE3DCGB_HEIGHT 88
#include "wire3d_cgb.h"
void project_one() {
u8 sx; u8 sy;
if (Wire3DCGB_ProjectCameraPoint(-24, 20, 96, &sx, &sy)) {
// Use the projected coordinates in a main-thread drawing routine.
}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
自作例の7構成で各48回、計336回の転送後にステージとVRAMを比較し、84回の空フレームで残像消去を確認しました。投影境界は5構成で計25,700ケースを確認。Rust版でも5構成が48フレーム完走し、BGM出力を確認しています。4 PPUフレームの更新期限を超える場合があり、平均fpsだけで処理落ちなしとは判断できません。エミュレータでの確認で、実機保証ではありません。
実装とソース
kitaqgb/lib/wire3d_cgb.h:168
Project camera-space coordinates without subtracting/rotating the camera. Depth 8..255; outputs unchanged on rejection. Shared scratch: no ISR calls.
kitaqgb/lib/wire3d_cgb.c:552
w3dcgb_u8 Wire3DCGB_ProjectCameraPoint(w3dcgb_i16 x,w3dcgb_i16 y,w3dcgb_i16 z,w3dcgb_u8* sx,w3dcgb_u8* sy)
{
return w3dcgb_project_camera_space(x,y,z,sx,sy);
}Wire3DDMG_EndFrameNow
DMG EndFrameNowは最初の新しいVBlank待ちを省き、BGキュー、転送モード、dirty履歴を処理します。CGB EndFrameSparseNowも同期処理です。NowでもSTAT待ち、DMA完了待ち、安全な表示切替は必要です。DMGの更新は原子的ではありません。CGBのdirty転送では現在と転送先バンクの2フレーム前の範囲を消去対象に含めます。低レベルCGB線描画はdirty登録が必要です。
書式
void Wire3DDMG_EndFrameNow();戻り値
返り値はありません。転送と必要な同期が完了してから戻ります。
使用条件と制限
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
独自の動く箱、バー計器、四音のBGMを使った実行例を用意しました。audio_vblankのVBlank時計から経過tickで移動を更新し、計器は短い安全なVBlank処理で更新します。レンダラーは共有状態を持つためISRから呼びません。wire3d_audio_vblank.cは音声キューを固定WRAMの80バイトへ置き、制御処理をROMバンク5に配置します。通常のaudio_vblank.cと重複してコンパイルしないでください。CGB例はWIRE3DCGB_STAGE_BANK2_ALLOCATIONを使います。8ビットtickの差は255フレーム以内に読み取る必要があります。
計測ツールは描画完了後の転送・表示完了マーカーを数えます。PPU dot(4,194,304/秒、70,224/フレーム)で、クリア、投影、線描画、転送+表示待ち、外側の待ちを記録し、平均・中央値・最大間隔・期限超過数を出します。転送+表示の時間には内部のSTAT/DMA/VBlank待ちが含まれます。CGB倍速CPUのサイクル数をそのままDMGと比較しません。平均fpsの一致だけで処理落ちなしとは判定できません。実機未検証です。
使用例
#define WIRE3D_DMG_HEIGHT 88
#include "wire3d_dmg.h"
void draw_one() {
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,32,32);
Wire3DDMG_PutBgTile(18,16,128);
Wire3DDMG_EndFrameNow();
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
自作例の7構成で各48回、計336回の転送後にステージとVRAMを比較し、84回の空フレームで残像消去を確認しました。投影境界は5構成で計25,700ケースを確認。Rust版でも5構成が48フレーム完走し、BGM出力を確認しています。4 PPUフレームの更新期限を超える場合があり、平均fpsだけで処理落ちなしとは判断できません。エミュレータでの確認で、実機保証ではありません。
実装とソース
kitaqgb/lib/wire3d_dmg.h:289
Same queue/gate/history lifecycle, without the initial fresh-VBlank wait. Synchronous STAT-safe upload; not an atomic swap. LCD must remain enabled.
kitaqgb/lib/wire3d_dmg.c:3826
void Wire3DDMG_EndFrameNow()
{
w3ddmg_flush_bg_queue();
if (w3ddmg_aux_transfer_enabled)
{
w3ddmg_transfer_stage_aux_asm();
}
if (w3ddmg_dirty_transfer_enabled)
{
w3ddmg_transfer_dirty_stage();
}
else
{
w3ddmg_transfer_stage_asm();
}
}wire3d_dmg.h — DMG向けワイヤーフレーム
描画速度を生かす使い方
表示領域内の線は、描画先アドレスとビット位置を逐次更新し、残りの画素数や誤差をCPUのレジスタで管理します。1画素ごとのタイル座標計算を省き、斜線の接続用画素も同じループで描きます。遮蔽を使う場合は、線上の5点をマスクで調べ、描画が必要な線を同じ高速経路へ渡します。マスクの行はバイト単位で埋め、両端の部分的なビットを保持します。面の外接矩形と5点による近似なので、三角形や線を画素ごとに正確に隠す方式ではありません。 0度・90度・180度・270度の回転は、入力の範囲制限を行ってから座標の交換と符号変更で計算します。ゲーム全体の速度にはモデルの頂点数、辺の長さ、遮蔽、VRAM転送も影響します。各関数の所要時間と、1画像を完成させる時間を分けて計測してください。
モノクロの3D線描画です。モデルの頂点・辺・面と物体の位置・回転・倍率を指定し、初期化→フレーム開始→描画→転送の順で使います。物理計算は別途行います。角度は16段階、モデル倍率は256が等倍です。
Vec3はx/y/z、Edgeは頂点番号a/b、Faceは三角形の頂点番号a/b/cを持ちます。Modelのvertices・edges・facesはそれらの配列、各countは要素数です。edge_facesは辺に隣接する面番号f0/f1で、FACE_NONEは面なしを表します。flagsのMODEL_HIDDEN_LINESは面を用いた辺選択を要求します。面の向きは投影後の符号付き面積で判定するため、頂点の並べ方が重要です。
Objectはmodelへの参照と、位置x/y/z・角度rx/ry/rz・倍率scale_q8・表示可否visibleを保持します。モデルの共有は可能ですが、表をコピーしたりROMバンクを自動で切り替えたりはしません。参照先を描画中に読み取れるように配置します。CGBのObjectには色番号colorもあります。
96行設定のModelにはedge_masksとedge_mask_countもあります。物体のY回転に対応する16ビットの辺マスクをRAMへ用意します。使わない場合は両方を0にします。
Wire3DDMG_BeginFrame
次のフレームの描画を開始し、隠線判定の適用を解除します。
書式
void Wire3DDMG_BeginFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
96行設定ではステージとマスクを消去します。120行設定ではピクセルを消去せず、転送時にステージを消費します。未転送の描画や背景キューを破棄する操作ではありません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:144
Disable occlusion filtering only. Stage clearing normally happens during transfer, and Init supplies the first clean stage. This does not discard an unsubmitted frame, clear the mask, or reset pending BG writes/dirty flags.
kitaqgb/lib/wire3d_dmg.c:3379
void Wire3DDMG_BeginFrame()
{
/* The consuming stage transfer clears D000 as it uploads, so BeginFrame only
resets draw state. Wire3DDMG_Init supplies the first clean buffer. */
w3ddmg_occlusion_active = 0;
}Wire3DDMG_DrawIndexedEdges
頂点配列と2バイトずつの辺インデックスから、整数倍率の線モデルを描きます。
書式
void Wire3DDMG_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale);引数
vertices3D頂点の配列。
vertex_count頂点配列の要素数。
edges辺の両端の頂点番号。構造体またはバイト対という宣言の形式に従います。
edge_count辺の本数。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
scale整数の拡大率。0は1として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。頂点24個まで、辺は128組以下にしてください。倍率0は1として扱い、物体回転や面による辺選択は行いません。NULL配列、不正な頂点番号、投影できない端点は無視します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
Wire3DDMG_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) Wire3DDMG_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
Wire3DDMG_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_direct.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:253
Draw byte-pair edge indices after integer scaling and translation; scale zero means one, and there is no object rotation or face filtering. Project at most 24 vertices. Supply at most 128 edge pairs because byte offsets wrap after 255. Invalid vertex IDs/depth-rejected endpoints are skipped; null arrays are ignored. Keep coordinate products/sums representable as s16.
kitaqgb/lib/wire3d_dmg.c:4568
void Wire3DDMG_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
if (vertices == 0) return;
if (edges == 0) return;
count = vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
if (scale == 0) scale = 1;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)(x + (w3ddmg_i16)(v->x * scale));
vy = (w3ddmg_i16)(y + (w3ddmg_i16)(v->y * scale));
vz = (w3ddmg_i16)(z + (w3ddmg_i16)(v->z * scale));
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < edge_count)
{
w3ddmg_u8 a;
w3ddmg_u8 b;
a = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)(i << 1))];
b = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)((i << 1) + 1))];
if ((a < count) && (b < count) &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)b])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)b]);
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3DDMG_DrawLine2D
画面座標の両端を含む、ピクセルが上下左右につながる線をステージへ描きます。
書式
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
端点は表示領域内に指定します。斜線には接続用のピクセルを補います。120行設定でdirty転送を有効にすると外接するタイル範囲を記録します。VRAMへの反映は転送関数で行います。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:237
Mark the bounding tile rectangle when dirty transfer is enabled, then draw a connected staged line. Scene drawing may reject it using five mask samples. Supply 128x120 viewport endpoints; low-level pixel bounds checks do not clip the segment geometrically.
kitaqgb/lib/wire3d_dmg.c:3423
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by)
{
w3ddmg_mark_line_dirty(ax, ay, bx, by);
if (w3ddmg_occlusion_active)
{
w3ddmg_line_stage_masked_c(ax, ay, bx, by);
return;
}
w3ddmg_line_x0 = ax;
w3ddmg_line_y0 = ay;
w3ddmg_line_x1 = bx;
w3ddmg_line_y1 = by;
if (ax < 128 && bx < 128 && ay < WIRE3D_DMG_SCREEN_H && by < WIRE3D_DMG_SCREEN_H)
w3ddmg_line_inside_asm();
else
w3ddmg_line_stage_asm();
}Wire3DDMG_DrawLine3D
世界座標の両端を投影して、1本の線を描きます。
書式
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);引数
ax世界座標の始点のX成分。
ay世界座標の始点のY成分。
az世界座標の始点のZ成分。
bx世界座標の終点のX成分。
by世界座標の終点のY成分。
bz世界座標の終点のZ成分。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
両端が奥行き範囲内の場合だけ描きます。近面・遠面との交点を計算する処理はありません。投影後の端点は個別に画面端へ制限されます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:226
Project both world endpoints and draw only when both pass the depth range. Do not intersect a crossing segment with near/far planes; projected endpoints are independently clamped to the screen edges.
kitaqgb/lib/wire3d_dmg.c:3546
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz)
{
w3ddmg_u8 sx0;
w3ddmg_u8 sy0;
w3ddmg_u8 sx1;
w3ddmg_u8 sy1;
if (w3ddmg_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3ddmg_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DDMG_DrawLine2D(sx0, sy0, sx1, sy1);
}Wire3DDMG_DrawModel
頂点・辺のモデルを、指定位置・回転で等倍描画します。
書式
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256でDrawModelScaledを呼びます。面や隣接情報があるモデルの辺選択にも、その関数の条件が適用されます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3DDMG_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3DDMG_Model cube;
Wire3DDMG_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
Wire3DDMG_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3DDMG_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DDMG_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3DDMG_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;Wire3DDMG_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
Wire3DDMG_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_models.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:164
Draw an unscaled model by delegating to DrawModelScaled with scale 256.
kitaqgb/lib/wire3d_dmg.c:3565
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}kitaqgb/lib/wire3d_dmg.c:3563
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}Wire3DDMG_DrawModelScaled
モデルを拡大縮小し、Y・X・Zの順に回転、平行移動、投影して辺を描きます。
書式
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
倍率256が等倍、0以下も等倍です。頂点は最大24、面は最大16。96行設定は辺も最大16、120行設定はedge_count個を参照します。NULLのモデル・頂点・辺配列は無視します。奥行き範囲をまたぐ辺は切り詰めず省略します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3DDMG_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3DDMG_Model cube;
Wire3DDMG_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
Wire3DDMG_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3DDMG_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DDMG_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3DDMG_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;Wire3DDMG_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
Wire3DDMG_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_models.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:170
Scale up to 24 vertices, rotate Y/X/Z, translate and project, then traverse all edge_count entries (a byte count) with optional adjacent-face filtering. Face scratch is capped at 16; edge tables must contain all declared entries. Nonpositive scale means 256. Null model/vertex/edge pointers are ignored. Keep products/translations in signed 16-bit range; depth-crossing edges are dropped.
kitaqgb/lib/wire3d_dmg.c:3710
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_is_edge_visible(model, i, face_count))
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}kitaqgb/lib/wire3d_dmg.c:3703
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
if (edge_count > WIRE3D_DMG_MODEL_EDGE_LIMIT) edge_count = WIRE3D_DMG_MODEL_EDGE_LIMIT;
w3ddmg_build_edge_flags(model, edge_count, face_count, rx, ry, rz);
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_edge_flags[(__safe_index w3ddmg_u8)i])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3DDMG_DrawScene
最大8個の物体をカメラに近い順に描き、手前の面で後方の辺を隠します。
書式
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);引数
objects位置・回転・倍率・visibleを持つ物体配列。
count渡した配列の要素数。関数固有の処理上限は注意事項を参照してください。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
物体中心の奥行きで順序を決め、面の外接矩形と線上の5点で隠れ方を近似します。ピクセル単位の深度バッファではありません。visibleが有効なモデルを描き、配列自体は並べ替えません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3DDMG_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3DDMG_Model cube;
Wire3DDMG_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
Wire3DDMG_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3DDMG_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DDMG_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3DDMG_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;Wire3DDMG_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
Wire3DDMG_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_models.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:201
Sort at most eight object origins by camera depth, near to far. Draw visible valid models and mark front-face bounding rectangles for later objects. The first sorted slot bypasses mask tests and the last needs no new occluder. This is coarse line rejection, not a depth buffer. Null/empty input is ignored; caller data and the current projection cache must remain valid during traversal.
kitaqgb/lib/wire3d_dmg.c:3461
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count)
{
w3ddmg_u8 scene_count;
w3ddmg_u8 i;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3D_DMG_SCENE_OBJECT_LIMIT) scene_count = WIRE3D_DMG_SCENE_OBJECT_LIMIT;
if (scene_count == 0) return;
i = 0;
while (i < scene_count)
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = i;
w3ddmg_scene_depth[(__safe_index w3ddmg_u8)i] = w3ddmg_scene_object_depth(&objects[(__safe_index w3ddmg_u8)i]);
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3ddmg_u8 j;
j = (w3ddmg_u8)(i + 1);
while (j < scene_count)
{
w3ddmg_u8 oi;
w3ddmg_u8 oj;
oi = w3ddmg_scene_order[(__safe_index w3ddmg_u8)i];
oj = w3ddmg_scene_order[(__safe_index w3ddmg_u8)j];
if (w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oj] < w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oi])
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = oj;
w3ddmg_scene_order[(__safe_index w3ddmg_u8)j] = oi;
}
j = (w3ddmg_u8)(j + 1);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 0;
i = 0;
while (i < scene_count)
{
Wire3DDMG_Object* obj;
obj = &objects[(__safe_index w3ddmg_u8)w3ddmg_scene_order[(__safe_index w3ddmg_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
w3ddmg_occlusion_active = i == 0 ? 0 : 1;
Wire3DDMG_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, obj->rx, obj->ry, obj->rz, obj->scale_q8);
if ((w3ddmg_u8)(i + 1) < scene_count)
{
w3ddmg_mark_model_occluder(obj->model);
}
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_occlusion_active = 0;
}Wire3DDMG_EndFrame
背景予約とステージの描画をVRAMへ反映します。
書式
void Wire3DDMG_EndFrame();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
次のVBlankを待って背景予約を反映します。120行設定では有効な補助転送を先に行い、続いてメインを全体またはdirty転送します。ステージは転送で消費します。STAT待ちにより複数の表示期間にまたがる場合があります。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:286
Wait for the next VBlank and flush BG writes, then optionally consume the auxiliary strips before uploading either dirty tiles or the entire main stage. Auxiliary/main source overlap makes that ordering significant. STAT polling continues across access windows; the update need not fit one VBlank.
kitaqgb/lib/wire3d_dmg.c:3819
void Wire3DDMG_EndFrame()
{
w3ddmg_wait_vblank_start();
Wire3DDMG_EndFrameNow();
}Wire3DDMG_EraseModelFaces
モデルの手前向きの三角形面を投影し、ステージ上の線を面の内側から消します。
書式
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
scale_q8256を等倍とする拡大率。0以下も等倍として扱います。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
隠線フラグと面データが必要です。頂点24・面16まで処理し、投影できない面は省略します。倍率0以下は256です。投影用の共有作業領域を書き換えますが、画面転送は行いません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3DDMG_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3DDMG_Face faces[2]={{0,2,1},{0,3,2}};
Wire3DDMG_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
Wire3DDMG_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3DDMG_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) Wire3DDMG_DrawLine2D(40,i,88,i);
Wire3DDMG_EraseTriangle2D(12,12,28,12,12,28);
Wire3DDMG_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3DDMG_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_faces.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:179
Project up to 24 vertices and erase front-facing triangle interiors from the stage for a model with hidden-line flags and face data. Nonpositive scale means 256 (unity). Up to 16 faces are considered; depth-crossing faces are skipped. This also replaces shared projection caches, but does not update VRAM.
kitaqgb/lib/wire3d_dmg.c:3581
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}kitaqgb/lib/wire3d_dmg.c:3579
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3DDMG_EraseSpan2D
指定した1行の両端を含む横区間を消去します。
書式
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);引数
y消去する行のY座標(ピクセル)。
x0最小側のX端点(表示領域内のピクセル)。
x1最大側のX端点(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
Xの左右は自動で並べ替えますが、両端を0~127に収めてください。範囲外のYは無視します。Xのクリッピングは行いません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:187
Order the X endpoints and erase the inclusive staged span. Both endpoints must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
kitaqgb/lib/wire3d_dmg.c:3681
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1)
{
w3ddmg_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
w3ddmg_stage_clear_span(y, x0, x1);
}Wire3DDMG_EraseTriangle2D
画面座標で指定した三角形の内部を、行ごとの塗りつぶしで消去します。
書式
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);引数
ax頂点AのX座標(表示領域内のピクセル)。
ay頂点AのY座標(表示領域内のピクセル)。
bx頂点BのX座標(表示領域内のピクセル)。
by頂点BのY座標(表示領域内のピクセル)。
cx頂点CのX座標(表示領域内のピクセル)。
cy頂点CのY座標(表示領域内のピクセル)。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
表示領域内の頂点を指定します。面積0の三角形は無視します。マスクや表示済みVRAMは変更しません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3DDMG_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3DDMG_Face faces[2]={{0,2,1},{0,3,2}};
Wire3DDMG_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
Wire3DDMG_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3DDMG_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) Wire3DDMG_DrawLine2D(40,i,88,i);
Wire3DDMG_EraseTriangle2D(12,12,28,12,12,28);
Wire3DDMG_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3DDMG_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
塗った領域から投影した面を消し、線が残る場所と消える場所を確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_faces.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:184
Erase a triangle from staged pixels using integer scanline intersections. Supply viewport coordinates. Degenerate triangles are ignored; the occlusion mask and VRAM are unchanged.
kitaqgb/lib/wire3d_dmg.c:3671
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy)
{
w3ddmg_clear_triangle(ax, ay, bx, by, cx, cy);
}Wire3DDMG_Init
LCD・タイル・背景マップを初期化し、モノクロのワイヤーフレーム表示領域を用意します。
書式
void Wire3DDMG_Init();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
カメラ、背景書き込みキュー、ステージをリセットし、BGPを0xB4に設定します。描画前にBeginFrameを呼びます。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:135
Take over LCD state, clear tile VRAM and the BG map, and install a 16x15 viewport beginning at map tile (3,1). Set scroll (8,0), BGP=0xB4 and BG display. Reset camera, queues, transfer modes, both dirty tables and stage. Reserve 0xD000..0xDFFF and retain its WRAM mapping; auxiliary storage aliases it. Call BeginFrame before drawing to establish occlusion state.
kitaqgb/lib/wire3d_dmg.c:3310
void Wire3DDMG_Init()
{
w3ddmg_u8 row;
w3ddmg_u8 col;
w3ddmg_u8 tid;
w3ddmg_u16 off;
w3ddmg_wait_vblank_start();
w3ddmg_reg_lcdc = 0;
w3ddmg_clear_vram_asm();
w3ddmg_fill_bg_map_asm();
row = 0;
tid = 0x90;
while (row < WIRE3D_DMG_TILE_H)
{
off = (w3ddmg_u16)(0x23 + ((w3ddmg_u16)row << 5));
col = 0;
while (col < WIRE3D_DMG_TILE_W)
{
w3ddmg_bg_map_9800[(w3ddmg_u16)(off + (w3ddmg_u16)col)] = tid;
tid = (w3ddmg_u8)(tid + WIRE3D_DMG_TILE_H);
col = (w3ddmg_u8)(col + 1);
}
row = (w3ddmg_u8)(row + 1);
tid = (w3ddmg_u8)(0x90 + row);
}
w3ddmg_reg_scx = 8;
w3ddmg_reg_scy = 0;
w3ddmg_reg_bgp = 0xB4;
w3ddmg_reg_lcdc = 0x81;
w3ddmg_bgq_count = 0;
w3ddmg_aux_transfer_enabled = 0;
w3ddmg_dirty_transfer_enabled = 0;
Wire3DDMG_SetCamera((w3ddmg_i16)0, (w3ddmg_i16)0, (w3ddmg_i16)0, (w3ddmg_u8)0, (w3ddmg_u8)0, (w3ddmg_u8)0);
w3ddmg_clear_stage_asm();
w3ddmg_clear_stage_aux_asm();
w3ddmg_clear_dirty_tables();
}Wire3DDMG_ProjectPoint
世界座標の点をカメラから見た画面座標へ変換します。
書式
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);引数
x世界座標での点または物体中心のX成分。
y世界座標での点または物体中心のY成分。
z世界座標での点または物体中心のZ成分。
sx投影結果のX座標を書き込む、有効なu8領域へのポインタ。
sy投影結果のY座標を書き込む、有効なu8領域へのポインタ。
戻り値
投影成功時は1、深度で拒否した場合は0です。
使用条件と制限
奥行き8~255だけを受理します。範囲外なら0を返し、出力値は保持します。成功時は画面端に制限した座標を書き込みます。座標制限は線の幾何学的クリッピングではありません。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:216
Project a world point with the current camera. Return zero for invalid depth without changing outputs; success writes clamped 128x120 coordinates. Both output pointers must be valid. Coordinate differences must fit s16.
kitaqgb/lib/wire3d_dmg.c:688
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy)
{
return w3ddmg_project_world(x, y, z, sx, sy);
}Wire3DDMG_PutBgTile
背景マップの1セルへのタイル番号書き込みを予約します。
書式
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);引数
x32×32背景マップ内のタイル単位のX座標。
y32×32背景マップ内のタイル単位のY座標。
tile背景セルに置く8ビットのタイル番号。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
座標は32×32マップのタイル単位です。48件まで予約でき、範囲外と超過分は無視します。EndFrameで反映する専用キューであり、一般のvramライブラリのキューとは別です。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:258
Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map coordinates and submissions beyond 48 pending writes are silently ignored. This queue is separate from the general vram library queue.
kitaqgb/lib/wire3d_dmg.c:731
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3ddmg_bgq_count >= WIRE3D_DMG_BG_QUEUE_LIMIT) return;
w3ddmg_bgq_x[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = x;
w3ddmg_bgq_y[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = y;
w3ddmg_bgq_tile[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = tile;
w3ddmg_bgq_count = (w3ddmg_u8)(w3ddmg_bgq_count + 1);
}Wire3DDMG_RotatePoint
1点のX・Y・Zを、指定した回転で直接書き換えます。
書式
void Wire3DDMG_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);引数
xX成分を読み書きする独立したs16領域へのポインタ。
yY成分を読み書きする独立したs16領域へのポインタ。
zZ成分を読み書きする独立したs16領域へのポインタ。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定で使います。16段階の角度でY・X・Zの順に回転し、各軸の回転前に成分を±220へ制限します。いずれかのポインタがNULLなら何もせず、各成分には別々の領域を指定します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3DDMG_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3DDMG_Model cube;
Wire3DDMG_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
Wire3DDMG_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3DDMG_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DDMG_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3DDMG_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;Wire3DDMG_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
Wire3DDMG_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_models.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:221
Rotate in place in Y/X/Z order using 16 steps per full turn. A null component pointer makes the whole call a no-op; use distinct pointers. Each axis rotation clamps its inputs to +/-220.
kitaqgb/lib/wire3d_dmg.c:3865
void Wire3DDMG_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
if (x == 0) return;
if (y == 0) return;
if (z == 0) return;
w3ddmg_rotate_y(x, z, ry);
w3ddmg_rotate_x(y, z, rx);
w3ddmg_rotate_z(x, y, rz);
}Wire3DDMG_SelectEdgeMask
物体のY回転に対応する、事前計算済みの辺表示マスクを選びます。
書式
w3ddmg_u16 Wire3DDMG_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);引数
model頂点・辺・面の配列と個数を持つモデル。表は呼び出し中に読み取れる領域に置きます。
rxX軸の回転。16段階で1周します。
ryY軸の回転。16段階で1周します。
rzZ軸の回転。16段階で1周します。
戻り値
表示する辺に対応する16ビットマスク。表がなければ0xFFFFです。
使用条件と制限
96行設定で使います。Y回転の下位4ビットを16・8・4・2区分へ割り当て、その他の正の個数では先頭を使います。X/Z回転とカメラは無視します。表はRAMに置き、未指定なら0xFFFFを返します。
このサンプルは128×96設定です。画面左上が表示領域の原点です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3DDMG_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3DDMG_Model cube;
Wire3DDMG_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3DDMG_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3DDMG_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DDMG_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3DDMG_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3DDMG_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3DDMG_SelectEdgeMask(&cube,0,8,0);
Wire3DDMG_EndFrame(); result[7]=0xA55A;
while(1) {}
}期待する結果
上段に等倍と半分のモデル、下段に同じ倍率の物体配列を描き、投影と倍率の関係を比較します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg96_models.c .\kitaqgb\lib\wire3d_dmg_96.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg96_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:207
Select a precomputed mask from the low four bits of object yaw; pitch, roll and camera orientation are ignored. Use 16, 8, 4 or 2 bins; other positive counts select entry zero (counts >=16 use the first 16). Missing data returns 0xFFFF. Keep the mask table readable through the current data-pointer mapping.
kitaqgb/lib/wire3d_dmg.c:1258
w3ddmg_u16 Wire3DDMG_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
w3ddmg_u8 count;
w3ddmg_u8 bin;
if (model == 0) return 0xFFFF;
if (model->edge_masks == 0) return 0xFFFF;
count = model->edge_mask_count;
if (count == 0) return 0xFFFF;
(void)rx;
(void)rz;
bin = (w3ddmg_u8)(((w3ddmg_u8)ry) & 15);
if (count >= 16) return model->edge_masks[(__safe_index w3ddmg_u8)bin];
if (count == 8) bin = (w3ddmg_u8)(bin >> 1);
else if (count == 4) bin = (w3ddmg_u8)(bin >> 2);
else if (count == 2) bin = (w3ddmg_u8)(bin >> 3);
else bin = 0;
if (bin >= count) bin = (w3ddmg_u8)(count - 1);
return model->edge_masks[(__safe_index w3ddmg_u8)bin];
}Wire3DDMG_SetAuxTransfer
EndFrameで補助領域も転送するかどうかを設定します。
書式
void Wire3DDMG_SetAuxTransfer(w3ddmg_u8 flag);引数
flag0で転送機能を無効、それ以外で有効にします。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。0で無効、それ以外で有効です。この呼び出しだけでは転送・消去を行いません。補助転送の元データはメインステージと重なります。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Use the auxiliary-transfer gate to consume an overlapping stage strip.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetDirtyTransfer(1); Wire3DDMG_SetAuxTransfer(1);
Wire3DDMG_BeginFrame();
for(i=24;i<=31;i++) Wire3DDMG_DrawLine2D(40,i,47,i);
// Auxiliary upload consumes this source before the dirty main tile is sent.
Wire3DDMG_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
メインステージの一部を補助転送へ渡し、画面座標(80,24)に8×8の黒いブロックを表示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_aux_gate.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_aux_gate.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:268
Store the EndFrame auxiliary-transfer gate; any nonzero value enables it. No data is copied or cleared here. Auxiliary source bytes alias the main stage.
kitaqgb/lib/wire3d_dmg.c:4639
void Wire3DDMG_SetAuxTransfer(w3ddmg_u8 flag)
{
w3ddmg_aux_transfer_enabled = flag;
}Wire3DDMG_SetCamera
後続の投影に使うカメラ位置と姿勢を保存します。
書式
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll);引数
x世界座標でのカメラのX成分。
y世界座標でのカメラのY成分。
z世界座標でのカメラのZ成分。
pitchX軸の回転。16段階で1周します。
yawY軸の回転。16段階で1周します。
rollZ軸の回転。16段階で1周します。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
角度は16段階で1周します。既に描いた線は移動しません。座標差をs16の範囲に収めてください。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:152
Store camera position and angles for later draws; a full turn is 16 angle steps. No existing drawing is reprojected and no input range validation occurs.
kitaqgb/lib/wire3d_dmg.c:3399
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}kitaqgb/lib/wire3d_dmg.c:3397
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}Wire3DDMG_SetDirtyTransfer
120行表示のメイン転送を、変更タイル中心の転送へ切り替えます。
書式
void Wire3DDMG_SetDirtyTransfer(w3ddmg_u8 flag);引数
flag0で転送機能を無効、それ以外で有効にします。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
値が同じでも現在・過去のdirty記録とステージを消去します。表示中のVRAMは保持します。古い画像が残る状態で有効化すると未登録のタイルが残り得るため、初期化直後など白紙の画面で設定します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Remove the previous frame and upload only the tracked current/previous tiles.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetDirtyTransfer(1); Wire3DDMG_SetAuxTransfer(0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,24,8); Wire3DDMG_DrawLine2D(24,8,24,24);
Wire3DDMG_DrawLine2D(24,24,8,24); Wire3DDMG_DrawLine2D(8,24,8,8);
Wire3DDMG_EndFrame();
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(80,80,104,80); Wire3DDMG_DrawLine2D(104,80,104,104);
Wire3DDMG_DrawLine2D(104,104,80,104); Wire3DDMG_DrawLine2D(80,104,80,80);
Wire3DDMG_EndFrame(); result[7]=0xA55A; while(1) {}
}期待する結果
左上の古い枠を消し、画面座標(96,88)~(120,112)に新しい枠だけを表示します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_dirty.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_dirty.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:272
Store the dirty-transfer gate and clear both dirty histories and the main stage, even if the value is unchanged. VRAM is retained; enabling this over an old image can leave unmarked old tiles, so arrange a clean displayed baseline.
kitaqgb/lib/wire3d_dmg.c:4699
void Wire3DDMG_SetDirtyTransfer(w3ddmg_u8 flag)
{
w3ddmg_dirty_transfer_enabled = flag;
w3ddmg_clear_dirty_tables();
w3ddmg_clear_stage_asm();
}Wire3DDMG_SetPalette
モノクロ背景パレットBGPの4階調の割り当てを直接変更します。
書式
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp);引数
bgp4色の階調を2ビットずつ並べたBGPレジスタ値。
戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
2ビットずつのハードウェア形式をそのまま渡します。VBlank待ちは行いません。CGBのRGBパレットは別途設定します。サンプルは0xFCで白地に黒線を表示します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_SetCamera(8,0,0,0,0,0);
result[1]=Wire3DDMG_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3DDMG_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3DDMG_SetCamera(0,0,0,0,0,0);
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawLine2D(8,8,56,8);
Wire3DDMG_DrawLine2D(56,8,56,40);
Wire3DDMG_DrawLine2D(56,40,8,40);
Wire3DDMG_DrawLine2D(8,40,8,8);
Wire3DDMG_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3DDMG_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3DDMG_PutBgTile(1,1,160);
Wire3DDMG_EndFrame();
result[7]=0xA55A;
while(1) {}
}期待する結果
基本の線・点・消去・座標変換を1画面で確認するサンプルです。 左上の枠の上辺の中央が消え、斜線が中央に投影されます。出力拒否時の座標が91・92のままになることも調べます。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_raster.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:260
Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
kitaqgb/lib/wire3d_dmg.c:746
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp)
{
w3ddmg_reg_bgp = bgp;
}Wire3DDMG_TransferAuxNow
補助用の6本のストリップを直ちに転送し、元データを消去します。
書式
void Wire3DDMG_TransferAuxNow();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。補助転送の有効設定にかかわらず実行し、VBlank開始・dirty記録・背景予約を扱いません。元領域D518+256×列から56バイトずつを読みます。メインステージと重なるため転送順に注意します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
Wire3DDMG_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) Wire3DDMG_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
Wire3DDMG_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_direct.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:280
Upload and consume auxiliary source strips immediately, independent of the auxiliary gate. No VBlank-start wait or dirty-history/BG-queue update occurs. The source overlaps the main stage and must be coordinated with its upload.
kitaqgb/lib/wire3d_dmg.c:4727
void Wire3DDMG_TransferAuxNow()
{
w3ddmg_transfer_stage_aux_asm();
}Wire3DDMG_TransferMainNow
メインステージ全体を直ちに転送し、読み出したステージを消去します。
書式
void Wire3DDMG_TransferMainNow();戻り値
戻り値はありません。作用する状態と画面への反映時期は上記の通りです。
使用条件と制限
120行設定用です。STATでVRAMアクセスを待ちますが、次のVBlank開始は待ちません。転送設定に従わず、dirty記録や背景予約を変更しません。呼び出し側で表示周期を管理します。
このサンプルは128×120設定です。表示領域の原点は画面座標(16,8)です。WRAM D000~DFFFを予約し、CGBで使う場合もそのバンク対応を保持します。共有状態を使い、再入はできません。
WIRE3D_DMG_HEIGHTは既定120です。96行設定はwire3d_dmg_96.cと組み合わせます。SelectEdgeMaskは96行、RotatePoint・DrawIndexedEdges・転送設定・直接転送は120行で提供します。
DMGはWIRE3D_DMG_HEIGHTを88・96・120から選択できます。CGBはWIRE3DCGB_HEIGHTを88・96から選択します。既定値はDMG 120、CGB 96です。88行は128×88、中心Y=44です。ライブラリと呼び出し側で同じ設定を使い、wire3d_dmg_88.c / wire3d_cgb_88.cを通常のエントリの代わりにコンパイルします。DMGの88行は96行と同じモデル構造・16辺制限です。CGBの160×144モードは変わりません。
使用例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "wire3d_dmg.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3DDMG_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DDMG_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3DDMG_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3DDMG_BeginFrame();
Wire3DDMG_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
Wire3DDMG_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) Wire3DDMG_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
Wire3DDMG_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}期待する結果
投影した横線と補助転送の8×8ブロックを表示し、ステージの元バイトが255から0へ消費されることを確認します。
ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg120_direct.c .\kitaqgb\lib\wire3d_dmg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg120_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm実装とソース
kitaqgb/lib/wire3d_dmg.h:276
Upload and consume the full main stage immediately, polling STAT but not waiting for the next VBlank. Ignore transfer gates and leave dirty histories and BG tile queue unchanged; the caller coordinates the frame lifecycle.
kitaqgb/lib/wire3d_dmg.c:4714
void Wire3DDMG_TransferMainNow()
{
w3ddmg_transfer_stage_asm();
}ROMの正弦波テーブル:math.c
math.cは256個のROMデータMATH_SINを用意します。値は128 + round(127*sin(2*pi*phase/256))で、添字0~255を1周期として使います。角度0・64・128・192の値は128・255・128・1です。
使うソースで#include "math.c"を一度だけ行います。u8 phaseを増やすと255の次は0に戻ります。たとえばs16 offset = (s16)MATH_SIN[phase] - 128;は、左右や上下へ揺らすための−127~127の変位を得る例です。必要な振幅へ縮小してから座標へ加え、位相はゲームのフレーム更新に合わせて進めます。ROMバンクを切り替える場合はテーブルが見える配置を保ってください。
構造体・定数・ヘッダー全文
関数形式でないマクロ、構造体、配列上限、enum、別名はここで確認できます。元のコメントを含む採取時点のヘッダー全文です。
asset.h — 素材IDとデータ記述表
#pragma once
#define ASSET_TYPE_RAW ((u8)0)
#define ASSET_TYPE_TILES ((u8)1)
#define ASSET_TYPE_MAP ((u8)2)
#define ASSET_TYPE_SONG ((u8)3)
// Packed ROM descriptor: type tag, payload bank, bank-window address and byte length.
// The table itself may live in a different bank from the payload.
typedef __packed struct {
u8 type;
u8 bank;
const u8* ptr;
u16 len;
} AssetDesc;
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);
void __vram_copy(u16 dst, const void* src, u16 len);
// Retain the descriptor table address, bank and count; the table is not copied.
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);
// Copy one descriptor from its ROM bank into caller storage. Return zero for a
// null destination or an out-of-range ID; table validity is the caller's responsibility.
u8 asset_get(u8 asset_id, AssetDesc* out_desc);
// Copy at most max_len bytes using the asset's bank. Success means the ID was
// valid, even when the payload was truncated to fit the supplied limit.
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);
// Accept tile/raw descriptors and transfer their bytes to VRAM. This path passes
// the descriptor pointer directly; callers must arrange the correct visible ROM bank.
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);
// Return the descriptor bank, or zero on lookup failure; zero can also be a valid bank.
u8 asset_get_bank(u8 asset_id);
// Return the stored address without switching banks. Use the descriptor bank
// with far-data helpers before dereferencing banked assets; failure returns null.
const u8* asset_get_ptr(u8 asset_id);
// Return the descriptor byte length, or zero if its ID cannot be resolved.
u16 asset_get_len(u8 asset_id);kitaqgb/lib/asset.h
audio.h — 音楽と効果音・音源制御
#pragma once
// Shared Game Boy audio driver for KITAQGB projects.
//
// Integration notes:
// - Compile a hardware-register file before audio.c so NR10..NR52 and
// WAVE0..WAVE15 are already declared.
// - Call Audio_Init() once during startup.
// - Call Audio_Update() once per frame, ideally from your VBlank-driven main loop.
// - audio.c is intended to stay in bank 0.
// - Music streams are banked explicitly with Audio_PlayMusic(bank, song).
// - Audio_PlaySFX(sfx, priority) captures the currently visible ROM bank.
// - Audio_PlaySFXBanked(bank, sfx, priority) safely classifies CH1/CH3 data
// after switching to the requested bank, so banked SFX can be started from
// any currently visible ROM bank.
// - GBDAW-Stream v1.2 extends the legacy command set with explicit per-channel
// automation commands that map more directly from GUI and Python tooling.
// Physical APIs and music-stream commands share zero-based CH1..CH4 IDs.
#define AUDIO_CHANNEL_CH1 ((u8)0)
#define AUDIO_CHANNEL_CH2 ((u8)1)
#define AUDIO_CHANNEL_CH3 ((u8)2)
#define AUDIO_CHANNEL_CH4 ((u8)3)
// Music-stream IDs used by NOTE, SET_INST and STOP_CHANNEL.
#define AUDIO_STREAM_CHANNEL_CH1 AUDIO_CHANNEL_CH1
#define AUDIO_STREAM_CHANNEL_CH2 AUDIO_CHANNEL_CH2
#define AUDIO_STREAM_CHANNEL_CH3 AUDIO_CHANNEL_CH3
#define AUDIO_STREAM_CHANNEL_CH4 AUDIO_CHANNEL_CH4
#define AUDIO_PAN_CENTER ((u8)0)
#define AUDIO_PAN_LEFT ((u8)1)
#define AUDIO_PAN_RIGHT ((u8)2)
#define AUDIO_PAN_MUTE ((u8)3)
#define AUDIO_PAN_INHERIT ((u8)0xFF)
#define AUDIO_WAVE_TRIANGLE ((u8)0)
#define AUDIO_WAVE_SAW ((u8)1)
#define AUDIO_WAVE_VOWEL_A ((u8)2)
#define AUDIO_WAVE_VOWEL_I ((u8)3)
#define AUDIO_WAVE_VOWEL_U ((u8)4)
#define AUDIO_WAVE_VOWEL_E ((u8)5)
#define AUDIO_WAVE_VOWEL_O ((u8)6)
#define AUDIO_WAVE_CUSTOM ((u8)7)
#define AUDIO_CH3_LEVEL_MUTE ((u8)0x00)
#define AUDIO_CH3_LEVEL_100 ((u8)0x20)
#define AUDIO_CH3_LEVEL_50 ((u8)0x40)
#define AUDIO_CH3_LEVEL_25 ((u8)0x60)
#define AUDIO_VOLUME_MIN ((u8)0)
#define AUDIO_VOLUME_MAX ((u8)7)
#define AUDIO_NOTE_MAX ((u8)67)
#define AUDIO_CMD_WAIT ((u8)0x00)
#define AUDIO_CMD_NOTE ((u8)0x10)
#define AUDIO_CMD_SET_INST ((u8)0x11)
#define AUDIO_CMD_SET_PAN ((u8)0x12)
#define AUDIO_CMD_SET_SWEEP ((u8)0x13)
#define AUDIO_CMD_SET_WAVE ((u8)0x14)
#define AUDIO_CMD_SET_CH3_CUSTOM_WAVE ((u8)0x15)
#define AUDIO_CMD_SET_CH4_PARAM ((u8)0x16)
#define AUDIO_CMD_SET_MASTER_VOLUME ((u8)0x17)
#define AUDIO_CMD_SET_CH1_DUTY ((u8)0x18)
#define AUDIO_CMD_SET_CH2_DUTY ((u8)0x19)
#define AUDIO_CMD_SET_CH3_LEVEL ((u8)0x1A)
#define AUDIO_CMD_STOP_CHANNEL ((u8)0x20)
#define AUDIO_CMD_LOOP ((u8)0x30)
#define AUDIO_CMD_STOP ((u8)0x40)
#define AUDIO_SFX_CH3_MARKER ((u8)0xFF)
// Enable the APU and reset software streams, instruments, mix and fade state.
// Load the initial triangle wave, then leave every channel silent. Call at startup
// before concurrent interrupt-driven audio service can access these shared fields.
void Audio_Init();
// Advance the master fade first. Unless paused, service both effect streams
// then the optional legacy music decoder. Call once per frame for frame-based timing.
void Audio_Update();
// Retain the bank and song pointer, reset legacy instruments/cursors, and arm
// playback from the beginning. Supply a valid, persistent song; no bytes are copied
// and null is not checked. Existing effect streams remain active.
void __stackcall Audio_PlayMusic(u8 bank, u8 *song);
// Stop legacy music and clear its resume mask, then silence all physical channels.
// Effect pointers remain active and may produce sound on a later update.
void Audio_StopMusic();
// On pause entry, silence hardware while retaining cursors. On resume, retrigger
// eligible legacy CH1, CH2 and CH4 notes; CH3 is not retriggered here. Updates still
// advance master fades while paused, and effect playback resumes on a later update.
void __stackcall Audio_SetPaused(u8 on);
// Capture the currently mapped ROM bank and start an effect with inherited pan.
// Use the explicit-bank API when the pointer belongs to another bank.
void __stackcall Audio_PlaySFX(u8 *sfx, u8 priority);
// Start a banked effect with the channel's base pan, subject to priority arbitration.
void __stackcall Audio_PlaySFXBanked(u8 bank, u8 *sfx, u8 priority);
// Capture the currently mapped ROM bank and request an effect with a pan override.
// Use the explicit-bank API when the pointer belongs to another bank.
void __stackcall Audio_PlaySFXPanned(u8 *sfx, u8 priority, u8 pan);
// Retain a banked effect stream if its priority equals or exceeds the current
// owner's priority, or that slot is empty. A leading CH3 marker selects the wave
// slot and is skipped; other streams use the pulse slot. Paused requests are ignored.
// Keep stream storage valid until completion; acceptance does not trigger a note.
void __stackcall Audio_PlaySFXPannedBanked(u8 bank, u8 *sfx, u8 priority, u8 pan);
// Clear both effect streams and their pan overrides. When not paused, release
// borrowed channels through the configured legacy or VBlank recovery path.
void Audio_StopSfx();
// Stop a physical channel and clear its legacy resume bit. CH1 also clears the
// pulse effect stream; CH3 clears the wave effect stream. This is the legacy
// channel API and does not clear a VBlank driver's independently latched notes.
void __stackcall Audio_StopChannel(u8 ch);
// Set the music-processing gate. This does not silence notes already playing
// or stop the legacy stream cursor from consuming commands.
void __stackcall Audio_SetMusicEnabled(u8 on);
// Enable effect note writes, or disable them and release both effect streams.
// Disabling follows Audio_StopSfx recovery rules rather than silencing all channels.
void __stackcall Audio_SetSfxEnabled(u8 on);
// Clamp both output levels to 0..7, cancel any fade, and write NR50 immediately.
void __stackcall Audio_SetMasterVolume(u8 left, u8 right);
// Move left/right volume toward clamped targets at the requested Audio_Update
// call interval. An interval of zero applies the levels immediately.
void __stackcall Audio_FadeToMasterVolume(u8 left, u8 right, u8 step_frames);
// Cancel further volume steps, retaining the current cached levels and hardware output.
void Audio_CancelMasterVolumeFade();
// Update the base pan for a physical channel and reapply active effect overrides.
// Unknown channel IDs select CH4; unsupported pan values mute the selected channel.
void __stackcall Audio_SetPan(u8 ch, u8 pan);
// Two bits per channel (CH1 in bits 0-1): center, left, right, mute.
// Applies all four positions with one NR51 update for sequencer automation.
void __stackcall Audio_SetPanPacked(u8 packed);
// Install a named preset or the cached custom wave. Unknown IDs are ignored.
// This does not update Audio_Ch3Wave or trigger a new note.
void __stackcall Audio_LoadWave(u8 wave_id);
// Copy 16 packed sample bytes into the persistent custom cache and wave RAM.
// A null pointer is ignored; the source is needed only for this call.
void __stackcall Audio_LoadCustomWave(const u8 *wave16);
// Cache the low two duty bits for the next CH1 note; do not change the active register.
void __stackcall Audio_SetCh1Duty(u8 duty);
// Cache the low two duty bits for the next CH2 note; do not change the active register.
void __stackcall Audio_SetCh2Duty(u8 duty);
// Cache the raw NR32 level encoding for the next CH3 note without writing hardware.
void __stackcall Audio_SetCh3Level(u8 level);
// Cache the raw noise polynomial encoding for the next CH4 note without writing hardware.
void __stackcall Audio_SetCh4Param(u8 param);
// Trigger CH1 with the cached sweep and the supplied raw envelope. Clamp the
// note index; duty values 0, 1 and 3 select their encodings, and all others use 2.
void __stackcall Audio_Ch1NoteOn(u8 note, u8 vol_env, u8 duty2);
// Trigger CH2 with the supplied raw envelope and a clamped note index. Duty
// values 0, 1 and 3 select their encodings; all other values use duty 2.
void __stackcall Audio_Ch2NoteOn(u8 note, u8 vol_env, u8 duty2);
// Clamp the note index, program the raw NR32 output level, and trigger CH3.
// Wave RAM must already contain the desired waveform; this does not load one.
void __stackcall Audio_Ch3NoteOn(u8 note, u8 level);
// Cache the supplied noise parameter, program the raw envelope and polynomial
// registers, then trigger CH4 with the length-enable bit clear.
void __stackcall Audio_Ch4NoteOn(u8 param, u8 vol_env);kitaqgb/lib/audio.h
audio_vblank.h — VBlank IRQによる音楽再生
#pragma once
// Fixed-bank VBlank BGM driver.
//
// Stream format:
// delay, ch1_note, ch2_note, ch3_note, ch4_noise_param
// IMMEDIATE, ch1_note, ch2_note, ch3_note, ch4_noise_param
// CONTROL, command, arg0, arg1, arg2
//
// Notes are 0..67. To stay inside the useful CGB register range, C6..G6
// (60..67) alias C5..G5 (48..55) in the fixed pitch table. REST leaves a
// channel unchanged, while STOP silences it. END stops either playback mode.
// LOOP jumps to MusicLoopPoint only in direct-pointer mode; a queue producer
// must expand loops itself. IMMEDIATE is recognized only in queue mode.
// Direct streams treat 0xFB as an ordinary delay, and queues treat 0xFE as one.
#define AUDIO_VBLANK_REST ((u8)0xFF)
#define AUDIO_VBLANK_END ((u8)0xFF)
#define AUDIO_VBLANK_LOOP ((u8)0xFE)
#define AUDIO_VBLANK_STOP ((u8)0xFD)
#define AUDIO_VBLANK_CONTROL ((u8)0xFC)
#define AUDIO_VBLANK_IMMEDIATE ((u8)0xFB)
#define AUDIO_VBLANK_CONTROL_RESET ((u8)0x01)
#define AUDIO_VBLANK_W8 ((u8)15)
#define AUDIO_VBLANK_WQ ((u8)30)
#define AUDIO_VBLANK_WH ((u8)60)
#define AUDIO_VBLANK_QUEUE_RECORDS 16
#define AUDIO_VBLANK_QUEUE_BYTES 80
typedef void (*AudioVBlankFrameHook)();
extern __wram u8 AudioVBlank_MusicPlaying;
extern __wram u8 AudioVBlank_MusicEnabled;
extern __wram u8 AudioVBlank_MusicDelay;
extern __wram u8 *AudioVBlank_MusicStart;
extern __wram u8 *AudioVBlank_MusicPointer;
extern __wram u8 *AudioVBlank_MusicLoopPoint;
extern __wram u8 AudioVBlank_Step;
extern __wram u8 AudioVBlank_Ch2Note;
extern __wram u8 AudioVBlank_Ch1Note;
extern __wram u8 AudioVBlank_Ch3Note;
extern __wram u8 AudioVBlank_Ch2LatchedNote;
extern __wram u8 AudioVBlank_Ch1LatchedNote;
extern __wram u8 AudioVBlank_Ch3LatchedNote;
// Sound effects temporarily own CH1/CH2/CH3. A pending value of 1 means a BGM
// note/stop event was suppressed and must be applied once the effect ends;
// value 2 means release the effect channel without retriggering an old note.
extern __wram u8 AudioVBlank_RestoreCh1Pending;
extern __wram u8 AudioVBlank_RestoreCh3Pending;
extern __wram u8 AudioVBlank_Ch4Param;
extern __wram u8 AudioVBlank_Ch1Envelope;
extern __wram u8 AudioVBlank_Ch2Envelope;
extern __wram u8 AudioVBlank_Ch4Envelope;
extern __wram u8 AudioVBlank_Ch1Duty;
extern __wram u8 AudioVBlank_Ch2Duty;
extern __wram u8 AudioVBlank_Ch1Sweep;
extern __wram u8 AudioVBlank_Ch3Level;
extern __wram u8 AudioVBlank_ControlCommand;
extern __wram u8 AudioVBlank_ControlArg0;
extern __wram u8 AudioVBlank_ControlArg1;
extern __wram u8 AudioVBlank_ControlArg2;
// QueueReset/QueueRefill/QueuePlay provide the foreground producer. The ISR owns
// ReadIndex and decreases Count; use the producer APIs instead of editing the
// indices/count while playback is running. Records contain exactly five bytes.
// MusicPlaying and all enable/pause gates still apply in queue mode.
extern __wram u8 AudioVBlank_QueueMode;
extern __wram u8 AudioVBlank_QueueReadIndex;
extern __wram u8 AudioVBlank_QueueWriteIndex;
extern __wram u8 AudioVBlank_QueueCount;
extern __wram u8 AudioVBlank_QueueUnderruns;
// Queue bytes reside in WRAM bank 1. A producer must select that bank while
// writing and restore the caller bank; publish the record only after all bytes
// are complete. The ISR selects bank 1 itself and restores SVBK before returning.
extern __wramx_bank(1) u8 AudioVBlank_QueueBuffer[AUDIO_VBLANK_QUEUE_BYTES];
// The ISR invokes this hook even when music processing is gated off. It runs
// with SVBK=1 using a direct-address call; keep hook code/data accessible in that
// context and synchronize pointer changes with interrupt execution.
extern __wram AudioVBlankFrameHook AudioVBlank_FrameHook;
// Reset software playback state, queue counters, instruments and the frame hook.
// Call before enabling this ISR; the routine does not mask interrupts or
// initialize the APU registers and cannot synchronize with an active handler.
void AudioVBlank_Init();
// Stop music and select an empty WRAM queue; preserve IE, IME and enable/pause gates.
void AudioVBlank_QueueReset();
// Copy complete records from the currently mapped source; return records accepted.
// Each call attempts at most 16 records and stops when full. Null/zero returns 0.
// Supports ROM and RAM sources, restores SVBK and IE, and never changes IME.
// Single foreground producer only: do not call from an ISR or frame hook.
// LOOP must be expanded by the producer. END occupies a full five-byte record.
u8 __stackcall AudioVBlank_QueueRefill(const u8 *records, u8 count);
// Start/resume queued playback; return 1 on success, 0 for an empty/nonqueue mode.
// Does not enable the interrupt, music gate or clear pause. Queue before starting.
u8 AudioVBlank_QueuePlay();
// Select a directly addressed song and reset its cursors, leaving the playing
// flag unchanged. Song storage must remain accessible to the fixed-bank ISR;
// coordinate this multi-field update with interrupt execution.
void __stackcall AudioVBlank_SetMusic(u8 *song);
// Restart the selected direct song at its beginning, leaving queue mode.
// A null song invokes Stop; this does not enable the music or IRQ gates.
void AudioVBlank_Play();
// Select and restart a directly addressed song. This is not the banked WRAM-queue path.
void __stackcall AudioVBlank_PlayMusic(u8 *song);
// Stop software playback and silence pulse/noise envelopes and the wave DAC.
// Song pointers and the IRQ enable remain unchanged; the routine writes the
// physical channels directly, so coordinate ownership with sound effects.
void AudioVBlank_Stop();
// Gate music processing and clear pending CH1/CH3 recovery on disable.
// No audio register is written here: disabling does not itself silence a note
// already playing or disable the VBlank interrupt.
void __stackcall AudioVBlank_SetEnabled(u8 on);
// Enable the VBlank source in IE and execute EI, enabling CPU interrupts globally.
// Install the correct interrupt vector and initialize driver state first.
void AudioVBlank_EnableIrq();
// Clear only the VBlank bit in IE. Other interrupt sources and current sound remain unchanged.
void AudioVBlank_DisableIrq();
// Recover the borrowed pulse channel (CH1 or CH2) from its latched music event.
// Pending value 2, disabled music, or a non-note latch silences it instead.
// The caller must first establish that the effect has released the channel.
void AudioVBlank_RestoreCh1();
// Recover a latched wave-channel music event after effect release, or disable
// the DAC for a release-only request, disabled music, or a non-note latch.
// The caller owns effect arbitration and clearing the pending flag.
void AudioVBlank_RestoreCh3();
// Request deferred pulse-channel release only when no recovery is pending.
// Preserve value 1, which records a music event suppressed while an effect owned it.
void AudioVBlank_RequestRestoreCh1();
// Request a deferred wave-channel release only if no recovery is pending.
// Preserve value 1, which records a new music event suppressed by the effect.
void AudioVBlank_RequestRestoreCh3();kitaqgb/lib/audio_vblank.h
bank.h — バンク番号付きのコード・データアクセス
#pragma once
// A bank byte accompanies the CPU-visible ROM address; the pointer alone is not a far address.
typedef __packed struct {
u8 bank;
const u8* ptr;
} BankPtr;
extern u8 kq_bank_current;
void __bankswitch(u8 bank);
u8 __farpeek8(u8 bank, const void* addr);
u16 __farpeek16(u8 bank, const void* addr);
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);
void __farcall_ptr(u8 bank, const void* func);
// Update both the library bank record and the compiler shadow used by far
// reads and calls. Invoke this permanent bank change from fixed-bank code.
void bank_switch(u8 bank);
// Return the last bank recorded by bank_switch; this does not read cartridge hardware.
u8 bank_get_current();
// Read one byte through the compiler intrinsic that accepts an explicit ROM bank.
u8 far_data_read8(u8 bank, const void* addr);
// Read a 16-bit value from the supplied bank and address through the far-read intrinsic.
u16 far_data_read16(u8 bank, const void* addr);
// Copy len bytes from banked ROM into caller-owned writable storage.
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);
// Invoke a no-argument callback in its ROM bank, then restore the caller
// bank. The pointer must identify executable code; no arguments are forwarded.
void far_call(u8 bank, const void* func);
// Store a bank/address pair without accessing ROM; a null output pointer is ignored.
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);
// Read one byte using both components of the bank-qualified pointer.
u8 farptr_read8(BankPtr ptr);
// Read a 16-bit value using both components of the bank-qualified pointer.
u16 farptr_read16(BankPtr ptr);
// Copy len bytes from a bank-qualified pointer; dst must have room for the full copy.
void farptr_read(BankPtr ptr, void* dst, u16 len);kitaqgb/lib/bank.h
camera.h — 世界座標と表示範囲の変換
#pragma once
#include "scroll.h"
// All position, target and bound fields use signed Q8.8 (-128 to just below 128 pixels).
// Pass initialized, non-null storage; world/screen conversion arguments use integer pixels.
typedef struct Camera8_8 {
s16 fx;
s16 fy;
s16 target_fx;
s16 target_fy;
s16 min_fx;
s16 min_fy;
s16 max_fx;
s16 max_fy;
u8 use_target;
u8 use_bounds;
} Camera8_8;
// Zero all position/target/bound fields and disable targeting and bounds.
void Camera_Init(Camera8_8* cam);
// Replace the current Q8.8 position and immediately apply enabled bounds.
void Camera_Set(Camera8_8* cam, s16 fx, s16 fy);
// Store a Q8.8 target and enable subsequent step-toward-target updates.
void Camera_SetTarget(Camera8_8* cam, s16 fx, s16 fy);
// Disable target following without moving the camera or erasing the stored target.
void Camera_ClearTarget(Camera8_8* cam);
// Install ordered Q8.8 bounds, enable them and clamp the current position immediately.
void Camera_SetBounds(Camera8_8* cam, s16 min_fx, s16 min_fy, s16 max_fx, s16 max_fy);
// Disable clamping while retaining the stored bounds and current position.
void Camera_ClearBounds(Camera8_8* cam);
// Add Q8.8 deltas and clamp afterward. Keep the intermediate sums within s16;
// clamping cannot repair arithmetic that already overflowed.
void Camera_Add(Camera8_8* cam, s16 dfx, s16 dfy);
// Move by the signed target delta shifted by 0..7 bits, then apply bounds.
// Quantization can make a small delta produce no movement; only exact equality
// clears targeting, and a target outside the bounds may remain active indefinitely.
void Camera_StepTowardTarget(Camera8_8* cam, u8 shift);
// Discard fractional bits and write the low eight integer bits to background scroll registers.
void Camera_ApplyBg(const Camera8_8* cam);
// Convert the Q8.8 position to byte scroll values and stage them for a later flush.
void Camera_ApplyBgBuffered(const Camera8_8* cam);
// Derive nonnegative camera extents from world minus screen size, then convert
// them to Q8.8 bounds. Extents must fit signed Q8.8; the u16 input type does
// not imply support for arbitrary large worlds.
void Camera_ClampToSize(Camera8_8* cam, u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);
// Convert an integer-pixel world X by subtracting the camera's integer X component.
s16 Camera_WorldToScreenX(const Camera8_8* cam, s16 world_x);
// Convert an integer-pixel world Y by subtracting the camera's integer Y component.
s16 Camera_WorldToScreenY(const Camera8_8* cam, s16 world_y);
// Convert integer-pixel screen X back to world X using the camera's integer component.
s16 Camera_ScreenToWorldX(const Camera8_8* cam, s16 screen_x);
// Convert integer-pixel screen Y back to world Y using the camera's integer component.
s16 Camera_ScreenToWorldY(const Camera8_8* cam, s16 screen_y);
// Set the shared camera from integer pixels, converting to the representable Q8.8 range.
void camera_set(s16 x, s16 y);
// Position the shared camera so the world point appears at the requested screen
// anchor. This is an immediate move, not the smoothed target-following path.
void camera_follow_xy(s16 x, s16 y, u8 screen_cx, u8 screen_cy);
// Derive and apply world/screen bounds to the shared convenience camera.
void camera_clamp(u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);
// Write the shared camera's current integer position to background scroll registers.
void camera_apply();
// Convert integer-pixel world X using the shared convenience camera.
s16 camera_world_to_screen_x(s16 world_x);
// Convert integer-pixel world Y using the shared convenience camera.
s16 camera_world_to_screen_y(s16 world_y);kitaqgb/lib/camera.h
cgb_palette.h — CGBのBG/OBJパレット
#pragma once
// High-level CGB palette helpers for KITAQGB.
// Palette writes no-op on DMG; RGB packing works on either hardware model.
// Safe here means CGB hardware gating. Palette data writes still require an accessible
// PPU phase (for example LCD off or a sufficiently short VBlank update).
u8 __cgb_is_cgb();
void __cgb_safe_set_bcps(u8 value);
void __cgb_safe_set_bcpd(u8 value);
void __cgb_safe_set_ocps(u8 value);
void __cgb_safe_set_ocpd(u8 value);
// Mask each component to five bits: red in 0..4, green in 5..9, blue in 10..14.
// These macros wrap oversized components rather than saturating them.
#define CGB_RGB15(r5, g5, b5) ((u16)((u16)((r5) & 31) | ((u16)((g5) & 31) << 5) | ((u16)((b5) & 31) << 10)))
// Pack the red, green and blue components using the public RGB15 macro.
u16 cgb_rgb15(u8 r5, u8 g5, u8 b5);
// Set one background color through the indexed palette wrapper.
void cgb_bg_color(u8 palette_index, u8 color_index, u16 rgb15);
// Set one object color through the indexed palette wrapper.
void cgb_obj_color(u8 palette_index, u8 color_index, u16 rgb15);
// Pack component values and write one background color.
void cgb_bg_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);
// Pack component values and write one object color.
void cgb_obj_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);
// Write background colors starting at the indexed slot. The range may cross
// palette boundaries and is clamped only at the end of all 32 slots.
void cgb_bg_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);
// Write object colors from the indexed slot, potentially crossing into later palettes.
void cgb_obj_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);
// Write four consecutive colors for one background palette.
void cgb_bg_palette(u8 palette_index, const u16* colors);
// Write four consecutive colors for one object palette.
void cgb_obj_palette(u8 palette_index, const u16* colors);
// Expose sequential background writes using an absolute color-slot index.
void cgb_bg_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);
// Expose sequential object writes using an absolute color-slot index.
void cgb_obj_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);kitaqgb/lib/cgb_palette.h
cgb_tile.h — CGBのタイル番号と属性
#pragma once
// Declarations for compiler-intrinsic CGB tile helpers.
// Canonical intrinsic entry points use the "__" prefix.
// Attr writes no-op on DMG. Combined tile+attr writes still write the tile on DMG.
// These helpers emit compiler code; cgb_tile.c has no runtime bodies. Scalar coordinates address 32x32 maps.
// Safe write paths poll VRAM access; LCD-on paths can enable interrupts after writing.
// Use _unsafe/_fast only with caller-managed writable VRAM timing, especially inside an ISR.
// Tile writes use the selected VRAM bank; choose bank zero before combined tile/attribute operations.
#define KQ_CGB_ATTR_PAL_MASK 0x07
#define KQ_CGB_ATTR_BANK1 0x08
#define KQ_CGB_ATTR_XFLIP 0x20
#define KQ_CGB_ATTR_YFLIP 0x40
#define KQ_CGB_ATTR_PRIORITY 0x80
#define KQ_CGB_ATTR_NONE ((u8)0)
#define KQ_CGB_ATTR_PAL(n) ((u8)((n) & KQ_CGB_ATTR_PAL_MASK))
#define KQ_CGB_ATTR_BANK(bank) ((u8)((bank) ? KQ_CGB_ATTR_BANK1 : 0))
#define KQ_CGB_ATTR_XFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_XFLIP : 0))
#define KQ_CGB_ATTR_YFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_YFLIP : 0))
#define KQ_CGB_ATTR_PRIORITY_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_PRIORITY : 0))
#define KQ_CGB_ATTR(palette, bank, xflip, yflip, priority) ((u8)(((palette) & KQ_CGB_ATTR_PAL_MASK) | ((bank) ? KQ_CGB_ATTR_BANK1 : 0) | ((xflip) ? KQ_CGB_ATTR_XFLIP : 0) | ((yflip) ? KQ_CGB_ATTR_YFLIP : 0) | ((priority) ? KQ_CGB_ATTR_PRIORITY : 0)))
// Return the compiler/runtime CGB mode test as a byte Boolean.
u8 __cgb_is_cgb();
// Write the VRAM bank selection only on CGB. Safe here means hardware-mode guarded, not PPU-timed.
void __cgb_safe_set_vbk(u8 value);
// Write a tile number to map 9800, waiting for VRAM access when necessary.
void __settile(u8 x, u8 y, u8 tile);
// Write a tile number to map 9800 without waiting for writable VRAM.
void __settile_unsafe(u8 x, u8 y, u8 tile);
// Write at base+32*y+x with VRAM access checks; supply a valid tile-map base.
void __settileat(u16 base, u8 x, u8 y, u8 tile);
// Use an explicit map base without waiting for VRAM access.
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);
// Select the window map from LCDC bit 6, then write a tile with access checks.
void __settilewin(u8 x, u8 y, u8 tile);
// Select the LCDC window map and write without access waits.
void __settilewin_unsafe(u8 x, u8 y, u8 tile);
// Select the background map from LCDC bit 3, then write with access checks.
void __settilebg(u8 x, u8 y, u8 tile);
// Select the LCDC background map and write without access waits.
void __settilebg_unsafe(u8 x, u8 y, u8 tile);
// Copy count sequential bytes to dest using the current VRAM bank and access-checked transfer.
void __settile_bulk(u16 dest, const u8* src, u8 count);
// Copy count sequential bytes to dest without VRAM access waits; retain source-bank visibility.
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);
// Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
void __settileattr(u8 x, u8 y, u8 attr);
// Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
void __settileattr_unsafe(u8 x, u8 y, u8 attr);
// Write tile and CGB attributes at map 9800; DMG receives only the tile write. Use the access-checked write path.
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);
// Write tile and CGB attributes at map 9800; DMG receives only the tile write. No VRAM access waits are inserted.
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);
// Write attributes in bank one at the explicit map base on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);
// Write attributes in bank one at the explicit map base on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);
// Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. Use the access-checked write path.
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);
// Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. No VRAM access waits are inserted.
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);
// Write attributes in bank one at the LCDC window map on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
void __settilewinattr(u8 x, u8 y, u8 attr);
// Write attributes in bank one at the LCDC window map on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);
// Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. Use the access-checked write path.
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);
// Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. No VRAM access waits are inserted.
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);
// Write attributes in bank one at the LCDC background map on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
void __settilebgattr(u8 x, u8 y, u8 attr);
// Write attributes in bank one at the LCDC background map on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);
// Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. Use the access-checked write path.
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);
// Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. No VRAM access waits are inserted.
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);
// Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. Transfers use access checks.
void __settileattr_bulk(u16 dest, const u8* src, u8 count);
// Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. The caller supplies writable VRAM timing.
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);
// Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. Transfers use access checks.
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);
// Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. The caller supplies writable VRAM timing.
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);kitaqgb/lib/cgb_tile.h
chain.h — 関節追従と座標履歴
#pragma once
typedef __packed struct {
s16 x;
s16 y;
} ChainPoint;
typedef __packed struct {
ChainPoint* points;
u8 capacity;
u8 count;
u8 head;
} Chain;
// Attach caller-owned ring storage and start with no recorded points.
// The storage must remain valid for the lifetime of the chain.
// Use a valid chain pointer and capacity entries of caller-owned storage.
// Capacity at most 128 keeps every head+age sum in the byte range used by the reader.
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity);
// Forget the recorded points without clearing or freeing the backing storage.
void chain_clear(Chain* chain);
// Move the ring head backward and store the newest point. Once full, each push
// replaces the oldest point; zero capacity is a no-op.
void chain_push_head(Chain* chain, s16 x, s16 y);
// Read a point by age, with index zero denoting the newest point. Return zero
// for an invalid request and clear a non-null output first. Keep head + index
// within the 8-bit range: addition is narrowed before the capacity wrap.
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out);
// Return the number of stored points, or zero for a null chain.
u8 chain_get_count(const Chain* chain);
// Return target minus current along the shortest wrapped route.
// Supply size 1..256 and coordinates in 0..size-1; inputs are not normalized.
// Exactly half a field retains the direct displacement sign.
s16 chain_wrap_delta(u8 target, u8 current, u16 size);
// Articulated body: one current pose per joint, including the head at index 0.
// Caller-owned byte arrays keep the inner loop compact on both GB and FC.
// Heading sectors run clockwise: 0=right, 4=down, 8=left, 12=up.
// Coordinates are pixels; each wrapped dimension accepts 1..256 pixels.
typedef __packed struct {
u8* x;
u8* y;
u8* heading;
u8 capacity;
u8 count;
u16 width;
u16 height;
} ChainBody;
// Attach three non-overlapping capacity-byte arrays. Return 1 on success.
// Invalid storage, zero capacity or dimensions outside 1..256 disable the body.
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height);
// Seed count joints in a straight pose behind the head; count includes the head.
// Return 0 without changing the pose if count is zero or exceeds capacity.
// Coordinates wrap to the playfield and headings are masked to 0..15.
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading);
// Install the caller's head pose, then pull joints from front to rear.
// Each joint aims four pixels behind its updated predecessor and receives that
// predecessor's OLD heading. Each axis moves 1 pixel, or 2 when error exceeds 4.
// Call once per simulation tick; this does not advance head physics or render.
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading);
// Append a copy of the current tail; later steps separate the new joint.
// Return 1 on growth, or 0 for an empty, disabled or full body.
u8 chain_body_grow(ChainBody* body);
// Remove all joints while retaining caller-owned storage and playfield settings.
void chain_body_clear(ChainBody* body);kitaqgb/lib/chain.h
danmaku.h — 弾プール・扇状生成・被弾とグレイズ
#pragma once
// Fixed-capacity, allocation-free bullet field for a 160 x 128 play area.
// Positions/velocities are signed Q12.4 / Q4.4; +Y points down.
// Tile ids 0..15 encode the four 4x4 quadrants of an 8x8 tile.
// Reserve those tiles in VRAM, with a 3x3 bullet centered at (2,2)
// in every set quadrant. Other tile ids in the map are decoration.
#define DANMAKU_MAX 96
#define DANMAKU_MAP_BYTES 576
typedef __packed struct {
s16 x;
s16 y;
s8 vx;
s8 vy;
u8 active;
u8 grazed;
} DanmakuBullet;
extern DanmakuBullet dm_bullets[DANMAKU_MAX];
extern __wram __aligned(16) u8 dm_map[DANMAKU_MAP_BYTES];
extern u8 dm_count;
extern u8 dm_peak;
// Per-step hit is a Boolean latch; graze counts newly grazed bullets this step. Spawn/rejection counters wrap at 65536.
extern u8 dm_hit;
extern u8 dm_graze;
extern u8 dm_player_x;
extern u8 dm_player_y;
extern u8 dm_invulnerable;
extern u16 dm_spawned;
extern u16 dm_rejected;
// Reset pool, peak and lifetime counters; player fields and map contents remain caller-owned state.
void danmaku_reset();
// Deactivate the pool while retaining peak/spawn/rejection counters and the RAM map.
void danmaku_clear();
// Use pixel origins inside 160x128 and signed sixteenth-pixel velocities; return one if a slot was allocated.
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);
// Attempt a fan using wrapping 32-step angles; count is capped at 96 and speed at 64.
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);
// 32 directions, 0=right, 8=down, 16=left, 24=up. speed in 1/16 px.
// Clear the 32x18 RAM map before drawing the next frame; no transfer is performed.
void danmaku_clear_map();
// Caller fills decoration after clear_map, then calls step to overlay bullets.
// hit and graze are events for this step. Collision uses rendered centers.
void danmaku_step();
// Requires CGB and LCD off or the beginning of VBlank. VBK restored to 0.
// Transfers 18 rows (32-byte stride) to BG map 9800 with 36-block GDMA.
void danmaku_present_now();kitaqgb/lib/danmaku.h
debug.h — RAMに値とアサートを記録
#pragma once
// Use a capacity from 1 through 255; the length counter is a byte.
// Compile every unit with the same override so the array declaration and storage agree.
#ifndef DEBUG_TRACE_MAX
#define DEBUG_TRACE_MAX 32
#endif
// Each packed entry retains a name pointer and two 16-bit values; name text is not copied.
typedef __packed struct {
const u8* name;
u16 value;
u16 frame;
} DebugTraceEntry;
extern DebugTraceEntry kq_debug_trace_log[DEBUG_TRACE_MAX];
extern u8 kq_debug_trace_count;
extern u16 kq_debug_frame;
extern u16 kq_debug_last_assert;
// Reset log length, frame tag and last assertion code; existing entry bytes are not erased.
void debug_init();
// Set the frame tag copied into subsequently recorded entries.
void debug_set_frame(u16 frame);
// Append a named frame marker using 0xFFFF as its value.
void debug_mark_frame(const u8* label);
// Append an unsigned byte value, widened to the log's 16-bit value field.
void debug_trace_u8(const u8* name, u8 value);
// Append a 16-bit value with the current frame tag.
void debug_trace_u16(const u8* name, u16 value);
// Record the assertion code and attempt to append an ASSERT entry. This helper
// does not halt execution, and the code is retained even if the log is full.
void debug_assert_fail(u16 code);
// Return the number of valid entries in the fixed trace buffer.
u8 debug_get_trace_count();
// Expose the internal trace array for inspection; use the count to bound reads.
const DebugTraceEntry* debug_get_trace_log();
// Read the last recorded assertion code, or the zero value installed by initialization.
u16 debug_get_last_assert();
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)kitaqgb/lib/debug.h
dmg3d.h — DMGの128×120モノクロワイヤーフレーム
// Compatibility names for the shared wire3d_dmg renderer.
// Select the same profile in every translation unit; compile one renderer entry.
#pragma once
#ifndef WIRE3D_DMG_HEIGHT
#define WIRE3D_DMG_HEIGHT 120
#endif
#if WIRE3D_DMG_HEIGHT != 120
#error Incompatible wire3d_dmg profile
#endif
#include "wire3d_dmg.h"
// The aliases below add no calls or storage. Their contracts are documented at the corresponding wire3d_dmg.h declarations.
#define DMG3D_ANGLE_STEPS WIRE3D_DMG_ANGLE_STEPS
#define DMG3D_AUX_COL_BYTES WIRE3D_DMG_AUX_COL_BYTES
#define DMG3D_AUX_ROWS WIRE3D_DMG_AUX_ROWS
#define DMG3D_BG_QUEUE_LIMIT WIRE3D_DMG_BG_QUEUE_LIMIT
#define DMG3D_BeginFrame Wire3DDMG_BeginFrame
#define DMG3D_CENTER_X WIRE3D_DMG_CENTER_X
#define DMG3D_CENTER_Y WIRE3D_DMG_CENTER_Y
#define DMG3D_DrawIndexedEdges Wire3DDMG_DrawIndexedEdges
#define DMG3D_DrawLine2D Wire3DDMG_DrawLine2D
#define DMG3D_DrawLine3D Wire3DDMG_DrawLine3D
#define DMG3D_DrawModel Wire3DDMG_DrawModel
#define DMG3D_DrawModelScaled Wire3DDMG_DrawModelScaled
#define DMG3D_DrawScene Wire3DDMG_DrawScene
#define DMG3D_Edge Wire3DDMG_Edge
#define DMG3D_EdgeFaces Wire3DDMG_EdgeFaces
#define DMG3D_EndFrame Wire3DDMG_EndFrame
#define DMG3D_EraseModelFaces Wire3DDMG_EraseModelFaces
#define DMG3D_EraseSpan2D Wire3DDMG_EraseSpan2D
#define DMG3D_EraseTriangle2D Wire3DDMG_EraseTriangle2D
#define DMG3D_FACE_NONE WIRE3D_DMG_FACE_NONE
#define DMG3D_FAR_Z WIRE3D_DMG_FAR_Z
#define DMG3D_Face Wire3DDMG_Face
#define DMG3D_Init Wire3DDMG_Init
#define DMG3D_MODEL_FACE_LIMIT WIRE3D_DMG_MODEL_FACE_LIMIT
#define DMG3D_MODEL_HIDDEN_LINES WIRE3D_DMG_MODEL_HIDDEN_LINES
#define DMG3D_MODEL_VERTEX_LIMIT WIRE3D_DMG_MODEL_VERTEX_LIMIT
#define DMG3D_Model Wire3DDMG_Model
#define DMG3D_NEAR_Z WIRE3D_DMG_NEAR_Z
#define DMG3D_Object Wire3DDMG_Object
#define DMG3D_PROJECT_LIMIT WIRE3D_DMG_PROJECT_LIMIT
#define DMG3D_ProjectPoint Wire3DDMG_ProjectPoint
#define DMG3D_PutBgTile Wire3DDMG_PutBgTile
#define DMG3D_ROW_BYTES WIRE3D_DMG_ROW_BYTES
#define DMG3D_RotatePoint Wire3DDMG_RotatePoint
#define DMG3D_SCENE_OBJECT_LIMIT WIRE3D_DMG_SCENE_OBJECT_LIMIT
#define DMG3D_SCREEN_H WIRE3D_DMG_SCREEN_H
#define DMG3D_SCREEN_W WIRE3D_DMG_SCREEN_W
#define DMG3D_SetAuxTransfer Wire3DDMG_SetAuxTransfer
#define DMG3D_SetCamera Wire3DDMG_SetCamera
#define DMG3D_SetDirtyTransfer Wire3DDMG_SetDirtyTransfer
#define DMG3D_SetPalette Wire3DDMG_SetPalette
#define DMG3D_TILE_H WIRE3D_DMG_TILE_H
#define DMG3D_TILE_W WIRE3D_DMG_TILE_W
#define DMG3D_TRANSFORM_LIMIT WIRE3D_DMG_TRANSFORM_LIMIT
#define DMG3D_TransferAuxNow Wire3DDMG_TransferAuxNow
#define DMG3D_TransferMainNow Wire3DDMG_TransferMainNow
#define DMG3D_Vec3 Wire3DDMG_Vec3
// The remaining aliases retain internal identifiers for source compatibility; they do not define an independent renderer.
#define DMG3D_abs_i16 w3ddmg_abs_i16
#define DMG3D_angle_index w3ddmg_angle_index
#define DMG3D_aux_transfer_enabled w3ddmg_aux_transfer_enabled
#define DMG3D_bg_map_9800 w3ddmg_bg_map_9800
#define DMG3D_bgq_addr_hi w3ddmg_bgq_addr_hi
#define DMG3D_bgq_addr_lo w3ddmg_bgq_addr_lo
#define DMG3D_bgq_count w3ddmg_bgq_count
#define DMG3D_bgq_tile w3ddmg_bgq_tile
#define DMG3D_bgq_value w3ddmg_bgq_value
#define DMG3D_bgq_x w3ddmg_bgq_x
#define DMG3D_bgq_y w3ddmg_bgq_y
#define DMG3D_bit_mask w3ddmg_bit_mask
#define DMG3D_build_face_visibility w3ddmg_build_face_visibility
#define DMG3D_cam_pitch w3ddmg_cam_pitch
#define DMG3D_cam_roll w3ddmg_cam_roll
#define DMG3D_cam_x w3ddmg_cam_x
#define DMG3D_cam_y w3ddmg_cam_y
#define DMG3D_cam_yaw w3ddmg_cam_yaw
#define DMG3D_cam_z w3ddmg_cam_z
#define DMG3D_clamp_i16 w3ddmg_clamp_i16
#define DMG3D_clamp_screen w3ddmg_clamp_screen
#define DMG3D_clear_dirty_tables w3ddmg_clear_dirty_tables
#define DMG3D_clear_occlusion_mask w3ddmg_clear_occlusion_mask
#define DMG3D_clear_occlusion_mask_asm w3ddmg_clear_occlusion_mask_asm
#define DMG3D_clear_stage_asm w3ddmg_clear_stage_asm
#define DMG3D_clear_stage_aux_asm w3ddmg_clear_stage_aux_asm
#define DMG3D_clear_triangle w3ddmg_clear_triangle
#define DMG3D_clear_vram_asm w3ddmg_clear_vram_asm
#define DMG3D_cos_q6 w3ddmg_cos_q6
#define DMG3D_dirty_dst_hi w3ddmg_dirty_dst_hi
#define DMG3D_dirty_dst_lo w3ddmg_dirty_dst_lo
#define DMG3D_dirty_src_hi w3ddmg_dirty_src_hi
#define DMG3D_dirty_src_lo w3ddmg_dirty_src_lo
#define DMG3D_dirty_tiles w3ddmg_dirty_tiles
#define DMG3D_dirty_transfer_enabled w3ddmg_dirty_transfer_enabled
#define DMG3D_edge_area w3ddmg_edge_area
#define DMG3D_face_visible w3ddmg_face_visible
#define DMG3D_fill_bg_map_asm w3ddmg_fill_bg_map_asm
#define DMG3D_flush_bg_queue w3ddmg_flush_bg_queue
#define DMG3D_i16 w3ddmg_i16
#define DMG3D_i8 w3ddmg_i8
#define DMG3D_inv_depth w3ddmg_inv_depth
#define DMG3D_is_edge_visible w3ddmg_is_edge_visible
#define DMG3D_line_dx w3ddmg_line_dx
#define DMG3D_line_dy w3ddmg_line_dy
#define DMG3D_line_err w3ddmg_line_err
#define DMG3D_line_stage_asm w3ddmg_line_stage_asm
#define DMG3D_line_stage_masked_c w3ddmg_line_stage_masked_c
#define DMG3D_line_sx w3ddmg_line_sx
#define DMG3D_line_sy w3ddmg_line_sy
#define DMG3D_line_x w3ddmg_line_x
#define DMG3D_line_x0 w3ddmg_line_x0
#define DMG3D_line_x1 w3ddmg_line_x1
#define DMG3D_line_y w3ddmg_line_y
#define DMG3D_line_y0 w3ddmg_line_y0
#define DMG3D_line_y1 w3ddmg_line_y1
#define DMG3D_mark_line_dirty w3ddmg_mark_line_dirty
#define DMG3D_mark_model_occluder w3ddmg_mark_model_occluder
#define DMG3D_mark_rect_dirty w3ddmg_mark_rect_dirty
#define DMG3D_mark_tile_dirty w3ddmg_mark_tile_dirty
#define DMG3D_mark_triangle w3ddmg_mark_triangle
#define DMG3D_mask_get w3ddmg_mask_get
#define DMG3D_mask_offset w3ddmg_mask_offset
#define DMG3D_mask_set w3ddmg_mask_set
#define DMG3D_mask_set_span w3ddmg_mask_set_span
#define DMG3D_neg_angle w3ddmg_neg_angle
#define DMG3D_occlusion_active w3ddmg_occlusion_active
#define DMG3D_occlusion_mask w3ddmg_occlusion_mask
#define DMG3D_plot_stage_asm w3ddmg_plot_stage_asm
#define DMG3D_plot_tx w3ddmg_plot_tx
#define DMG3D_plot_x w3ddmg_plot_x
#define DMG3D_plot_y w3ddmg_plot_y
#define DMG3D_prev_dirty_tiles w3ddmg_prev_dirty_tiles
#define DMG3D_project_camera_space w3ddmg_project_camera_space
#define DMG3D_project_world w3ddmg_project_world
#define DMG3D_put_bg_tile_safe_asm w3ddmg_put_bg_tile_safe_asm
#define DMG3D_reg_bgp w3ddmg_reg_bgp
#define DMG3D_reg_lcdc w3ddmg_reg_lcdc
#define DMG3D_reg_ly w3ddmg_reg_ly
#define DMG3D_reg_scx w3ddmg_reg_scx
#define DMG3D_reg_scy w3ddmg_reg_scy
#define DMG3D_rotate_x w3ddmg_rotate_x
#define DMG3D_rotate_y w3ddmg_rotate_y
#define DMG3D_rotate_z w3ddmg_rotate_z
#define DMG3D_scene_depth w3ddmg_scene_depth
#define DMG3D_scene_object_depth w3ddmg_scene_object_depth
#define DMG3D_scene_order w3ddmg_scene_order
#define DMG3D_screen_delta w3ddmg_screen_delta
#define DMG3D_screen_visible w3ddmg_screen_visible
#define DMG3D_screen_x w3ddmg_screen_x
#define DMG3D_screen_y w3ddmg_screen_y
#define DMG3D_sin_q6 w3ddmg_sin_q6
#define DMG3D_stage w3ddmg_stage
#define DMG3D_stage_clear_pixel w3ddmg_stage_clear_pixel
#define DMG3D_stage_clear_span w3ddmg_stage_clear_span
#define DMG3D_transfer_dirty_stage w3ddmg_transfer_dirty_stage
#define DMG3D_transfer_dirty_tile_asm w3ddmg_transfer_dirty_tile_asm
#define DMG3D_transfer_stage_asm w3ddmg_transfer_stage_asm
#define DMG3D_transfer_stage_aux_asm w3ddmg_transfer_stage_aux_asm
#define DMG3D_u16 w3ddmg_u16
#define DMG3D_u8 w3ddmg_u8
#define DMG3D_vram_tiles w3ddmg_vram_tiles
#define DMG3D_wait_vblank_start w3ddmg_wait_vblank_start
#define dmg3d w3ddmg
#define dmg3d_pair_aux_block w3ddmg_pair_aux_block
#define dmg3d_pair_aux_done w3ddmg_pair_aux_done
#define dmg3d_pair_aux_wait_0 w3ddmg_pair_aux_wait_0
#define dmg3d_pair_aux_wait_1 w3ddmg_pair_aux_wait_1
#define dmg3d_pair_aux_wait_10 w3ddmg_pair_aux_wait_10
#define dmg3d_pair_aux_wait_11 w3ddmg_pair_aux_wait_11
#define dmg3d_pair_aux_wait_12 w3ddmg_pair_aux_wait_12
#define dmg3d_pair_aux_wait_13 w3ddmg_pair_aux_wait_13
#define dmg3d_pair_aux_wait_14 w3ddmg_pair_aux_wait_14
#define dmg3d_pair_aux_wait_15 w3ddmg_pair_aux_wait_15
#define dmg3d_pair_aux_wait_16 w3ddmg_pair_aux_wait_16
#define dmg3d_pair_aux_wait_17 w3ddmg_pair_aux_wait_17
#define dmg3d_pair_aux_wait_18 w3ddmg_pair_aux_wait_18
#define dmg3d_pair_aux_wait_19 w3ddmg_pair_aux_wait_19
#define dmg3d_pair_aux_wait_2 w3ddmg_pair_aux_wait_2
#define dmg3d_pair_aux_wait_20 w3ddmg_pair_aux_wait_20
#define dmg3d_pair_aux_wait_21 w3ddmg_pair_aux_wait_21
#define dmg3d_pair_aux_wait_22 w3ddmg_pair_aux_wait_22
#define dmg3d_pair_aux_wait_23 w3ddmg_pair_aux_wait_23
#define dmg3d_pair_aux_wait_24 w3ddmg_pair_aux_wait_24
#define dmg3d_pair_aux_wait_25 w3ddmg_pair_aux_wait_25
#define dmg3d_pair_aux_wait_26 w3ddmg_pair_aux_wait_26
#define dmg3d_pair_aux_wait_27 w3ddmg_pair_aux_wait_27
#define dmg3d_pair_aux_wait_3 w3ddmg_pair_aux_wait_3
#define dmg3d_pair_aux_wait_4 w3ddmg_pair_aux_wait_4
#define dmg3d_pair_aux_wait_5 w3ddmg_pair_aux_wait_5
#define dmg3d_pair_aux_wait_6 w3ddmg_pair_aux_wait_6
#define dmg3d_pair_aux_wait_7 w3ddmg_pair_aux_wait_7
#define dmg3d_pair_aux_wait_8 w3ddmg_pair_aux_wait_8
#define dmg3d_pair_aux_wait_9 w3ddmg_pair_aux_wait_9
#define dmg3d_pair_main_block w3ddmg_pair_main_block
#define dmg3d_pair_main_wait_0 w3ddmg_pair_main_wait_0
#define dmg3d_pair_main_wait_1 w3ddmg_pair_main_wait_1
#define dmg3d_pair_main_wait_10 w3ddmg_pair_main_wait_10
#define dmg3d_pair_main_wait_11 w3ddmg_pair_main_wait_11
#define dmg3d_pair_main_wait_12 w3ddmg_pair_main_wait_12
#define dmg3d_pair_main_wait_13 w3ddmg_pair_main_wait_13
#define dmg3d_pair_main_wait_14 w3ddmg_pair_main_wait_14
#define dmg3d_pair_main_wait_2 w3ddmg_pair_main_wait_2
#define dmg3d_pair_main_wait_3 w3ddmg_pair_main_wait_3
#define dmg3d_pair_main_wait_4 w3ddmg_pair_main_wait_4
#define dmg3d_pair_main_wait_5 w3ddmg_pair_main_wait_5
#define dmg3d_pair_main_wait_6 w3ddmg_pair_main_wait_6
#define dmg3d_pair_main_wait_7 w3ddmg_pair_main_wait_7
#define dmg3d_pair_main_wait_8 w3ddmg_pair_main_wait_8
#define dmg3d_pair_main_wait_9 w3ddmg_pair_main_wait_9
#define dmg3dac_col w3ddmgac_col
#define dmg3dac_enter w3ddmgac_enter
#define dmg3dac_row w3ddmgac_row
#define dmg3dbg_enter w3ddmgbg_enter
#define dmg3dco_enter w3ddmgco_enter
#define dmg3dco_loop w3ddmgco_loop
#define dmg3dco_page w3ddmgco_page
#define dmg3dcs_enter w3ddmgcs_enter
#define dmg3dcs_inner w3ddmgcs_inner
#define dmg3dcs_outer w3ddmgcs_outer
#define dmg3dcv_enter w3ddmgcv_enter
#define dmg3dcv_loop w3ddmgcv_loop
#define dmg3dcv_page w3ddmgcv_page
#define dmg3ddt_enter w3ddmgdt_enter
#define dmg3ddt_loop w3ddmgdt_loop
#define dmg3dfb_enter w3ddmgfb_enter
#define dmg3dfb_loop w3ddmgfb_loop
#define dmg3dfb_page w3ddmgfb_page
#define dmg3dls_branch w3ddmgls_branch
#define dmg3dls_done w3ddmgls_done
#define dmg3dls_enter w3ddmgls_enter
#define dmg3dls_shallow w3ddmgls_shallow
#define dmg3dls_shallow_loop w3ddmgls_shallow_loop
#define dmg3dls_shallow_skip_bridge w3ddmgls_shallow_skip_bridge
#define dmg3dls_steep w3ddmgls_steep
#define dmg3dls_steep_loop w3ddmgls_steep_loop
#define dmg3dls_steep_skip_bridge w3ddmgls_steep_skip_bridge
#define dmg3dls_x_reverse w3ddmgls_x_reverse
#define dmg3dls_y_reverse w3ddmgls_y_reverse
#define dmg3dls_y_start w3ddmgls_y_start
#define dmg3dps_enter w3ddmgps_enter
#define dmg3dps_ret w3ddmgps_retkitaqgb/lib/dmg3d.h
entity.h — 固定配列のオブジェクトプール
#pragma once
// Keep capacity in 1..255: IDs and scan counters are bytes and FF is reserved for failure.
// Use the same definition in every compilation unit.
#ifndef ENTITY_MAX
#define ENTITY_MAX ((u8)16)
#endif
// Position, velocity, state and timer units are game-defined; the pool does not advance them.
// The sprite field is only bookkeeping and does not allocate or release OAM entries.
typedef __packed struct {
u8 active;
u8 type;
s16 x;
s16 y;
s16 vx;
s16 vy;
u8 state;
u8 timer;
u8 sprite;
} Entity;
// An entity callback receives a slot ID, not a persistent object handle.
typedef void (*EntityFn)(u8 id);
// Reset every slot before the game starts using the pool.
void entity_init();
// Returns a reusable slot ID, or 0xFF if no slot is free.
u8 entity_create(u8 type, s16 x, s16 y);
// Release a valid slot without clearing its payload. Reallocation resets the payload;
// a saved pointer or ID must not be treated as a persistent entity identity.
void entity_destroy(u8 id);
// Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.
Entity* entity_get(u8 id);
// Call fn for each slot that is active when the scan reaches it; null is a no-op.
// Callbacks may change the pool, so newly activated later slots can run in this pass.
void entity_update_all(EntityFn fn);
// Use the same active-slot traversal for drawing; the callback supplies all rendering.
void entity_draw_all(EntityFn fn);
// Count occupied slots without changing entity state.
u8 entity_count_active();
// Release every entity and restore deterministic defaults, including the no-sprite sentinel.
void entity_clear_all();kitaqgb/lib/entity.h
fixed.h — Q8.8固定小数点と矩形
#pragma once
// Signed Q8.8 uses 256 units per pixel/value: the range is -128 to 127 + 255/256.
typedef s16 fix8;
#define FIX_ONE ((fix8)256)
#define FIX_HALF ((fix8)128)
typedef __packed struct {
fix8 x;
fix8 y;
} Vec2;
typedef __packed struct {
s16 x;
s16 y;
s16 w;
s16 h;
} KQRect;
// Convert an integer to signed 8.8 fixed point by moving it into the high byte.
// The caller must keep the result within the representable 16-bit range.
fix8 fix_from_int(s16 n);
// Discard the eight fractional bits using a signed right shift.
s16 fix_to_int(fix8 x);
// Approximate 8.8 multiplication by discarding four low bits from each operand
// before multiplying. This trades precision for a smaller intermediate product.
fix8 fix_mul(fix8 a, fix8 b);
// Scale the reduced-precision quotient back to 8.8 and return zero for b == 0.
// Callers must also ensure (b >> 4) is nonzero; the explicit check only tests b.
// The intermediate product must fit the compiler's integer arithmetic.
fix8 fix_div(fix8 a, fix8 b);
// Interpolate toward b using a quantized Q0.8 weight. Both shifts discard low
// bits, so small changes may vanish and a weight of 255 need not reach b.
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8);
// Return the magnitude in s16. The most-negative s16 has no positive s16
// representation and must be excluded by callers needing a nonnegative result.
s16 kq_abs_s16(s16 v);
// Select the smaller signed value.
s16 kq_min_s16(s16 a, s16 b);
// Select the larger signed value.
s16 kq_max_s16(s16 a, s16 b);
// Clamp to an inclusive interval; callers must provide lo <= hi.
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi);
// Test overlap using exclusive right/bottom edges; touching edges do not collide.
// Require positive dimensions and edge sums that fit s16; zero-sized rectangles
// are not explicitly rejected and may be reported as overlapping.
u8 kq_rect_intersect(KQRect a, KQRect b);
// Test a half-open rectangle: left/top edges are included, right/bottom excluded.
// The coordinate-plus-size sums must remain representable as s16.
u8 kq_point_in_rect(s16 x, s16 y, KQRect r);kitaqgb/lib/fixed.h
input.h — ボタンの現在値・押下・解放・リピート
#pragma once
#define BTN_RIGHT ((u8)0x01)
#define BTN_LEFT ((u8)0x02)
#define BTN_UP ((u8)0x04)
#define BTN_DOWN ((u8)0x08)
#define BTN_A ((u8)0x10)
#define BTN_B ((u8)0x20)
#define BTN_SELECT ((u8)0x40)
#define BTN_START ((u8)0x80)
// A fresh press pulses immediately. The next pulse occurs DELAY + 1 updates later.
#ifndef INPUT_REPEAT_DELAY
#define INPUT_REPEAT_DELAY ((u8)18)
#endif
// Subsequent pulses are RATE + 1 updates apart; zero repeats on every held update.
#ifndef INPUT_REPEAT_RATE
#define INPUT_REPEAT_RATE ((u8)5)
#endif
extern u8 kq_input_prev_keys;
extern u8 kq_input_keys;
extern u8 kq_input_press_keys;
extern u8 kq_input_release_keys;
extern u8 kq_input_repeat_keys;
u16 __readpadex(u8 prev_keys);
// Clear sampled/edge state and initialize an independent repeat timer for each button.
void input_init();
// Sample input once per game tick and derive press, release and repeat masks.
// Repeat delays are measured in calls to this function, not elapsed wall-clock time.
void input_update();
// Return whether any masked button is currently held.
u8 input_down(u8 mask);
// Return whether any masked button became pressed in the latest sample.
u8 input_pressed(u8 mask);
// Return whether any masked button became released in the latest sample.
u8 input_released(u8 mask);
// Return whether any masked button produced a fresh press or scheduled repeat pulse.
u8 input_repeat(u8 mask);
// Return the complete held-button mask from the latest update.
u8 input_current();
// Return the held-button mask that preceded the latest update.
u8 input_previous();
#define btn_down input_down
#define btn_pressed input_pressed
#define btn_released input_released
#define btn_repeat input_repeatkitaqgb/lib/input.h
link.h — シリアル通信と論理パケット
#pragma once
// Shared Game Boy serial link library for KITAQGB projects.
//
// Integration notes:
// - Compile a hardware-register file before link.c / link_packet.c so SB, SC,
// IF_REG, and IE_REG are already declared.
// - Call Link_InitMaster() on the clock-source side and Link_InitSlave() on the
// other side.
// - Call Link_Poll() or Link_PollPacket() regularly from the main loop.
// - If you enable serial IRQ mode, call Link_OnSerialIRQ() from your own
// serial interrupt handler at vector 0x0058.
// - The packet layer is intentionally small and one-deep. It works best as a
// master-driven command/response layer. The low-level byte API remains the
// escape hatch for custom protocols.
#define LINK_MODE_SLAVE ((u8)0)
#define LINK_MODE_MASTER ((u8)1)
#define LINK_CLOCK_NORMAL ((u8)0)
#define LINK_CLOCK_FAST ((u8)1)
#define LINK_ERR_NONE ((u8)0)
#define LINK_ERR_BUSY ((u8)1)
#define LINK_ERR_TIMEOUT ((u8)2)
#define LINK_ERR_OVERFLOW ((u8)3)
#define LINK_ERR_PROTOCOL ((u8)4)
#define LINK_ERR_DISCONNECT ((u8)5)
#define LINK_PKT_MAX_PAYLOAD ((u8)24)
#define LINK_RX_QUEUE_SIZE ((u8)8)
#define LINK_TX_QUEUE_SIZE ((u8)8)
#define LINK4_MODE_NONE ((u8)0)
#define LINK4_MODE_HOST ((u8)1)
#define LINK4_MODE_PEER ((u8)2)
#define LINK4_MAX_PLAYERS ((u8)4)
#define LINK4_HOST_SLOT ((u8)0)
#define LINK4_INVALID_SLOT ((u8)0xFF)
// Reset byte queues, packet mailboxes, cooperative-peer state and errors, then
// disable serial transfers and serial IRQ delivery. Existing data is discarded.
void Link_InitCommon();
// Reset link state and select the internal-clock role for subsequent transfers.
void Link_InitMaster();
// Reset link state and select the external-clock role for subsequent transfers.
void Link_InitSlave();
// Enable or disable serial IRQ delivery and clear its pending flag. Enabling
// does not install the handler. Leaving its outer critical section enables CPU interrupts.
void __stackcall Link_SetUseInterrupt(u8 on);
// Select the fast-clock bit for future master transfers; this does not restart
// an active transfer or verify that the hardware supports CGB fast mode.
void __stackcall Link_SetFastClock(u8 on);
// Poll once before returning busy state, so this query may collect a completed byte.
u8 Link_IsBusy();
// Read the ordinary receive-ready flag without polling hardware.
u8 Link_HasByte();
// Return the latest latched error without clearing it.
u8 Link_LastError();
// Clear the error latch without resetting queues or an active transfer.
void Link_ClearError();
// Start one full-duplex byte exchange, or return BUSY without replacing an
// active transfer. Slave mode arms reception and waits for external clocks.
u8 __stackcall Link_BeginTransfer(u8 out);
// Collect a pending transfer only after hardware clears the SC start bit.
void Link_Poll();
// Stop the hardware transfer under interrupt protection. Queued receive bytes
// and packet-layer state remain available; this is not a full protocol reset.
void Link_Cancel();
// Acknowledge serial IRQ and finish a software-busy transfer. Call this from
// the actual serial interrupt handler, not an unrelated interrupt.
void Link_OnSerialIRQ();
// Pop the ordinary receive queue without waiting or polling hardware; null discards a byte.
u8 __stackcall Link_TryReadByte(u8 *out);
// Pop one byte, returning zero when empty. Use Link_TryReadByte when a received
// zero byte must be distinguished from an empty queue.
u8 Link_ReadByte();
// Require an output pointer, poll immediately, then wait at most timeout_frames
// VBlanks for a byte. Zero timeout still performs the initial poll and read.
u8 __stackcall Link_WaitByte(u16 timeout_frames, u8 *out);
// Validate payload size and idle packet state, copy payload bytes into owned
// storage and schedule transmission. Return success means queued, not acknowledged;
// keep polling to progress the exchange and inspect the error latch.
u8 __stackcall Link_SendPacket(const u8 *data, u8 len, u8 cmd);
// Drain received bytes, advance one outgoing exchange and update the retry
// timer. Timeout ticks count calls without progress, not hardware frames; call
// with a controlled cadence and avoid mixing raw reads with packet consumption.
void Link_PollPacket();
// Inspect the shared completed-packet flag without advancing the protocol.
u8 Link_HasPacket();
// Consume the shared packet mailbox and its matching peer flag. Any output
// may be null; a nonnull destination needs room for the received payload because
// there is no destination-capacity argument.
u8 __stackcall Link_ReadPacket(u8 *cmd, u8 *len, u8 *dst);
// Cooperative 4-player adapter helpers.
//
// This layer models the common host-selected adapter pattern:
// - Slot 0 is always the host.
// - The host selects one active peer slot at a time.
// - Packet mailboxes stay one-deep per peer.
// - The original Link_* API remains available for normal 2-player projects.
// Reset as master, validate slot count and select logical peer one. Invalid configuration still discards old link state.
u8 __stackcall Link4_InitHost(u8 slot_count);
// Reset as slave and validate the local nonzero slot and total slot count.
// An invalid configuration still discards the previous link state.
u8 __stackcall Link4_InitPeer(u8 local_slot, u8 slot_count);
// Return the configured cooperative role; this is not a hardware connection probe.
u8 Link4_GetMode();
// Return the configured zero-based local slot.
u8 Link4_GetLocalSlot();
// Return the configured number of logical slots, not a live connection count.
u8 Link4_GetSlotCount();
// Return the current logical destination, or the invalid-slot sentinel when unset.
u8 Link4_GetSelectedPeer();
// Return the peer attributed to the last completed byte transfer.
u8 Link4_GetLastRxPeer();
// Return the peer attributed when the last byte transfer was started.
u8 Link4_GetLastTxPeer();
// Poll completion, then change the logical destination only while byte and
// packet state are idle. A peer can select only the host. No adapter-routing
// command is sent here; the transport must provide the matching physical route.
u8 __stackcall Link4_SelectPeer(u8 peer_slot);
// Select the logical peer and start a byte exchange, returning the first error.
u8 __stackcall Link4_BeginTransferTo(u8 peer_slot, u8 out);
// Inspect a valid peer ring without polling the hardware.
u8 __stackcall Link4_HasByteFrom(u8 peer_slot);
// Pop one byte from the selected peer ring; null output discards it.
u8 __stackcall Link4_TryReadByteFrom(u8 peer_slot, u8 *out);
// Scan remote rings in increasing slot order and return the first available
// byte. Null output pointers are allowed; lower slots have polling priority.
u8 __stackcall Link4_TryReadByteAny(u8 *peer_slot, u8 *out);
// Select a logical peer, then copy and schedule a packet; return the first error.
u8 __stackcall Link4_SendPacketTo(u8 peer_slot, const u8 *data, u8 len, u8 cmd);
// Advance the shared packet engine; this does not poll every logical peer in turn.
void Link4_PollPacket();
// Inspect the one-packet mailbox for a valid remote slot without consuming it.
u8 __stackcall Link4_HasPacketFrom(u8 peer_slot);
// Consume a peer mailbox, copying to optional outputs. A nonnull destination
// needs room for the entire payload. Clear the shared mailbox only when it
// refers to this same peer.
u8 __stackcall Link4_ReadPacketFrom(u8 peer_slot, u8 *cmd, u8 *len, u8 *dst);kitaqgb/lib/link.h
link_dmg07.h — DMG-07の外部クロック通信
#pragma once
// Polling driver for the physical Nintendo DMG-07 Four Player Adapter.
//
// The adapter supplies every serial clock. LinkDmg07_Poll() must therefore be
// called frequently enough to re-arm the serial port between adapter bytes;
// calling it only once per video frame is not sufficient. Call
// LinkDmg07_TickFrame() exactly once per frame for timeout accounting.
//
// Transmission uses SIZE=1: every adapter packet contains four bytes, one for
// each physical player slot. A console sends its local byte during byte 0 and
// sends zero during bytes 1..3, except while emitting an aligned FF restart
// packet. The adapter broadcasts those four submitted bytes in the following
// packet. The initial broadcast contains undefined adapter data and is
// discarded by this driver.
#define LINK_DMG07_PHASE_PING ((u8)0)
#define LINK_DMG07_PHASE_REQUEST_AA ((u8)1)
#define LINK_DMG07_PHASE_WAIT_CC ((u8)2)
#define LINK_DMG07_PHASE_TRANSFER ((u8)3)
#define LINK_DMG07_MAX_PLAYERS ((u8)4)
#define LINK_DMG07_PACKET_BYTES ((u8)4)
#define LINK_DMG07_SIZE ((u8)1)
// Physical slots here are one-based; do not substitute the zero-based Link4 cooperative slot identifiers.
#define LINK_DMG07_NO_SLOT ((u8)0)
// RATE $10 gives the shortest documented packet period for SIZE=1 while
// retaining the adapter's normal inter-byte pacing. RATE $00 is not used.
#define LINK_DMG07_DEFAULT_RATE ((u8)0x10)
#ifndef LINK_DMG07_TIMEOUT_FRAMES
#define LINK_DMG07_TIMEOUT_FRAMES ((u8)12)
#endif
#define LINK_DMG07_ERR_NONE ((u8)0)
#define LINK_DMG07_ERR_STATE ((u8)1)
#define LINK_DMG07_ERR_NOT_PLAYER1 ((u8)2)
#define LINK_DMG07_ERR_NOT_READY ((u8)3)
#define LINK_DMG07_ERR_TIMEOUT ((u8)4)
#define LINK_DMG07_ERR_PROTOCOL ((u8)5)
#define LINK_DMG07_ERR_OVERFLOW ((u8)6)
#define LINK_DMG07_RESTART_NONE ((u8)0)
#define LINK_DMG07_RESTART_PENDING ((u8)1)
#define LINK_DMG07_RESTART_SENDING ((u8)2)
// Initializes polling-only external-clock operation. A zero RATE is replaced
// with LINK_DMG07_DEFAULT_RATE. This driver disables only the serial IRQ bit.
void __stackcall LinkDmg07_Init(u8 rate);
// Services at most one completed serial byte and immediately re-arms SC=$80.
// Call continuously from the main loop, including between VBlanks.
void LinkDmg07_Poll();
// Advances saturating frame-based silence and handshake timeout counters.
void LinkDmg07_TickFrame();
// Return the discovery, start-handshake or transfer phase without polling.
u8 LinkDmg07_GetPhase();
// Return the one-based player slot, or LINK_DMG07_NO_SLOT before discovery.
u8 LinkDmg07_GetLocalSlot();
// Normalized mask: bit 0 is player 1, ..., bit 3 is player 4.
u8 LinkDmg07_GetConnectedMask();
// Return the last validated raw adapter status byte.
u8 LinkDmg07_GetLastStatus();
// Read the completed-discovery status flag without clearing it.
u8 LinkDmg07_HasPingStatus();
// Clear and report the completed-discovery flag; return zero if no new status exists.
u8 LinkDmg07_ConsumePingStatus();
// Only physical player 1 may request AA AA AA AA. The bytes are aligned to
// the next FE/status/status/status ping packet. All consoles enter transfer
// mode only after receiving four consecutive CC bytes.
u8 LinkDmg07_RequestTransmission();
// Any physical slot may schedule FF FF FF FF at the next four-byte packet
// boundary while remaining in TRANSFER phase. The driver keeps sending aligned
// FF bytes until it sees the adapter's complete all-FF indicator packet, then
// returns to PING. A transfer-phase silence timeout schedules the same recovery
// automatically and preserves the current byte position for clocks that resume.
u8 LinkDmg07_RequestRestart();
// Return whether aligned recovery is absent, pending or sending control traffic.
u8 LinkDmg07_GetRestartState();
// Sets the byte sampled when byte 0 of the next adapter packet is armed.
// Set it before the current packet completes (and before requesting start for
// the first submission). During normal transfer, bytes 1..3 are sent as zero.
void __stackcall LinkDmg07_SetLocalByte(u8 value);
// The first incoming adapter data packet is discarded. Thereafter ReadPacket
// copies four bytes in physical slot order (players 1..4).
u8 LinkDmg07_IsPipelinePrimed();
// Inspect the one-packet receive mailbox without consuming it.
u8 LinkDmg07_HasPacket();
// Consume the latest four-byte packet. A nonnull destination must hold four
// bytes; null discards the packet. Packet storage remains after the flag clears.
u8 __stackcall LinkDmg07_ReadPacket(u8 *dst);
// Read one cached player byte using a one-based slot. Invalid slots return
// zero; the function neither requires readiness nor consumes the packet.
u8 __stackcall LinkDmg07_GetPacketSlot(u8 player_slot);
// SentSequence increments when this console's packet byte is clocked out.
// PacketSequence labels the returned four-byte packet. For a valid packet it
// is one less than SentSequence, making the DMG-07 one-packet delay explicit.
u8 LinkDmg07_GetSentSequence();
// Return the modulo-256 sequence associated with the most recently published packet.
u8 LinkDmg07_GetPacketSequence();
// SilenceFrames is reset by every completed serial byte. TimeoutCount counts
// distinct serial-silence or AA/CC/restart handshake timeouts. DisconnectCount counts
// adapter-silence outages and ping-status membership drops. All saturate.
u8 LinkDmg07_GetSilenceFrames();
// Return the saturating lifetime timeout count, preserved across protocol restarts.
u8 LinkDmg07_GetTimeoutCount();
// Return the saturating count of removed-player and previously-seen-adapter timeout events.
u8 LinkDmg07_GetDisconnectCount();
// Return the saturating protocol/overflow error count. Timeout and request
// validation paths use separate counters or latches and do not increment this value.
u8 LinkDmg07_GetErrorCount();
// Return the latest error latch without clearing counters or restarting the protocol.
u8 LinkDmg07_LastError();
// Clear only the latest error code, preserving counters and protocol state.
void LinkDmg07_ClearError();kitaqgb/lib/link_dmg07.h
physics2d.h — 2Dの運動と衝突
#pragma once
// Position and velocity use caller-selected integer units with one integration
// step per call; no hidden pixel or time conversion occurs. Keep intermediate
// signed arithmetic in range. World/body storage is owned by the caller.
#include "fixed.h"
// Multiply a signed word by a signed byte; divide by 256, truncating toward zero.
s16 __smul16x8(s16 a, s8 b);
// Lightweight deterministic 2D physics for KITAQGB.
// Integer pixels per step are one possible unit convention; inv_mass is Q8.
typedef struct {
s16 x;
s16 y;
s16 vx;
s16 vy;
s16 half_x;
s16 half_y;
s16 inv_mass_q8; // 0 means static body.
s16 restitution_q8; // 0..255 (reserved for future tuning).
u8 active;
} KQBody2D;
typedef struct {
KQBody2D* bodies;
u8 body_count;
s16 gravity_x;
s16 gravity_y;
u8 solver_iterations;
s16 max_speed;
} KQWorld2D;
// Retain the caller-owned body array without initializing its elements.
// Set gravity to (0,32), four contact iterations and a per-axis speed limit of 512.
// A null world is ignored; the array must outlive subsequent world operations.
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count);
// Advance active dynamic bodies, then visit each active unordered pair for
// the configured contact passes (zero means one). Static bodies still participate
// as obstacles. The world borrows its array and shares non-reentrant ASM scratch.
// This is discrete integration; fast bodies can pass through thin obstacles.
void kq2d_step(KQWorld2D* world);
// For one active dynamic body, add gravity, clamp each velocity axis and then
// add velocity to position. Null inputs are ignored; no contacts are solved.
// Use a nonnegative speed limit and keep additions in signed 16-bit range.
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body);
// Test strict overlap of two centered boxes; touching edges return false.
// Both pointers must be valid. Active/mass flags are ignored, and coordinate
// differences and extent sums must fit the signed 16-bit calculations.
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b);
// Initialize an active unit-inverse-mass body at the supplied center and half
// extents, with zero velocity and reserved restitution set to zero. Null is ignored.
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y);
// Replace the center without altering velocity, extents or activity. Null is ignored.
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y);
// Replace velocity without clamping or checking activity/mass. Null is ignored.
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy);
// Add gravity and clamp each velocity axis separately for an active dynamic
// body. Null, inactive and static bodies are ignored. Supply a nonnegative limit
// and avoid overflow in the additions before clamping; position is unchanged.
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed);
// Scale both velocities through the signed 8-bit coefficient intrinsic.
// Despite the unsigned parameter, values 128..255 become negative coefficients;
// this is not a full-range unsigned Q8 damping helper. Null is ignored.
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8);
// Delegate strict rectangle overlap to the fixed-point utility; edge-only
// contact is false. Coordinate-plus-size sums must remain representable as s16.
u8 kq2d_rect_intersect(KQRect a, KQRect b);
// Delegate the half-open point test: include left/top and exclude right/bottom.
// Coordinate-plus-size sums must remain representable as s16.
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r);
// Opt-in surface solver. Velocities/threshold/kick share the caller's units.
// Normal must be unit length in Q8; coefficients are 0..255.
typedef struct {
s16 nx_q8;
s16 ny_q8;
s16 vx;
s16 vy;
s16 bounce_threshold;
s16 kick;
u8 restitution_q8;
u8 friction_q8;
} KQSurface2D;
// Truncates toward zero, including negative values; coefficient is -256..256.
s16 kq2d_scale_q8(s16 value, s16 coefficient);
// Conservative circular bound, with a shared Q8 scale for both components.
// Quantizes toward zero; very small limits relative to input may yield zero.
// Components and positive max_speed must be <= 16383 in magnitude.
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);
// Kinematic surface response; returns incoming normal speed, or zero if separating.
// Caller resolves penetration separately. Relative components must fit +/-8191.
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface);
// Linear crossing time in Q8 (0..256); positive separation is outside.
// Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap);kitaqgb/lib/physics2d.h
physics2d_circle.h — 円同士の物理計算
#pragma once
// Deterministic 2D circle physics for KITAQGB.
// Intended for top-down ball games such as billiards, air hockey, and marbles.
// Use consistent caller-selected units for centers, radii and per-step velocity.
// The implementation uses 16-bit arithmetic: reduced squared-distance sums and
// weight products must fit their types. Reduction to byte-sized components alone
// is insufficient to guarantee this. Body storage remains caller-owned.
typedef struct {
s16 x;
s16 y;
s16 vx;
s16 vy;
s16 radius;
s16 inv_mass_q8; // 0 means static body.
u8 restitution_q8; // 0..255, higher means bouncier.
u8 friction_q8; // 0..255, higher removes more tangential motion.
u8 active;
} KQCircleBody2D;
typedef struct {
KQCircleBody2D* bodies;
u8 body_count;
s16 gravity_x;
s16 gravity_y;
u8 solver_iterations;
s16 max_speed;
u8 linear_damping_q8; // 255 is almost no damping, lower values damp faster.
u8 use_bounds;
s16 min_x;
s16 min_y;
s16 max_x;
s16 max_y;
u8 wall_restitution_q8;
u8 wall_friction_q8;
} KQCircleWorld2D;
// Retain a caller-owned array without initializing bodies. Set zero gravity,
// four contact passes, per-axis limit 768 and damping 252/256; bounds start disabled.
// A null world is ignored. Keep the array alive for subsequent steps.
void kq2dc_world_init(KQCircleWorld2D* world, KQCircleBody2D* bodies, u8 body_count);
// Store and enable bounds without validating their order or fitting bodies
// immediately. Supply extents large enough for body diameters. Null is ignored.
void kq2dc_set_bounds(KQCircleWorld2D* world, s16 min_x, s16 min_y, s16 max_x, s16 max_y);
// Integrate active dynamic bodies, apply bounds, solve contacts, then damp
// velocity and snap components in -1..1 to zero. Zero solver iterations means one.
// Pairs with both velocities zero are skipped, so resting overlaps can remain.
// Bounds can reposition static bodies; null world/array inputs are ignored.
void kq2dc_step(KQCircleWorld2D* world);
// Compare a reduced integer center distance with the reduced radius sum.
// Valid pointers and positive radii are required; activity and mass are ignored.
// This approximate test requires representable differences, radius sums and
// squared-distance sum. Touching at the computed distance returns false.
u8 kq2dc_overlap_circle(const KQCircleBody2D* a, const KQCircleBody2D* b);kitaqgb/lib/physics2d_circle.h
physics3d.h — 3DのAABB物理計算
#pragma once
// Lightweight deterministic 3D physics for KITAQGB.
// AABB contacts, acceleration, mass-weighted bounce, and impact state flags.
// The game owns break decisions: the solver records impact speeds but never
// sets this flag or deactivates a body based on break_speed. Positions, extents,
// velocities and acceleration use consistent caller-selected per-step units.
#define KQ3D_BODY_BROKEN ((u8)0x01)
typedef struct {
s16 x;
s16 y;
s16 z;
s16 vx;
s16 vy;
s16 vz;
s16 ax;
s16 ay;
s16 az;
s16 half_x;
s16 half_y;
s16 half_z;
s16 mass_q8; // 256 means mass 1.0.
s16 inv_mass_q8; // 0 means static body.
s16 restitution_q8; // 256 means perfectly elastic.
s16 break_speed; // Reserved for game-side impact thresholds.
s16 last_impact_speed;
u8 active;
u8 flags;
} KQBody3D;
typedef struct {
KQBody3D* bodies;
u8 body_count;
s16 gravity_x;
s16 gravity_y;
s16 gravity_z;
u8 solver_iterations;
s16 max_speed;
} KQWorld3D;
// Borrow the body array without initializing it. Set gravity (0,24,0), four
// contact passes and a per-axis speed limit of 512. Null world is ignored;
// the caller retains ownership and must keep the array alive.
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count);
// Initialize an active centered box with zero velocity/acceleration,
// restitution 224, cleared impact/flags and mass set through the weight helper.
// The caller supplies valid half extents. Null body is ignored.
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8);
// Store the requested mass; nonpositive mass makes the body static.
// For positive mass compute inverse weight as max(1,32767/max(mass,64)).
// The stored mass retains the original input; this is a relative solver weight,
// not an exact Q8 reciprocal. Null body is ignored.
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8);
// Integrate active dynamic bodies and reset their impact values, then solve
// each active unordered pair for the configured passes (zero means one).
// Static bodies retain previous impact maxima until the game clears them.
// Acceleration persists; flags and break thresholds remain game-owned.
// This discrete solver shares non-reentrant ASM scratch and can miss fast crossings.
void kq3d_step(KQWorld3D* world);
// Reset impact for one active dynamic body, add gravity and persistent body
// acceleration, clamp velocity per axis and advance position. Static/inactive
// bodies retain impact state. Null inputs are ignored; no contacts are solved.
// Use a nonnegative speed limit and avoid overflow before clamping.
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);
// Test strict overlap on all three axes; face-only contact returns false.
// Valid pointers are required and flags are ignored. Center differences and
// extent sums must fit the signed 16-bit magnitude calculations.
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b);
// Sum three signed products, shifting each by eight bits before addition.
// Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az);kitaqgb/lib/physics3d.h
raster.h — 帯・行単位のスクロール表
#pragma once
#include "scroll.h"
// Keep wrapper capacity consistent with the compiler split-table capacity; wrapper checks do not enlarge it.
#define KQ_RASTER_MAX_BANDS 8
#define KQ_RASTER_OK 0
#define KQ_RASTER_FULL 1
#define KQ_RASTER_BAD_LY 2
#define KQ_RASTER_UNSORTED 3
#define KQ_RASTER_BAD_RANGE 4
#define KQ_RASTER_BG KQ_SCROLL_SPLIT_USE_BG
#define KQ_RASTER_WIN KQ_SCROLL_SPLIT_USE_WIN
#define KQ_RASTER_WIN_SHOW KQ_SCROLL_SPLIT_WIN_SHOW
#define KQ_RASTER_WIN_HIDE KQ_SCROLL_SPLIT_WIN_HIDE
#define KQ_RASTER_BG_COLOR0 KQ_SCROLL_SPLIT_BG_COLOR0
#define KQ_RASTER_FLAG_MASK 0x1F
#define KQ_RASTER_LINE_FLAT 0
#define KQ_RASTER_LINE_X_TITLE 1
#define KQ_RASTER_LINE_TRAVEL_GATE 2
// Caller-owned wave state. Initialize it before use and keep it non-null for every LineX operation.
// frame_counter measures RunFrame calls, not an independent hardware clock.
typedef struct RasterLineX {
u8 profile;
u8 base_x;
u8 phase;
u8 phase_step;
u8 frame_divider;
u8 frame_counter;
} RasterLineX;
// Stop split scheduling, empty the shared table and clear error/count state.
void Raster_Init();
// Stop splits immediately and clear bookkeeping. Build and commit a new table to restart.
void Raster_Clear();
// Commit an empty split table so no raster bands remain active.
void Raster_Disable();
// Read the number of bands currently recorded by this wrapper.
u8 Raster_GetCount();
// Read the last stored status without clearing it.
u8 Raster_GetLastError();
// Append a validated background-scroll band and advance the monotonic-LY
// bookkeeping. A successful append clears the previous error.
u8 Raster_Push(u8 ly, u8 scx, u8 scy);
// Append a validated extended band after masking flags to supported operations.
u8 Raster_PushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);
// Enable window updates and pass hardware WX/WY values without coordinate conversion.
u8 Raster_PushWindowRaw(u8 ly, u8 wx, u8 wy, u8 flags);
// Convert screen X to hardware WX by adding seven, then queue a window band.
// The byte addition wraps if callers supply an excessive X coordinate.
u8 Raster_PushWindowScreen(u8 ly, u8 screen_x, u8 screen_y, u8 flags);
// Queue both background and window updates, converting window screen X to WX.
u8 Raster_PushBgWindowScreen(u8 ly, u8 scx, u8 scy, u8 screen_x, u8 screen_y, u8 flags);
// Pack a 15-bit color into the extended band's two scroll-byte fields and select
// the color-zero operation, which interprets those fields as color data.
u8 Raster_PushBgColor0(u8 ly, u16 rgb15);
// Publish pending splits and return the stored status. This commits even if an
// earlier append failed; callers should inspect errors while building the table.
u8 Raster_Commit();
// Validate the HUD height, then replace pending splits with a fixed top band
// and scrolling playfield. The new table still requires an explicit commit.
u8 Raster_BuildHudTop(u8 hud_height, u8 camera_x, u8 camera_y);
// Build a scrolling upper playfield and fixed bottom HUD after validating the split.
u8 Raster_BuildHudBottom(u8 hud_y, u8 camera_x, u8 camera_y);
// Build two horizontal-scroll bands sharing one vertical scroll value.
u8 Raster_BuildParallax2(u8 split_y, u8 far_scx, u8 near_scx, u8 scy);
// Build three horizontal-scroll bands at ordered visible split lines. Invalid
// parameters leave the previous pending table intact and set the error status.
u8 Raster_BuildParallax3(u8 y1, u8 y2, u8 scx0, u8 scx1, u8 scx2, u8 scy);
// Initialize a scanline-wave state with phase zero and one phase step per frame.
void Raster_LineXInit(RasterLineX* effect, u8 profile, u8 base_x);
// Change the wave profile and restart its phase/divider counter.
void Raster_LineXSetProfile(RasterLineX* effect, u8 profile);
// Configure the phase increment and frame interval; clamp a zero interval to one.
void Raster_LineXSetSpeed(RasterLineX* effect, u8 phase_step, u8 frame_divider);
// Set the byte phase directly without resetting the frame-divider counter.
void Raster_LineXSetPhase(RasterLineX* effect, u8 phase);
// Look up the selected periodic profile at LY + phase and add base X modulo
// 256. An unknown profile returns the unmodified base X.
u8 Raster_LineXGetOffset(const RasterLineX* effect, u8 ly);
// Render all 144 visible lines with one SCX write per line, then advance phase.
// This foreground renderer masks interrupts during the visible frame. Do game
// and audio work between calls; do not combine it with a STAT raster driver.
// Returns without changing the effect when the LCD is off.
void Raster_LineXRunFrame(RasterLineX* effect);kitaqgb/lib/raster.h
rpg.h — 乱数、文字、マップ、セーブ、RPG/ADV/SLG
#pragma once
// The application supplies frame/cursor/font tiles at these indices; these constants do not load graphics.
#ifndef TEXT_TILE_FILL
#define TEXT_TILE_FILL ((u8)0)
#endif
#ifndef TEXT_TILE_FRAME_TL
#define TEXT_TILE_FRAME_TL ((u8)1)
#endif
#ifndef TEXT_TILE_FRAME_TR
#define TEXT_TILE_FRAME_TR ((u8)2)
#endif
#ifndef TEXT_TILE_FRAME_BL
#define TEXT_TILE_FRAME_BL ((u8)3)
#endif
#ifndef TEXT_TILE_FRAME_BR
#define TEXT_TILE_FRAME_BR ((u8)4)
#endif
#ifndef TEXT_TILE_FRAME_H
#define TEXT_TILE_FRAME_H ((u8)5)
#endif
#ifndef TEXT_TILE_FRAME_V
#define TEXT_TILE_FRAME_V ((u8)6)
#endif
#ifndef TEXT_TILE_CURSOR
#define TEXT_TILE_CURSOR ((u8)7)
#endif
#ifndef TEXT_TILE_PAGE_WAIT
#define TEXT_TILE_PAGE_WAIT ((u8)8)
#endif
#ifndef TEXT_DIGIT_BASE
#define TEXT_DIGIT_BASE ((u8)'0')
#endif
#define TEXT_CTRL_END ((u8)0x00)
#define TEXT_CTRL_NEWLINE ((u8)0x0A)
#define TEXT_CTRL_WAIT ((u8)0x01)
#define TEXT_CTRL_PAGE_WAIT ((u8)0x02)
#define PAD_KEY_RIGHT ((u8)0x01)
#define PAD_KEY_LEFT ((u8)0x02)
#define PAD_KEY_UP ((u8)0x04)
#define PAD_KEY_DOWN ((u8)0x08)
#define PAD_KEY_A ((u8)0x10)
#define PAD_KEY_B ((u8)0x20)
#define PAD_KEY_SELECT ((u8)0x40)
#define PAD_KEY_START ((u8)0x80)
// Default layout: four 512-byte slots, each with a six-byte header and at most 506 payload bytes.
#define SAVE_SLOT_COUNT ((u8)4)
#define SAVE_SLOT_SIZE ((u16)512)
#define SAVE_MAGIC0 ((u8)0x4B)
#define SAVE_MAGIC1 ((u8)0x51)
#define SAVE_VERSION ((u8)1)
#define SAVE_OK ((u8)1)
#define SAVE_ERR ((u8)0)
// A bank byte plus a CPU-visible address forms this data reference; it does not switch banks by itself.
typedef __packed struct {
u8 bank;
const u8* ptr;
} far_ptr_t;
typedef __packed struct {
u8 x;
u8 y;
u8 w;
u8 h;
const u8* const* items;
u8 count;
u8 cursor;
} menu_t;
typedef __packed struct {
u8 id;
u8 qty;
const u8* name;
} item_t;
// Packed nine-byte ordinary-map descriptor. Tile IDs use bank_tiles; collision bits and event bytes
// use the descriptor bank. Width/height are tile cells, with row-major data.
typedef __packed struct {
u8 width;
u8 height;
u8 bank_tiles;
const u8* tiles;
const u8* collision_bits;
const void* events;
} map_t;
// One 2x2 metatile: four tile IDs plus collision, palette metadata and event ID.
// The map drawing routine uses tile IDs only; it does not apply palette attributes.
typedef __packed struct {
u8 tile_tl;
u8 tile_tr;
u8 tile_bl;
u8 tile_br;
u8 collision;
u8 palette;
u8 event;
} metatile_t;
// Metatile dimensions count 2x2 cells. bank_tiles=0 means use the descriptor bank;
// nonzero bank_tiles supplies both ID-array and definition-table reads.
typedef __packed struct {
u8 width;
u8 height;
u8 bank_tiles;
const u8* metatile_ids;
const metatile_t* metatiles;
} metatile_map_t;
typedef __packed struct {
u8 x;
u8 y;
u8 w;
u8 h;
const u8* const* items;
u8 count;
u8 cursor;
u8 prev_keys;
u8 selected;
u8 cancelled;
} menu_state_t;
typedef __packed struct {
u8 x;
u8 y;
u8 hp;
u8 move;
u8 atk_min;
u8 atk_max;
u8 team;
u8 acted;
} unit_t;
void __wait_vblank();
u16 __readpadex(u8 prev_keys);
u16 __getbgmapbase();
void __memset(void* dst, u8 value, u16 count);
void __memcpy(void* dst, const void* src, u16 count);
void __settile(u8 x, u8 y, u8 tile);
void __settilebg(u8 x, u8 y, u8 tile);
void __settileat(u16 base, u8 x, u8 y, u8 tile);
void __vram_memcpy(u16 dst, const void* src, u16 len);
void __vram_memset(u16 dst, u8 value, u16 len);
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);
u8 __rng8();
void __rng_seed(u16 seed);
void __scroll_bg_set(u8 scx, u8 scy);
void __settile_xy(u8 x, u8 y, u8 tile);
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
void __settile_row(u8 x, u8 y, const u8* src, u8 len);
void __settile_col(u8 x, u8 y, const u8* src, u8 len);
void __fill_tilemap(u16 base, u8 tile, u16 count);
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);
void __vram_copy(u16 dst, const void* src, u16 len);
void __vram_fill(u16 dst, u8 value, u16 len);
void __vram_copy_hblank(u16 dst, const void* src, u16 len);
void __vram_copy_dma(u16 dst, const void* src, u16 len);
u8 __farpeek8(u8 bank, const void* addr);
u16 __farpeek16(u8 bank, const void* addr);
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);
// The current GB backend evaluates/discards these arguments but emits no indirect call.
void __farcall_ptr(u8 bank, const void* func);
#define far_read8(p) __farpeek8((p).bank, (p).ptr)
#define far_read16(p) __farpeek16((p).bank, (p).ptr)
#define far_read_block(dst, p, len) __farmemcpy((dst), (p).bank, (p).ptr, (len))
void __memcpy_small(void* dst, const void* src, u8 len);
void __memset_small(void* dst, u8 value, u8 len);
void __copy16(void* dst, const void* src);
void __copy32(void* dst, const void* src);
u16 __tile_addr(u16 base, u8 x, u8 y);
u16 __map_index(u8 x, u8 y, u8 width);
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);
u8 __bit_test(const u8* p, u16 bit_index);
void __bit_set(u8* p, u16 bit_index);
void __bit_clear(u8* p, u16 bit_index);
void __bit_toggle(u8* p, u16 bit_index);
// Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);
// Advance the compiler-provided pseudorandom generator and return one byte.
u8 rng8();
// Consume two generator bytes in high-byte/low-byte order to form a 16-bit value.
u16 rng16();
// Reduce a random byte modulo max; zero returns zero without consuming randomness.
// Modulo reduction is biased unless max divides the generator range.
u8 rand_range(u8 max);
// Select an index from cumulative byte weights using a 16-bit random draw.
// A zero total, including an empty list, returns zero; callers must distinguish
// that case from a valid index. Modulo reduction can introduce a small bias.
u8 weighted_choice(const u8* weights, u8 count);
// Replace the generator seed so subsequent calls can reproduce a sequence.
void rng_seed(u16 seed);
// Alias the byte generator; this consumes one generator step.
u8 rng_next8();
// Alias the two-step 16-bit generator.
u16 rng_next16();
// Alias rand_range and return a value in [0,max), or zero when max is zero.
u8 rng_range(u8 max);
// Return false for zero and true for values at least 100. Intermediate values
// use the modulo-based range generator, so percentages are approximate.
u8 rng_chance(u8 percent);
// Set the tile-space window, reset its cursor and redraw it immediately.
// Opening does not reset the existing character speed or load font tiles.
void text_open(u8 x, u8 y, u8 w, u8 h);
// Erase the open window with the fill tile and disable windowed character output.
void text_close();
// Decode an END-terminated stream of tile bytes and newline/wait/page controls.
// WAIT consumes a following frame-count byte. The source must remain readable
// through blocking waits; the routine does not validate stream length.
void text_print(const u8* str);
// Decode the same text controls while fetching each byte from the specified
// ROM bank. The pointer advances within that bank; it does not roll into the next bank.
void text_print_far(u8 bank, const u8* str);
// Redraw choices and block for edge-triggered navigation: UP/DOWN wrap, A
// accepts, and B returns 0xFF. Zero choices also returns 0xFF. Choice strings
// use raw tile bytes, and the caller must fit them inside the available rows.
u8 text_choice(const u8* const* choices, u8 count);
// Set VBlank waits per windowed character; zero prints without character delays.
void text_set_speed(u8 speed);
// Open and redraw a window using the same tile-space arguments as text_open.
void text_window(u8 x, u8 y, u8 w, u8 h);
// Fill the supplied tile rectangle without changing the active text window or cursor.
void text_clear_rect(u8 x, u8 y, u8 w, u8 h);
// Draw a zero-terminated tile string at absolute tile coordinates, without
// window clipping, control codes or the windowed printing delay.
void text_print_xy(u8 x, u8 y, const u8* str);
// Print an unsigned byte in decimal using only the required number of cells.
void text_print_u8(u8 x, u8 y, u8 value);
// Print an unsigned word in decimal using only the required number of cells.
void text_print_u16(u8 x, u8 y, u16 value);
// Print a minus tile for negative values, followed by the unsigned magnitude.
// Positive values use the unsigned decimal path; no plus sign is emitted.
void text_print_s16(u8 x, u8 y, s16 value);
// Replace the active script and clear wait, choice and saved-return state.
void script_run(u8 bank, const u8* script);
// Execute commands until an output/choice yields (0), a wait yields (1), or
// execution ends (2). Control/flag commands continue within the same call, so
// a script loop without a yielding command can block the game loop indefinitely.
u8 script_step();
// Set the number of subsequent script_step calls to spend waiting. Frame-based
// timing requires the application to call script_step once per frame.
void script_wait_frames(u8 n);
// Run a blocking menu loop with wraparound up/down selection, A to accept
// and B to cancel. Return 0xFF for an empty menu or cancellation. Item strings
// and menu dimensions must fit the visible tile area.
u8 menu_run(const menu_t* menu);
// Open a two-choice dialog and return the text-choice result (YES first, NO second).
u8 menu_yesno();
// Run a blocking item menu, drawing names and two-digit quantities. Return
// the selected index or 0xFF on empty input/B; this does not consume an item.
u8 menu_inventory(const item_t* items, u8 count);
// Initialize a nonblocking menu state that borrows the item pointer table.
// Selection starts unset (0xFF), and the cancellation flag starts clear.
void menu_init_state(menu_state_t* state, u8 x, u8 y, u8 w, u8 h, const u8* const* items, u8 count);
// Draw the current menu and cursor without waiting or sampling input. The
// caller schedules suitable display timing and keeps item text within bounds.
void menu_draw(menu_state_t* state);
// Sample new button presses and update cursor/selection state without drawing.
// Selected and cancelled results remain latched; a B press in the same update
// as A clears selection and leaves cancellation set.
void menu_update(menu_state_t* state);
// Read the latched selection without clearing it; null state returns 0xFF.
u8 menu_get_selected(menu_state_t* state);
// Read the latched cancellation flag; null state returns false.
u8 menu_was_cancelled(menu_state_t* state);
// Load the nine-byte ordinary-map descriptor and draw up to 20 by 18 tiles
// from its top-left corner. Drawing does not upload tile graphics or clear
// background cells beyond smaller map dimensions.
void map_load(u8 bank, const map_t* map);
// Use metatile collision when that mode is active; otherwise read one packed
// collision bit. Missing maps/out-of-range cells block movement, but a valid
// ordinary map without a collision array is entirely passable.
u8 map_is_blocked(u8 x, u8 y);
// Report whether an event exists at the cell. Ordinary maps expose a Boolean
// byte-presence test; metatile maps reduce their event ID to a Boolean here.
u8 map_trigger_at(u8 x, u8 y);
// Center an ordinary-map tile coordinate within a 20-by-18 viewport and clamp
// the scroll origin to its edges. This adjusts scroll registers only; it does
// not stream newly exposed map rows into VRAM.
void camera_center(u8 x, u8 y);
// Select metatile mode, copy its descriptor and draw the initial viewport
// with the camera at cell (0,0).
void map_load_metatile(u8 bank, const metatile_map_t* map);
// Draw a 10-by-9 metatile viewport as 20-by-18 individual background tiles.
// Out-of-range cells use definition zero; palette attributes are not written.
// Remember the camera and clear the dirty flag after the full redraw.
void map_draw_visible(u8 camera_tx, u8 camera_ty);
// Read a row-major metatile ID, returning ID zero for inactive or out-of-range
// lookups. Callers must provide a usable definition for metatile zero.
u8 map_get_metatile(u8 tx, u8 ty);
// Return the selected definition's palette byte. An out-of-range coordinate
// uses the metatile-zero fallback; this function does not write CGB attributes.
u8 map_get_attr(u8 tx, u8 ty);
// Treat out-of-range metatile cells as solid; otherwise inspect the definition
// collision flag. In ordinary-map mode, delegate to its packed collision bits.
u8 map_is_solid(u8 tx, u8 ty);
// Return a metatile event ID, or the ordinary-map Boolean trigger result.
// Missing/out-of-range metatile cells have no event.
u8 map_get_event(u8 tx, u8 ty);
// Mark the viewport for a full redraw when a valid metatile cell changes.
// The coordinate is validated but no per-cell dirty list is retained.
void map_mark_dirty(u8 tx, u8 ty);
// Redraw the remembered metatile viewport only when the shared dirty flag is set.
void map_flush_dirty();
// Validate slot and payload length, write payload bytes, then write the header
// and checksum. Return one after completing the writes; interruption can leave
// a partially replaced slot, so this is not transactional save storage.
u8 save_write(u8 slot, const void* data, u16 len);
// Require matching magic/version, exact requested length and a valid checksum
// before copying data into the destination. Validation failure leaves the
// destination unchanged. An invalid slot returns before touching RAM control;
// validation failures after enabling RAM disable it before returning zero.
u8 save_read(u8 slot, void* data, u16 len);
// Validate a slot's header, payload bounds and checksum without copying the
// payload out. A nonzero result means the stored record passes these checks.
u8 save_exists(u8 slot);
// Initialize the expected RAM access state and leave cartridge RAM disabled.
// Existing slot contents are preserved.
void save_init();
// Alias the checked save_read operation.
u8 save_load(u8 slot, void* data, u16 len);
// Alias save_exists, including its header and checksum validation.
u8 save_check(u8 slot);
// Zero the complete selected slot, including header and payload; invalid slot
// IDs are ignored. Cartridge RAM is disabled when clearing finishes.
void save_clear(u8 slot);
// Build movement costs, then convert reachable cells to one and unreachable
// 0xFF cells to zero. The caller provides one output byte per current-map cell
// and a valid unit origin; path-search workspace limits also apply.
u8 unit_move_range(unit_t* u, u8* out_mask);
// Mark cells whose Manhattan distance falls within the inclusive attack range.
// This geometric mask does not test obstacles, line of sight or other units.
u8 unit_attack_range(unit_t* u, u8* out_mask);
// A unit can act only while HP is nonzero and its acted flag is clear.
// The unit pointer must refer to a valid record.
u8 unit_can_act(const unit_t* u);
// Find a shortest cardinal path with BFS and write direction codes 0=up,
// 1=right, 2=down, 3=left. Return its byte length, or zero for failure or an
// already-reached goal. The caller supplies max_len output bytes and respects
// the shared workspace/distance limits; excessive paths are not truncated.
u8 path_find_bfs(u8 sx, u8 sy, u8 gx, u8 gy, u8* out_path, u8 max_len);
// Flood cardinal neighbors at unit cost, stopping expansion at the movement
// limit and rejecting blocked destinations. Initialize output to 0xFF only
// after validating the map and origin; invalid input leaves output unchanged.
void range_fill_move(u8 sx, u8 sy, u8 move, u8* out_costmap);
// Read a bit from the flag array. Callers must keep id below 2048.
u8 flag_get(u16 id);
// Set a flag bit without disturbing adjacent flags; id must be below 2048.
void flag_set(u16 id);
// Clear a flag bit; id must be below 2048.
void flag_clear(u16 id);
// Read a quest-state byte; quest_id must be below 64.
u8 quest_state(u8 quest_id);
// Replace a quest-state byte without validating its game-specific meaning.
// The caller must keep quest_id below 64.
void quest_set_state(u8 quest_id, u8 state);
// Expand count/value byte pairs until a zero count terminates the stream.
// The caller must provide a valid terminator and sufficient destination storage;
// there are no source or destination bounds parameters. Return the 16-bit byte count.
u16 rle_decode(void* dst, const void* src);
// Decode the same count/value format using bank-qualified reads for every source
// byte. Keep the encoded stream within the supplied bank/address mapping and
// provide enough destination space; a zero count ends the stream.
u16 rle_decode_far(void* dst, u8 bank, const void* src);
// Return the active map width in its own cell units, or zero when no map is loaded.
u8 map_current_width();
// Return the active map height in its own cell units, or zero when no map is loaded.
u8 map_current_height();kitaqgb/lib/rpg.h
scene.h — シーンの登録、変更と更新
#pragma once
typedef void (*SceneFunc)();
// Callbacks may be null. Keep the table and non-null targets readable for its lifetime.
// These plain function pointers do not carry a ROM bank number.
typedef __packed struct {
SceneFunc enter;
SceneFunc update;
SceneFunc draw;
SceneFunc exit;
} SceneDef;
// Attach the caller-owned scene table and clear current-scene state without invoking callbacks.
void scene_init(const SceneDef* scenes, u8 count);
// Replace the table by resetting scene state; this does not call the old scene's exit handler.
void scene_set_table(const SceneDef* scenes, u8 count);
// Ignore invalid IDs; otherwise exit the current scene and enter the requested one.
// Selecting the current ID still performs exit/enter. Callbacks run synchronously.
void scene_change(u8 scene_id);
// Clear the change flag before calling the active scene's update handler, so a
// transition made during that handler is visible afterward.
void scene_update();
// Call the active scene's draw handler if one exists; no current scene is a no-op.
void scene_draw();
// Return the stored scene ID; zero is also returned before the first transition.
u8 scene_get_current();
// Read the transition flag, which is cleared at the start of an active scene update.
u8 scene_was_changed();kitaqgb/lib/scene.h
scroll.h — 背景・ウィンドウ・分割スクロール
#pragma once
// Flags select operations in a raster split; raw window X includes the hardware +7 offset.
#define KQ_SCROLL_SPLIT_USE_BG 0x01
#define KQ_SCROLL_SPLIT_USE_WIN 0x02
#define KQ_SCROLL_SPLIT_WIN_SHOW 0x04
#define KQ_SCROLL_SPLIT_WIN_HIDE 0x08
#define KQ_SCROLL_SPLIT_BG_COLOR0 0x10
void __scroll_bg_set(u8 scx, u8 scy);
void __scroll_bg_x_set(u8 scx);
void __scroll_bg_y_set(u8 scy);
u8 __scroll_bg_x_get();
u8 __scroll_bg_y_get();
void __scroll_win_set(u8 wx, u8 wy);
void __scroll_win_x_set(u8 wx);
void __scroll_win_y_set(u8 wy);
u8 __scroll_win_x_get();
u8 __scroll_win_y_get();
void __scroll_bg_set_buffered(u8 scx, u8 scy);
void __scroll_bg_x_set_buffered(u8 scx);
void __scroll_bg_y_set_buffered(u8 scy);
void __scroll_win_set_buffered(u8 wx, u8 wy);
void __scroll_win_x_set_buffered(u8 wx);
void __scroll_win_y_set_buffered(u8 wy);
void __scroll_flush();
void __scroll_split_reset();
void __scroll_split_push(u8 ly, u8 scx, u8 scy);
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);
void __scroll_split_commit();
void __scroll_win_show();
void __scroll_win_hide();
void __scroll_bg_add(s8 dx, s8 dy);
void __scroll_win_add(s8 dx, s8 dy);
// Reset background scroll to zero and place the window at screen origin
// using hardware WX=7 and WY=0.
void Scroll_Init();
// Write both hardware background-scroll bytes immediately.
void Scroll_SetBg(u8 scx, u8 scy);
// Write horizontal background scroll while leaving vertical scroll unchanged.
void Scroll_SetBgX(u8 scx);
// Write vertical background scroll while leaving horizontal scroll unchanged.
void Scroll_SetBgY(u8 scy);
// Read the tracked current background X; this is not a direct SCX register read.
u8 Scroll_GetBgX();
// Read the tracked current background Y; pending and raster-only values are excluded.
u8 Scroll_GetBgY();
// Write raw hardware WX/WY values; callers add seven to a screen-space window X.
void Scroll_SetWindow(u8 wx, u8 wy);
// Write raw hardware WX without converting a screen-space X coordinate.
void Scroll_SetWindowX(u8 wx);
// Write the window Y coordinate through the intrinsic.
void Scroll_SetWindowY(u8 wy);
// Read tracked current WX, including its seven-pixel offset; not a live hardware read.
u8 Scroll_GetWindowX();
// Read tracked current WY; pending and raster-only values are excluded.
u8 Scroll_GetWindowY();
// Stage background-scroll values in the intrinsic buffer for a later flush.
void Scroll_SetBgBuffered(u8 scx, u8 scy);
// Stage raw window-coordinate values for a later flush.
void Scroll_SetWindowBuffered(u8 wx, u8 wy);
// Apply the buffered scroll/window state through the compiler runtime.
void Scroll_Flush();
// Apply signed deltas to the byte-sized background-scroll coordinates.
void Scroll_BgAdd(s8 dx, s8 dy);
// Apply signed deltas to the byte-sized hardware window coordinates.
void Scroll_WindowAdd(s8 dx, s8 dy);
// Request window visibility through the compiler intrinsic.
void Scroll_WindowShow();
// Request that the window be hidden through the compiler intrinsic.
void Scroll_WindowHide();
// Clear the pending split table before building a new raster configuration.
void Scroll_SplitReset();
// Append a background-scroll split directly. Use Raster_Push when caller-side
// scanline ordering and capacity validation are desired.
void Scroll_SplitPush(u8 ly, u8 scx, u8 scy);
// Append an extended split with raw background/window fields and operation flags.
void Scroll_SplitPushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);
// Reuse the two background-scroll fields to carry the low/high RGB15 bytes
// for a split whose operation flag selects background color zero.
void Scroll_SplitPushBgColor0(u8 ly, u16 rgb15);
// Publish the pending split configuration through the compiler runtime.
void Scroll_SplitCommit();kitaqgb/lib/scroll.h
slg.h — 盤面、手の一覧とundo
#pragma once
// A row-major byte board. Storage is borrowed; cell values and coordinates have game-defined meaning.
typedef __packed struct {
u8 width;
u8 height;
u8* cells;
} SLGBoard;
typedef __packed struct {
u8 x;
u8 y;
u8 value;
} SLGMove;
typedef __packed struct {
SLGMove* items;
u8 count;
u8 capacity;
} SLGMoveList;
// Undo records are retained cell values only; popping one does not apply it to a board.
typedef __packed struct {
SLGMove* items;
u8 count;
u8 capacity;
} SLGUndoStack;
// Attach width * height bytes of caller-owned cell storage without clearing it.
void slg_board_init(SLGBoard* board, u8 width, u8 height, u8* cells);
// Fill every cell with value; a null board is ignored. A valid board must own
// enough accessible cell storage for its declared dimensions.
void slg_board_clear(SLGBoard* board, u8 value);
// Return zero for a null board or out-of-range coordinate; otherwise read the
// cell. Zero therefore cannot distinguish an invalid lookup from an empty cell.
u8 slg_board_get(const SLGBoard* board, u8 x, u8 y);
// Write a cell only when the board and coordinate are valid.
void slg_board_set(SLGBoard* board, u8 x, u8 y, u8 value);
// Attach caller-owned move storage and reset the list length.
void slg_move_list_init(SLGMoveList* list, SLGMove* items, u8 capacity);
// Forget the moves without erasing their storage; a null list is ignored.
void slg_move_list_clear(SLGMoveList* list);
// Append coordinates and a value to the bounded move list. Return zero on a
// null/full list without replacing any existing move.
u8 slg_move_list_push(SLGMoveList* list, u8 x, u8 y, u8 value);
// Attach caller-owned undo records and initialize an empty stack.
void slg_undo_init(SLGUndoStack* stack, SLGMove* items, u8 capacity);
// Record a cell's previous value for later undo. Return zero if the stack is
// null or full; this function does not change the board itself.
u8 slg_undo_push(SLGUndoStack* stack, u8 x, u8 y, u8 old_value);
// Remove the newest undo record and optionally copy it out. A null output
// still discards the record; a null or empty stack returns zero.
u8 slg_undo_pop(SLGUndoStack* stack, SLGMove* out_move);kitaqgb/lib/slg.h
sprite.h — OBJの確保、配置、メタスプライト、アニメーション
#pragma once
// Keep the default 40 for the standard DMA path. Hardware always transfers 160 bytes,
// even if this array is shortened; a custom layout must provide valid bytes for that full transfer.
#ifndef SPRITE_MAX
#define SPRITE_MAX 40
#endif
#define SPRITE_FLAG_PAL1 ((u8)0x10)
#define SPRITE_FLAG_XFLIP ((u8)0x20)
#define SPRITE_FLAG_YFLIP ((u8)0x40)
#define SPRITE_FLAG_PRIORITY ((u8)0x80)
// Packed hardware order is Y, X, tile, flags; screen coordinates need the GB OAM offsets.
typedef __packed struct {
u8 y;
u8 x;
u8 tile;
u8 flags;
} SpriteOamEntry;
extern __wram __aligned(256) SpriteOamEntry kq_sprite_oam[SPRITE_MAX];
extern u8 kq_sprite_active[SPRITE_MAX];
extern u8 kq_sprite_used;
extern u8 kq_sprite_height;
// Signed pixel displacements from a common origin; final byte coordinates wrap rather than clip.
typedef __packed struct {
s8 dx;
s8 dy;
u8 tile;
u8 flags;
} MetaSpritePart;
// Initialize frame/ticks within their configured ranges. This animation selects consecutive tile IDs;
// it does not change sprite size or automatically step by two tiles in 8x16 mode.
typedef __packed struct {
u8 first_tile;
u8 frame_count;
u8 frame;
u8 ticks;
u8 ticks_per_frame;
} SpriteAnim;
void __wait_vblank();
void __oam_dma(u16 src_ptr);
// Clear allocation and shadow OAM state and reset the DMA-stub installation flag.
// The default height is eight pixels; the hardware display mode is configured elsewhere.
void sprite_init();
// Claim the first free slot, hide it, and return its ID; return 0xFF when full.
// Previously stored tile/flag bytes are retained until the caller replaces them.
u8 sprite_alloc();
// Release an allocated valid slot and hide it. Repeated frees do not decrement the count.
void sprite_free(u8 id);
// Convert screen coordinates to OAM coordinates by adding X=8 and Y=16.
// The byte additions wrap; this updates shadow storage even for an unallocated slot.
void sprite_set_pos(u8 id, u8 x, u8 y);
// Set a valid shadow slot's tile byte; allocation and hardware transfer are separate.
void sprite_set_tile(u8 id, u8 tile);
// Replace a valid shadow slot's complete hardware attribute byte.
void sprite_set_flags(u8 id, u8 flags);
// Move a valid shadow slot off screen without freeing it.
void sprite_hide(u8 id);
// Install the HRAM stub lazily and execute OAM DMA with interrupts disabled.
// This routine does not wait for VBlank and always enables interrupts afterward;
// it does not restore the caller's previous interrupt-enable state.
void sprite_flush_oam_now();
// Wait for VBlank, then transfer shadow OAM using the immediate DMA routine.
void sprite_flush_oam();
// Return the number of active slots, including slots activated by metasprite_draw.
// Hidden allocated slots remain active until sprite_free releases them.
u8 sprite_count_used();
// Report whether the software overlap estimate exceeds ten sprites on a scanline.
u8 sprite_warn_scanline_overflow();
// Estimate the largest overlap of active, non-hidden shadow sprites across
// visible scanlines using the configured sprite height. This is a software
// overlap count, not a rendered-pixel or hardware-priority simulation.
u8 sprite_max_scanline_count();
// Place consecutive parts, counting each newly activated slot once. Existing
// active slots are overwritten without increasing the allocation count. Return
// the number written; an invalid first slot or zero count writes nothing.
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count);
// Advance animation ticks/frame with wrapping frame selection and return the
// resulting tile. Zero ticks-per-frame freezes animation; null state returns zero.
u8 anim_update(SpriteAnim* anim);kitaqgb/lib/sprite.h
sprite_order.h — 優先度と交替によるスプライト選択
#pragma once
// Copyright (c) 2026 DAISUKE OBA. MIT License.
// Hardware-compatible layout without requiring the sprite allocator or its
// global storage. Cast a sprite.h shadow buffer to this type when combining
// the libraries; both layouts contain exactly Y, X, tile and flags bytes.
typedef __packed struct {
u8 y;
u8 x;
u8 tile;
u8 flags;
} SpriteOrderOamEntry;
#define SPRITE_ORDER_LEVELS 4
#define SPRITE_ORDER_OAM_COUNT 40
// One submitted, visible sprite. Attributes retain their hardware meaning;
// priority 0..3 controls OAM selection order, not the BG-over-OBJ flag.
typedef __packed struct {
SpriteOrderOamEntry oam;
u8 priority;
} SpriteOrderItem;
// Caller-owned foreground state. Capacity is 1..255 items; count includes only
// accepted submissions. Rebuild the same logical submission list each frame so
// phase rotation distributes selection among equal-priority sprites.
typedef struct {
SpriteOrderItem* items;
u8 capacity;
u8 count;
u8 height;
u8 phase;
} SpriteOrder;
// Bind writable item storage and select the hardware sprite height (8 or 16).
// Return 1 on success. Invalid storage, zero capacity or another height returns
// 0 and leaves a non-null state disabled. A null state returns 0 without writes.
u8 sprite_order_init(SpriteOrder* order, SpriteOrderItem* items, u8 capacity, u8 height);
// Discard this frame's submissions while preserving storage, height and phase.
// A null state is ignored. Call once before submitting the next frame's sprites.
void sprite_order_begin(SpriteOrder* order);
// Submit one sprite at signed, top-left SCREEN pixel coordinates. Preserve the
// tile and attribute bytes; use priority 0 (first) through 3 (last). Fully
// off-screen sprites, invalid priorities, disabled state and full storage return
// 0 without consuming a slot. Partly visible sprites are accepted. Return 1 on
// acceptance. Hardware 8x16 mode still ignores the tile index's low bit.
u8 sprite_order_push(SpriteOrder* order, s16 x, s16 y, u8 tile, u8 flags, u8 priority);
// Write a complete 160-byte shadow OAM: ascending priority, then cyclic order
// within each priority. Clamp limit to 40, return the number emitted and zero
// all unused entries (including Y) so they cannot consume scanline slots.
// Advance phase once for a nonempty valid queue, even when limit is zero.
// Empty queues reset phase to zero. Null/disabled/corrupt state or null output
// returns 0 without modifying output or phase.
// Output must provide 40 entries and must not overlap item/state storage.
// This only writes RAM. Build in foreground, then transfer during VBlank.
// DMG overlap priority also depends on X: this cannot override that hardware
// rule. OAM order chooses the first ten sprites per scanline on both DMG/CGB.
// It neither updates sprite_alloc's bookkeeping nor performs OAM DMA.
u8 sprite_order_build(SpriteOrder* order, SpriteOrderOamEntry* output, u8 limit);kitaqgb/lib/sprite_order.h
system.h — 起動、フレーム数、待機と割り込み
#pragma once
// This callback runs in the caller of system_wait_vblank, not as an installed ISR.
// Its plain pointer has no ROM bank component; keep the target callable in that mapping.
typedef void (*SystemCallback)();
extern u16 kq_system_frame;
void __wait_vblank();
// Reset the software frame counter and remove any registered callback.
void system_init();
// Wait for VBlank, count this wrapper call, then invoke the callback synchronously.
// The counter wraps at 16 bits and is not an independent hardware frame clock.
void system_wait_vblank();
// Replace the callback used by system_wait_vblank; null disables the callback.
void system_set_vblank_callback(SystemCallback callback);
// Read the software count of completed system_wait_vblank calls.
u16 system_get_frame();
// Return the low byte of the software frame counter for wrapping 8-bit timing.
u8 system_get_frame8();
// Enable CPU interrupts and return using the inline-assembly function body.
void system_enable_interrupts();
// Disable CPU interrupts and return; callers must explicitly re-enable them later.
void system_disable_interrupts();
#define gb_init system_init
#define gb_wait_vblank system_wait_vblank
#define gb_set_vblank_callback system_set_vblank_callback
#define gb_frame_count system_get_frame
#define gb_enable_interrupts system_enable_interrupts
#define gb_disable_interrupts system_disable_interruptskitaqgb/lib/system.h
vram.h — VRAM更新の予約と転送
#pragma once
// Keep this capacity in 1..255 in all units; counts and capacity/free return values are bytes.
#ifndef VRAM_QUEUE_MAX
#define VRAM_QUEUE_MAX 32
#endif
#define VRAM_CMD_BG_TILE ((u8)1)
#define VRAM_CMD_BG_RECT ((u8)2)
#define VRAM_CMD_BG_BLOCK ((u8)3)
#define VRAM_CMD_MEMCPY ((u8)4)
#define VRAM_CMD_MEMSET ((u8)5)
// A command owns metadata only. src is a retained near pointer with no bank number or copied payload.
typedef __packed struct {
u8 kind;
u8 x;
u8 y;
u8 w;
u8 h;
u8 value;
u16 dst;
const u8* src;
u16 len;
} VramCommand;
extern VramCommand kq_vram_queue[VRAM_QUEUE_MAX];
extern u8 kq_vram_queue_used;
extern u8 kq_vram_queue_overflowed;
void __wait_vblank();
void __settile_xy(u8 x, u8 y, u8 tile);
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);
void __vram_copy(u16 dst, const void* src, u16 len);
void __vram_fill(u16 dst, u8 value, u16 len);
// Start with an empty command queue and a clear overflow flag.
void vram_init();
// Discard pending commands and clear the latched overflow flag without writing VRAM.
void vram_clear_queue();
// Queue a single background tile write; return zero if no command slot is available.
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);
// Provide the short alias for a queued background tile write.
u8 vram_queue_tile(u8 x, u8 y, u8 tile);
// Queue a rectangle filled with one tile value; coordinates and dimensions are in tiles.
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
// Queue a tile-map rectangle while retaining src rather than copying its bytes.
// Keep the source data and its ROM bank accessible until the queue is flushed.
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);
// Queue a byte transfer and retain the source pointer. The source must remain
// valid and visible until flush; len is measured in bytes, not tiles.
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);
// Alias the queued byte-copy operation; this does not convert a tile count to bytes.
u8 vram_queue_tiles(u16 dst, const void* src, u16 len);
// Queue a byte fill and return zero if the command queue is full.
u8 vram_queue_memset(u16 dst, u8 value, u16 len);
// Execute pending commands in insertion order without waiting for VBlank here.
// The caller must arrange suitable VRAM access timing and a sufficient transfer budget.
// Only the used count is cleared; overflow stays latched until an explicit reset.
void vram_flush_now();
// Wait for VBlank before executing the queue. Waiting once does not itself limit
// the queued workload to the available VBlank duration.
void vram_flush();
// Return the number of queued commands, not the number of transfer bytes.
u8 vram_get_queue_used();
// Return the total number of command slots, independent of queue usage.
// This is command-buffer capacity, not free space in hardware VRAM.
u8 vram_get_queue_capacity();
// Return the remaining command-slot capacity.
u8 vram_get_queue_free();
// Read the overflow latch without clearing it.
u8 vram_get_overflowed();kitaqgb/lib/vram.h
wire3d.h — DMG向けワイヤーフレーム
// Compatibility names for the shared wire3d_dmg renderer.
// Select the same profile in every translation unit; compile one renderer entry.
#pragma once
#ifndef WIRE3D_DMG_HEIGHT
#define WIRE3D_DMG_HEIGHT 96
#endif
#if WIRE3D_DMG_HEIGHT != 96
#error Incompatible wire3d_dmg profile
#endif
#include "wire3d_dmg.h"
#define W3D_BG_QUEUE_LIMIT WIRE3D_DMG_BG_QUEUE_LIMIT
#define W3D_CENTER_X WIRE3D_DMG_CENTER_X
#define W3D_CENTER_Y WIRE3D_DMG_CENTER_Y
#define W3D_FAR_Z WIRE3D_DMG_FAR_Z
#define W3D_HUD_TILE_BASE WIRE3D_DMG_HUD_TILE_BASE
#define W3D_HUD_TILE_BLANK WIRE3D_DMG_HUD_TILE_BLANK
#define W3D_HUD_TILE_COUNT WIRE3D_DMG_HUD_TILE_COUNT
#define W3D_NEAR_Z WIRE3D_DMG_NEAR_Z
#define W3D_PROJECT_LIMIT WIRE3D_DMG_PROJECT_LIMIT
#define W3D_ROW_BYTES WIRE3D_DMG_ROW_BYTES
#define W3D_TILE_H WIRE3D_DMG_TILE_H
#define W3D_TILE_W WIRE3D_DMG_TILE_W
#define W3D_TRANSFORM_LIMIT WIRE3D_DMG_TRANSFORM_LIMIT
#define WIRE3D_EDGE_MASK_BINS_16 WIRE3D_DMG_EDGE_MASK_BINS_16
#define WIRE3D_FACE_NONE WIRE3D_DMG_FACE_NONE
#define WIRE3D_MODEL_EDGE_LIMIT WIRE3D_DMG_MODEL_EDGE_LIMIT
#define WIRE3D_MODEL_FACE_LIMIT WIRE3D_DMG_MODEL_FACE_LIMIT
#define WIRE3D_MODEL_HIDDEN_LINES WIRE3D_DMG_MODEL_HIDDEN_LINES
#define WIRE3D_MODEL_VERTEX_LIMIT WIRE3D_DMG_MODEL_VERTEX_LIMIT
#define WIRE3D_SCENE_OBJECT_LIMIT WIRE3D_DMG_SCENE_OBJECT_LIMIT
#define WIRE3D_SCREEN_H WIRE3D_DMG_SCREEN_H
#define WIRE3D_SCREEN_W WIRE3D_DMG_SCREEN_W
#define Wire3D_BeginFrame Wire3DDMG_BeginFrame
#define Wire3D_DrawLine2D Wire3DDMG_DrawLine2D
#define Wire3D_DrawLine3D Wire3DDMG_DrawLine3D
#define Wire3D_DrawModel Wire3DDMG_DrawModel
#define Wire3D_DrawModelScaled Wire3DDMG_DrawModelScaled
#define Wire3D_DrawScene Wire3DDMG_DrawScene
#define Wire3D_Edge Wire3DDMG_Edge
#define Wire3D_EdgeFaces Wire3DDMG_EdgeFaces
#define Wire3D_EndFrame Wire3DDMG_EndFrame
#define Wire3D_EraseModelFaces Wire3DDMG_EraseModelFaces
#define Wire3D_EraseSpan2D Wire3DDMG_EraseSpan2D
#define Wire3D_EraseTriangle2D Wire3DDMG_EraseTriangle2D
#define Wire3D_Face Wire3DDMG_Face
#define Wire3D_Init Wire3DDMG_Init
#define Wire3D_Model Wire3DDMG_Model
#define Wire3D_Object Wire3DDMG_Object
#define Wire3D_ProjectPoint Wire3DDMG_ProjectPoint
#define Wire3D_PutBgTile Wire3DDMG_PutBgTile
#define Wire3D_SelectEdgeMask Wire3DDMG_SelectEdgeMask
#define Wire3D_SetCamera Wire3DDMG_SetCamera
#define Wire3D_SetPalette Wire3DDMG_SetPalette
#define Wire3D_Vec3 Wire3DDMG_Vec3
#define w3d_abs_i16 w3ddmg_abs_i16
#define w3d_bg_map_9800 w3ddmg_bg_map_9800
#define w3d_bgq_addr_hi w3ddmg_bgq_addr_hi
#define w3d_bgq_addr_lo w3ddmg_bgq_addr_lo
#define w3d_bgq_count w3ddmg_bgq_count
#define w3d_bgq_tile w3ddmg_bgq_tile
#define w3d_bgq_value w3ddmg_bgq_value
#define w3d_bgq_x w3ddmg_bgq_x
#define w3d_bgq_y w3ddmg_bgq_y
#define w3d_bit_mask w3ddmg_bit_mask
#define w3d_build_edge_flags w3ddmg_build_edge_flags
#define w3d_build_face_visibility w3ddmg_build_face_visibility
#define w3d_cam_pitch w3ddmg_cam_pitch
#define w3d_cam_roll w3ddmg_cam_roll
#define w3d_cam_x w3ddmg_cam_x
#define w3d_cam_y w3ddmg_cam_y
#define w3d_cam_yaw w3ddmg_cam_yaw
#define w3d_cam_z w3ddmg_cam_z
#define w3d_clamp_i16 w3ddmg_clamp_i16
#define w3d_clamp_screen w3ddmg_clamp_screen
#define w3d_clear_occlusion_mask w3ddmg_clear_occlusion_mask
#define w3d_clear_occlusion_mask_asm w3ddmg_clear_occlusion_mask_asm
#define w3d_clear_stage_asm w3ddmg_clear_stage_asm
#define w3d_clear_triangle w3ddmg_clear_triangle
#define w3d_clear_vram_asm w3ddmg_clear_vram_asm
#define w3d_cos_q6 w3ddmg_cos_q6
#define w3d_edge_area w3ddmg_edge_area
#define w3d_edge_flags w3ddmg_edge_flags
#define w3d_face_visible w3ddmg_face_visible
#define w3d_fill_bg_map_asm w3ddmg_fill_bg_map_asm
#define w3d_flush_bg_queue w3ddmg_flush_bg_queue
#define w3d_hud_tiles w3ddmg_hud_tiles
#define w3d_i16 w3ddmg_i16
#define w3d_i8 w3ddmg_i8
#define w3d_inv_depth w3ddmg_inv_depth
#define w3d_is_edge_visible w3ddmg_is_edge_visible
#define w3d_line_dx w3ddmg_line_dx
#define w3d_line_dy w3ddmg_line_dy
#define w3d_line_err w3ddmg_line_err
#define w3d_line_stage_asm w3ddmg_line_stage_asm
#define w3d_line_stage_masked_c w3ddmg_line_stage_masked_c
#define w3d_line_sx w3ddmg_line_sx
#define w3d_line_sy w3ddmg_line_sy
#define w3d_line_x w3ddmg_line_x
#define w3d_line_x0 w3ddmg_line_x0
#define w3d_line_x1 w3ddmg_line_x1
#define w3d_line_y w3ddmg_line_y
#define w3d_line_y0 w3ddmg_line_y0
#define w3d_line_y1 w3ddmg_line_y1
#define w3d_load_hud_tiles w3ddmg_load_hud_tiles
#define w3d_mark_model_occluder w3ddmg_mark_model_occluder
#define w3d_mark_triangle w3ddmg_mark_triangle
#define w3d_mask_get w3ddmg_mask_get
#define w3d_mask_offset w3ddmg_mask_offset
#define w3d_mask_set w3ddmg_mask_set
#define w3d_mask_set_span w3ddmg_mask_set_span
#define w3d_neg_angle w3ddmg_neg_angle
#define w3d_occlusion_active w3ddmg_occlusion_active
#define w3d_occlusion_mask w3ddmg_occlusion_mask
#define w3d_pair_block w3ddmg_pair_block
#define w3d_pair_wait_0 w3ddmg_pair_wait_0
#define w3d_pair_wait_1 w3ddmg_pair_wait_1
#define w3d_pair_wait_10 w3ddmg_pair_wait_10
#define w3d_pair_wait_11 w3ddmg_pair_wait_11
#define w3d_pair_wait_12 w3ddmg_pair_wait_12
#define w3d_pair_wait_13 w3ddmg_pair_wait_13
#define w3d_pair_wait_14 w3ddmg_pair_wait_14
#define w3d_pair_wait_15 w3ddmg_pair_wait_15
#define w3d_pair_wait_2 w3ddmg_pair_wait_2
#define w3d_pair_wait_3 w3ddmg_pair_wait_3
#define w3d_pair_wait_4 w3ddmg_pair_wait_4
#define w3d_pair_wait_5 w3ddmg_pair_wait_5
#define w3d_pair_wait_6 w3ddmg_pair_wait_6
#define w3d_pair_wait_7 w3ddmg_pair_wait_7
#define w3d_pair_wait_8 w3ddmg_pair_wait_8
#define w3d_pair_wait_9 w3ddmg_pair_wait_9
#define w3d_plot_stage_asm w3ddmg_plot_stage_asm
#define w3d_plot_tx w3ddmg_plot_tx
#define w3d_plot_x w3ddmg_plot_x
#define w3d_plot_y w3ddmg_plot_y
#define w3d_project_camera_space w3ddmg_project_camera_space
#define w3d_project_world w3ddmg_project_world
#define w3d_put_bg_tile_safe_asm w3ddmg_put_bg_tile_safe_asm
#define w3d_reg_bgp w3ddmg_reg_bgp
#define w3d_reg_lcdc w3ddmg_reg_lcdc
#define w3d_reg_ly w3ddmg_reg_ly
#define w3d_reg_scx w3ddmg_reg_scx
#define w3d_reg_scy w3ddmg_reg_scy
#define w3d_rotate_x w3ddmg_rotate_x
#define w3d_rotate_y w3ddmg_rotate_y
#define w3d_rotate_z w3ddmg_rotate_z
#define w3d_scene_depth w3ddmg_scene_depth
#define w3d_scene_object_depth w3ddmg_scene_object_depth
#define w3d_scene_order w3ddmg_scene_order
#define w3d_screen_delta w3ddmg_screen_delta
#define w3d_screen_visible w3ddmg_screen_visible
#define w3d_screen_x w3ddmg_screen_x
#define w3d_screen_y w3ddmg_screen_y
#define w3d_sin_q6 w3ddmg_sin_q6
#define w3d_stage w3ddmg_stage
#define w3d_stage_clear_pixel w3ddmg_stage_clear_pixel
#define w3d_stage_clear_span w3ddmg_stage_clear_span
#define w3d_transfer_stage_asm w3ddmg_transfer_stage_asm
#define w3d_u16 w3ddmg_u16
#define w3d_u8 w3ddmg_u8
#define w3d_vram_tiles w3ddmg_vram_tiles
#define w3d_wait_vblank_start w3ddmg_wait_vblank_start
#define w3dbg_enter w3ddmgbg_enter
#define w3dco_enter w3ddmgco_enter
#define w3dco_loop w3ddmgco_loop
#define w3dco_page w3ddmgco_page
#define w3dcs_enter w3ddmgcs_enter
#define w3dcs_inner w3ddmgcs_inner
#define w3dcs_outer w3ddmgcs_outer
#define w3dcv_enter w3ddmgcv_enter
#define w3dcv_loop w3ddmgcv_loop
#define w3dcv_page w3ddmgcv_page
#define w3dfb_enter w3ddmgfb_enter
#define w3dfb_loop w3ddmgfb_loop
#define w3dfb_page w3ddmgfb_page
#define w3dls_branch w3ddmgls_branch
#define w3dls_done w3ddmgls_done
#define w3dls_enter w3ddmgls_enter
#define w3dls_shallow w3ddmgls_shallow
#define w3dls_shallow_loop w3ddmgls_shallow_loop
#define w3dls_shallow_skip_bridge w3ddmgls_shallow_skip_bridge
#define w3dls_steep w3ddmgls_steep
#define w3dls_steep_loop w3ddmgls_steep_loop
#define w3dls_steep_skip_bridge w3ddmgls_steep_skip_bridge
#define w3dls_x_reverse w3ddmgls_x_reverse
#define w3dls_y_reverse w3ddmgls_y_reverse
#define w3dls_y_start w3ddmgls_y_start
#define w3dps_enter w3ddmgps_enter
#define w3dps_ret w3ddmgps_retkitaqgb/lib/wire3d.h
wire3d_cgb.h — CGB専用の色付きワイヤーフレーム
#pragma once
#ifndef KITAQGB_WIRE3D_CGB_H
#define KITAQGB_WIRE3D_CGB_H
#define WIRE3DCGB_SCREEN_W 128
#ifndef WIRE3DCGB_HEIGHT
#define WIRE3DCGB_HEIGHT 96
#endif
#if WIRE3DCGB_HEIGHT != 88 && WIRE3DCGB_HEIGHT != 96
#error WIRE3DCGB_HEIGHT must be 88 or 96
#endif
#define WIRE3DCGB_SCREEN_H WIRE3DCGB_HEIGHT
#if WIRE3DCGB_HEIGHT == 88
#define WIRE3DCGB_TILE_ROWS 11
#define WIRE3DCGB_TILE_COUNT 176
#define WIRE3DCGB_COLUMN_BYTES 176
#define WIRE3DCGB_NEG_COLUMN_BYTES 0xFF50
#define WIRE3DCGB_STAGE_RIGHT 0xDD50
#define WIRE3DCGB_STAGE_BOTTOM 0xD3AE
#define WIRE3DCGB_SECOND_DMA 0xAF
#else
#define WIRE3DCGB_TILE_ROWS 12
#define WIRE3DCGB_TILE_COUNT 192
#define WIRE3DCGB_COLUMN_BYTES 192
#define WIRE3DCGB_NEG_COLUMN_BYTES 0xFF40
#define WIRE3DCGB_STAGE_RIGHT 0xDE40
#define WIRE3DCGB_STAGE_BOTTOM 0xD3BE
#define WIRE3DCGB_SECOND_DMA 0xBF
#endif
// Reserve 3072 bytes in either profile for full-screen compatibility.
#define WIRE3DCGB_UPLOAD_BYTES (WIRE3DCGB_TILE_COUNT * 16)
#define WIRE3DCGB_FULL_SCREEN_W 160
#define WIRE3DCGB_FULL_SCREEN_H 144
#define WIRE3DCGB_FULL_TILE_LIMIT 127
#define WIRE3DCGB_STAGE_BASE 0xD300
#define WIRE3DCGB_STAGE_WRAM_BANK 2
#define WIRE3DCGB_STAGE_BYTES 3072
#define WIRE3DCGB_MODEL_VERTEX_LIMIT 24
#define WIRE3DCGB_MODEL_FACE_LIMIT 16
#define WIRE3DCGB_MODEL_EDGE_LIMIT 64
#define WIRE3DCGB_SCENE_OBJECT_LIMIT 8
#define WIRE3DCGB_FACE_NONE 255
#define WIRE3DCGB_MODEL_HIDDEN_LINES 1
#define WIRE3DCGB_COLOR_LOW 1
#define WIRE3DCGB_COLOR_MAIN 2
#define WIRE3DCGB_COLOR_HIGH 3
#define WIRE3DCGB_COLOR_DEFAULT WIRE3DCGB_COLOR_MAIN
#define WIRE3DCGB_RGB15(r5, g5, b5) ((w3dcgb_u16)((w3dcgb_u16)((r5) & 31) | ((w3dcgb_u16)((g5) & 31) << 5) | ((w3dcgb_u16)((b5) & 31) << 10)))
typedef s8 w3dcgb_i8;
typedef s16 w3dcgb_i16;
typedef u8 w3dcgb_u8;
typedef u16 w3dcgb_u16;
typedef struct {
w3dcgb_i16 x;
w3dcgb_i16 y;
w3dcgb_i16 z;
} Wire3DCGB_Vec3;
typedef struct {
w3dcgb_u8 a;
w3dcgb_u8 b;
} Wire3DCGB_Edge;
typedef struct {
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 c;
} Wire3DCGB_Face;
typedef struct {
w3dcgb_u8 f0;
w3dcgb_u8 f1;
} Wire3DCGB_EdgeFaces;
typedef struct {
const Wire3DCGB_Vec3* vertices;
const Wire3DCGB_Edge* edges;
const Wire3DCGB_Face* faces;
const Wire3DCGB_EdgeFaces* edge_faces;
w3dcgb_u8 vertex_count;
w3dcgb_u8 edge_count;
w3dcgb_u8 face_count;
w3dcgb_u8 flags;
} Wire3DCGB_Model;
typedef struct {
const Wire3DCGB_Model* model;
w3dcgb_i16 x;
w3dcgb_i16 y;
w3dcgb_i16 z;
w3dcgb_i8 rx;
w3dcgb_i8 ry;
w3dcgb_i8 rz;
w3dcgb_i16 scale_q8;
w3dcgb_u8 visible;
w3dcgb_u8 color;
} Wire3DCGB_Object;
typedef struct {
w3dcgb_i16 x;
w3dcgb_i16 y;
w3dcgb_i16 z;
w3dcgb_i8 rx;
w3dcgb_i8 ry;
w3dcgb_i8 rz;
w3dcgb_u8 color;
} Wire3DCGB_FastCube;
void Wire3DCGB_Init();
void Wire3DCGB_InitFullScreen();
void Wire3DCGB_InitFastMapLite();
void Wire3DCGB_EnableDoubleSpeed();
void Wire3DCGB_SetScreenOffset(w3dcgb_u8 scx, w3dcgb_u8 scy);
void Wire3DCGB_SetPaletteRGB15(w3dcgb_u16 color0, w3dcgb_u16 color1, w3dcgb_u16 color2, w3dcgb_u16 color3);
void Wire3DCGB_SetLineColor(w3dcgb_u8 color);
w3dcgb_u8 Wire3DCGB_GetLineColor();
void Wire3DCGB_ClearFrameTiles();
void Wire3DCGB_BeginFrame();
void Wire3DCGB_BeginFrameFast();
void Wire3DCGB_BeginFrameSparse();
void Wire3DCGB_ClearSparseStageFast();
void Wire3DCGB_MarkDirtyRect2D(w3dcgb_u8 min_x, w3dcgb_u8 min_y, w3dcgb_u8 max_x, w3dcgb_u8 max_y);
void Wire3DCGB_SetCamera(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 pitch, w3dcgb_i8 yaw, w3dcgb_i8 roll);
void Wire3DCGB_DrawModel(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz);
void Wire3DCGB_DrawModelScaled(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);
void Wire3DCGB_DrawModelColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_u8 color);
void Wire3DCGB_DrawModelScaledColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8, w3dcgb_u8 color);
void Wire3DCGB_EraseModelFaces(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);
void Wire3DCGB_EraseTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);
void Wire3DCGB_EraseSpan2D(w3dcgb_u8 y, w3dcgb_u8 x0, w3dcgb_u8 x1);
/* Signed endpoints, clipped before conversion to the rasterizer's u8 ABI. */
void Wire3DCGB_DrawLineClipped2D(w3dcgb_i16 x0, w3dcgb_i16 y0,
w3dcgb_i16 x1, w3dcgb_i16 y1, w3dcgb_u8 color);
void Wire3DCGB_DrawEdgeListClipped2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i16* vx, const w3dcgb_i16* vy, w3dcgb_u8 vertex_count,
w3dcgb_i16 cx, w3dcgb_i16 cy, w3dcgb_u8 color);
w3dcgb_i16 Wire3DCGB_ProjectAxis48(w3dcgb_i16 value, w3dcgb_i16 z);
void Wire3DCGB_InvalidateFrameHistory();
/* Startup-only: reserves both BG maps; queued HUD tiles are mirrored. */
void Wire3DCGB_EnableAtomicMaps();
void Wire3DCGB_EraseRect2D(w3dcgb_u8 x0, w3dcgb_u8 y0, w3dcgb_u8 x1, w3dcgb_u8 y1);
void Wire3DCGB_EraseLeftGuard24Fast();
void Wire3DCGB_DrawWhiteBorderFast();
void Wire3DCGB_ClearOcclusionMask();
void Wire3DCGB_SetOcclusionActive(w3dcgb_u8 active);
void Wire3DCGB_MarkTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);
void Wire3DCGB_MarkPackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);
void Wire3DCGB_ErasePackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);
void Wire3DCGB_DrawScene(Wire3DCGB_Object* objects, w3dcgb_u8 count);
void Wire3DCGB_DrawFastCubes(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);
void Wire3DCGB_DrawFastStatus(w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);
void Wire3DCGB_DrawFastCubeFrame(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);
void Wire3DCGB_DrawFastProjectile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_i16 z, w3dcgb_u8 phase, w3dcgb_u8 color);
void Wire3DCGB_FastMapBegin();
void Wire3DCGB_FastMapBeginTilesOnly();
void Wire3DCGB_FastMapCell(w3dcgb_u8 tx, w3dcgb_u8 ty, w3dcgb_u8 color, w3dcgb_u8 mask);
void Wire3DCGB_FastMapRect(w3dcgb_u8 tx0, w3dcgb_u8 ty0, w3dcgb_u8 tx1, w3dcgb_u8 ty1, w3dcgb_u8 color, w3dcgb_u8 sides);
void Wire3DCGB_FastMapFlush();
void Wire3DCGB_FastMapFlushTilesOnly();
// Project camera-space coordinates without subtracting/rotating the camera.
// Depth 8..255; outputs unchanged on rejection. Shared scratch: no ISR calls.
w3dcgb_u8 Wire3DCGB_ProjectCameraPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);
w3dcgb_u8 Wire3DCGB_ProjectPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);
/* Fast path for games whose camera pitch, yaw, and roll stay at zero. */
w3dcgb_u8 Wire3DCGB_ProjectPointNoRotation(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);
void Wire3DCGB_DrawLine3D(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz);
void Wire3DCGB_DrawLine3DColor(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz, w3dcgb_u8 color);
void Wire3DCGB_DrawPoint2D(w3dcgb_u8 x, w3dcgb_u8 y);
void Wire3DCGB_DrawTinyModel2D(w3dcgb_u8 x, w3dcgb_u8 y,
w3dcgb_u8 turn, w3dcgb_u8 color);
void Wire3DCGB_DrawLine2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by);
void Wire3DCGB_DrawLine2DColor(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 color);
void Wire3DCGB_DrawMaskedModel2D(const Wire3DCGB_Model* model,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
const w3dcgb_u8* edge_mask,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);
void Wire3DCGB_DrawEdgeList2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);
void Wire3DCGB_DrawLineList2DColor(const w3dcgb_u8* line_xy,
w3dcgb_u8 line_count, w3dcgb_u8 color);
void Wire3DCGB_PutBgTile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 tile);
void Wire3DCGB_EndFrame();
void Wire3DCGB_EndFrameFast();
void Wire3DCGB_EndFrameSparse();
void Wire3DCGB_EndFrameSparseNow();
w3dcgb_u8 Wire3DCGB_GetFullScreenTileCount();
w3dcgb_u8 Wire3DCGB_GetFullScreenOverflow();
#endifkitaqgb/lib/wire3d_cgb.h
wire3d_dmg.h — DMG向けワイヤーフレーム
#pragma once
// Select the viewport at build time before including this header or source.
// Default: 128x120 with consuming uploads and optional dirty/auxiliary transfer.
// Heights 88 and 96 retain the HUD and yaw-mask profile with BeginFrame clearing.
#ifndef WIRE3D_DMG_HEIGHT
#define WIRE3D_DMG_HEIGHT 120
#endif
#if WIRE3D_DMG_HEIGHT != 88 && WIRE3D_DMG_HEIGHT != 96 && WIRE3D_DMG_HEIGHT != 120
#error WIRE3D_DMG_HEIGHT must be 88, 96 or 120
#endif
#ifndef KITAQGB_WIRE3D_DMG_H
#define KITAQGB_WIRE3D_DMG_H
#if WIRE3D_DMG_HEIGHT != 120
// Monochrome 128x88/96 wireframe renderer with global, non-reentrant state.
// It takes over LCD tile data/map and reserves WRAM 0xD000..0xDFFF for staging.
// Angles wrap every 16 steps; scale 256 is unity. Scene occlusion is a coarse
// face-bounding-box/five-line-sample heuristic, not per-pixel depth testing.
#else
// Monochrome 128x120 staged renderer with global non-reentrant state.
// Reserve WRAM 0xD000..0xDFFF and the renderer's tile/map VRAM regions.
// BeginFrame does not clear pixels: uploads consume the stage. Auxiliary source
// strips alias the main stage, and dirty transfer relies on current/previous flags.
#endif
#define WIRE3D_DMG_SCREEN_W 128
#if WIRE3D_DMG_HEIGHT != 120
#define WIRE3D_DMG_SCREEN_H WIRE3D_DMG_HEIGHT
#else
#define WIRE3D_DMG_SCREEN_H 120
#endif
#define WIRE3D_DMG_MODEL_VERTEX_LIMIT 24
#if WIRE3D_DMG_HEIGHT != 120
#define WIRE3D_DMG_MODEL_EDGE_LIMIT 16
#endif
#define WIRE3D_DMG_MODEL_FACE_LIMIT 16
#define WIRE3D_DMG_SCENE_OBJECT_LIMIT 8
#define WIRE3D_DMG_FACE_NONE 255
#define WIRE3D_DMG_MODEL_HIDDEN_LINES 1
#if WIRE3D_DMG_HEIGHT != 120
#define WIRE3D_DMG_EDGE_MASK_BINS_16 16
#else
#define WIRE3D_DMG_ANGLE_STEPS 16
#endif
typedef s8 w3ddmg_i8;
typedef s16 w3ddmg_i16;
typedef u8 w3ddmg_u8;
typedef u16 w3ddmg_u16;
typedef struct {
w3ddmg_i16 x;
w3ddmg_i16 y;
w3ddmg_i16 z;
} Wire3DDMG_Vec3;
typedef struct {
w3ddmg_u8 a;
w3ddmg_u8 b;
} Wire3DDMG_Edge;
typedef struct {
w3ddmg_u8 a;
w3ddmg_u8 b;
w3ddmg_u8 c;
} Wire3DDMG_Face;
typedef struct {
w3ddmg_u8 f0;
w3ddmg_u8 f1;
} Wire3DDMG_EdgeFaces;
#if WIRE3D_DMG_HEIGHT != 120
// Model tables are borrowed for drawing, never copied or bank-switched.
// Counts are capped at 24 vertices, 16 edges and 16 faces; each supplied table
// must contain the referenced entries. Triangle winding with positive projected
// area is front-facing. FACE_NONE denotes a missing adjacent face.
#else
// Model tables are borrowed and must remain readable in the active bank.
// At most 24 vertices and 16 faces are processed; the model edge count is not
// limited to 16. Supply all declared edge and adjacency entries. Positive
// projected triangle area is front-facing; FACE_NONE means missing adjacency.
#endif
typedef struct {
const Wire3DDMG_Vec3* vertices;
const Wire3DDMG_Edge* edges;
const Wire3DDMG_Face* faces;
const Wire3DDMG_EdgeFaces* edge_faces;
#if WIRE3D_DMG_HEIGHT != 120
/* Keep edge mask tables in RAM for pointer-indirect access on current KITAQGB. */
const w3ddmg_u16* edge_masks;
#endif
w3ddmg_u8 vertex_count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
#if WIRE3D_DMG_HEIGHT != 120
w3ddmg_u8 edge_mask_count;
#endif
w3ddmg_u8 flags;
} Wire3DDMG_Model;
typedef struct {
const Wire3DDMG_Model* model;
w3ddmg_i16 x;
w3ddmg_i16 y;
w3ddmg_i16 z;
#if WIRE3D_DMG_HEIGHT != 120
w3ddmg_i8 rx;
w3ddmg_i8 ry;
w3ddmg_i8 rz;
#else
w3ddmg_u8 rx;
w3ddmg_u8 ry;
w3ddmg_u8 rz;
#endif
w3ddmg_i16 scale_q8;
w3ddmg_u8 visible;
} Wire3DDMG_Object;
#if WIRE3D_DMG_HEIGHT != 120
// Take over the LCD: wait for VBlank, switch it off, clear 6144 tile bytes and
// the BG map, install HUD tiles and the 128x96 signed-tile viewport, then enable
// BG display with scroll zero and BGP=0xB4. Reset camera, BG queue and stage.
// Reserve 0xD000..0xDFFF for staging and retain its WRAM bank mapping.
// Call BeginFrame before drawing to initialize the occlusion state as well.
#else
// Take over LCD state, clear tile VRAM and the BG map, and install a 16x15
// viewport beginning at map tile (3,1). Set scroll (8,0), BGP=0xB4 and BG display.
// Reset camera, queues, transfer modes, both dirty tables and stage. Reserve
// 0xD000..0xDFFF and retain its WRAM mapping; auxiliary storage aliases it.
// Call BeginFrame before drawing to establish occlusion state.
#endif
void Wire3DDMG_Init();
#if WIRE3D_DMG_HEIGHT != 120
// Clear staged pixels and occlusion mask and disable occlusion filtering.
// This leaves the displayed VRAM frame and pending BG tile queue unchanged.
#else
// Disable occlusion filtering only. Stage clearing normally happens during
// transfer, and Init supplies the first clean stage. This does not discard an
// unsubmitted frame, clear the mask, or reset pending BG writes/dirty flags.
#endif
void Wire3DDMG_BeginFrame();
/* Validate depth and project coordinates already in camera space. */
w3ddmg_u8 Wire3DDMG_ProjectCameraPoint(w3ddmg_i16 x,w3ddmg_i16 y,w3ddmg_i16 z,w3ddmg_u8* sx,w3ddmg_u8* sy);
// Store camera position and angles for later draws; a full turn is 16 angle
// steps. No existing drawing is reprojected and no input range validation occurs.
#if WIRE3D_DMG_HEIGHT != 120
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll);
#else
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll);
#endif
// Draw an unscaled model by delegating to DrawModelScaled with scale 256.
#if WIRE3D_DMG_HEIGHT != 120
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);
// Scale, rotate in Y/X/Z order, translate and project at most 24 vertices;
// then draw up to 16 edges filtered by yaw masks and optional front-facing faces.
// Nonpositive scale becomes unity (256). Null model/vertex/edge pointers are
// ignored; referenced tables must remain readable. Keep scale products and
// translations in signed 16-bit range. Depth-crossing edges are dropped, not clipped.
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);
#else
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);
// Scale up to 24 vertices, rotate Y/X/Z, translate and project, then traverse
// all edge_count entries (a byte count) with optional adjacent-face filtering.
// Face scratch is capped at 16; edge tables must contain all declared entries.
// Nonpositive scale means 256. Null model/vertex/edge pointers are ignored.
// Keep products/translations in signed 16-bit range; depth-crossing edges are dropped.
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);
#endif
// Project up to 24 vertices and erase front-facing triangle interiors from
// the stage for a model with hidden-line flags and face data. Nonpositive scale
// means 256 (unity). Up to 16 faces are considered; depth-crossing faces are
// skipped. This also replaces shared projection caches, but does not update VRAM.
#if WIRE3D_DMG_HEIGHT != 120
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);
#else
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);
#endif
// Erase a triangle from staged pixels using integer scanline intersections.
// Supply viewport coordinates. Degenerate triangles are ignored; the occlusion
// mask and VRAM are unchanged.
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);
// Order the X endpoints and erase the inclusive staged span. Both endpoints
// must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);
#if WIRE3D_DMG_HEIGHT != 120
// Process at most the first eight objects, sorted near to far by transformed
// origin depth. Draw visible valid models, then mark their front-face bounding
// rectangles to reject fully sampled later lines. This approximates inter-object
// occlusion, not a depth buffer. It clears its mask but retains existing staged
// pixels. Null arrays are ignored; objects and their data must remain readable.
#else
// Sort at most eight object origins by camera depth, near to far. Draw visible
// valid models and mark front-face bounding rectangles for later objects. The
// first sorted slot bypasses mask tests and the last needs no new occluder.
// This is coarse line rejection, not a depth buffer. Null/empty input is ignored;
// caller data and the current projection cache must remain valid during traversal.
#endif
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);
#if WIRE3D_DMG_HEIGHT != 120
// Select a precomputed mask from the low four bits of object yaw; pitch,
// roll and camera orientation are ignored. Use 16, 8, 4 or 2 bins; other positive
// counts select entry zero (counts >=16 use the first 16). Missing data returns
// 0xFFFF. Keep the mask table readable through the current data-pointer mapping.
w3ddmg_u16 Wire3DDMG_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);
// Project a world point with the current camera. Return zero for rejected
// depth, leaving outputs unchanged; success writes clamped coordinates for the selected height.
// Output pointers must be valid. Screen clamping is not geometric clipping.
#else
// Project a world point with the current camera. Return zero for invalid
// depth without changing outputs; success writes clamped 128x120 coordinates.
// Both output pointers must be valid. Coordinate differences must fit s16.
#endif
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);
#if WIRE3D_DMG_HEIGHT == 120
// Rotate in place in Y/X/Z order using 16 steps per full turn. A null
// component pointer makes the whole call a no-op; use distinct pointers.
// Each axis rotation clamps its inputs to +/-220.
void Wire3DDMG_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);
#endif
// Project both world endpoints and draw only when both pass the depth range.
// Do not intersect a crossing segment with near/far planes; projected endpoints
// are independently clamped to the screen edges.
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);
#if WIRE3D_DMG_HEIGHT != 120
// Draw a connected line into the 128x96 stage, including endpoints. During
// DrawScene, use the coarse five-sample occlusion rejection; otherwise draw directly.
// Supply viewport coordinates. VRAM is updated only by EndFrame.
#else
// Mark the bounding tile rectangle when dirty transfer is enabled, then draw
// a connected staged line. Scene drawing may reject it using five mask samples.
// Supply 128x120 viewport endpoints; low-level pixel bounds checks do not clip
// the segment geometrically.
#endif
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);
#if WIRE3D_DMG_HEIGHT == 120
// Draw a wireframe rectangle directly into the staged bitplane. Endpoints are
// inclusive screen pixels in 0..127 x 0..119; both horizontal and vertical
// edges are written with byte masks and only touched tiles are uploaded.
void Wire3DDMG_DrawRect2D(w3ddmg_u8 x0, w3ddmg_u8 y0, w3ddmg_u8 x1, w3ddmg_u8 y1);
#endif
// Draw the canonical 12 edges of an 8-vertex projected box in one call.
// Vertex order is near-face corners 0..3, then far-face corners 4..7.
void Wire3DDMG_DrawBoxEdges2D(const w3ddmg_u8* sx, const w3ddmg_u8* sy);
#if WIRE3D_DMG_HEIGHT == 120
// Draw byte-pair edge indices after integer scaling and translation; scale
// zero means one, and there is no object rotation or face filtering. Project at
// most 24 vertices. Supply at most 128 edge pairs because byte offsets wrap
// after 255. Invalid vertex IDs/depth-rejected endpoints are skipped; null arrays
// are ignored. Keep coordinate products/sums representable as s16.
void Wire3DDMG_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale);
#endif
// Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map
// coordinates and submissions beyond 48 pending writes are silently ignored.
// This queue is separate from the general vram library queue.
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);
// Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp);
#if WIRE3D_DMG_HEIGHT != 120
// Wait for the next VBlank, flush pending BG map writes, then copy the stage
// to VRAM while polling access windows. Copying consumes the staged pixels.
// The transfer may extend beyond VBlank; this is not an atomic frame swap.
#else
// Store the EndFrame auxiliary-transfer gate; any nonzero value enables it.
// No data is copied or cleared here. Auxiliary source bytes alias the main stage.
void Wire3DDMG_SetAuxTransfer(w3ddmg_u8 flag);
// Store the dirty-transfer gate and clear both dirty histories and the main
// stage, even if the value is unchanged. VRAM is retained; enabling this over an
// old image can leave unmarked old tiles, so arrange a clean displayed baseline.
void Wire3DDMG_SetDirtyTransfer(w3ddmg_u8 flag);
// Upload and consume the full main stage immediately, polling STAT but not
// waiting for the next VBlank. Ignore transfer gates and leave dirty histories
// and BG tile queue unchanged; the caller coordinates the frame lifecycle.
void Wire3DDMG_TransferMainNow();
// Upload and consume auxiliary source strips immediately, independent of the
// auxiliary gate. No VBlank-start wait or dirty-history/BG-queue update occurs.
// The source overlaps the main stage and must be coordinated with its upload.
void Wire3DDMG_TransferAuxNow();
// Wait for the next VBlank and flush BG writes, then optionally consume the
// auxiliary strips before uploading either dirty tiles or the entire main stage.
// Auxiliary/main source overlap makes that ordering significant. STAT polling
// continues across access windows; the update need not fit one VBlank.
#endif
void Wire3DDMG_EndFrame();
// Same queue/gate/history lifecycle, without the initial fresh-VBlank wait.
// Synchronous STAT-safe upload; not an atomic swap. LCD must remain enabled.
void Wire3DDMG_EndFrameNow();
#endif
#if WIRE3D_DMG_HEIGHT == 120
#endifkitaqgb/lib/wire3d_dmg.h
zx0.h —
#pragma once
// Copyright (c) 2026 DAISUKE OBA. MIT License.
// ZX0-compatible forward v2 streams; format designed by Einar Saukas.
#define ZX0_OK 0
#define ZX0_BAD_ARGUMENT 1
#define ZX0_TRUNCATED 2
#define ZX0_BAD_STREAM 3
#define ZX0_OUTPUT_FULL 4
#define ZX0_DISPLAY_ACTIVE 5
// Shared last-call status. These foreground routines are not reentrant.
extern u8 zx0_error;
// Return decoded bytes, or zero on error (possibly after partial writes).
// Source and output must not overlap or cross their current CPU bank windows.
// Capacity and packed_size are byte counts. No prefix/backward/v1 streams.
u16 zx0_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);
// Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto.
// Empty raw assets return zero with zx0_error==ZX0_OK.
u16 asset_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);
// Decode directly into the selected GB VRAM bank ($8000..$9FFF), with LCD off.
// Preserve LCDC, VBK and interrupt state. Output must fit before $A000.
u16 zx0_decompress_vram(u16 vram_addr, const void* src, u16 packed_size);kitaqgb/lib/zx0.h







