KITAQ SERIES • REFERENCE EDITION
KITAQFC LIBRARY
NESライブラリと機器
ローカルソース採取:2026年9月14日
Rust製コンパイラでのライブラリ利用
lib/はゲーム機のROMに組み込むCヘッダーとCソースです。PC用コンパイラと補助ツールはRust製で、ゲームのソース、対象機のライブラリとAPIはCのまま使用します。収録するSOURCE_MANIFEST.jsonとLIBRARY_MERGE.jsonにはライブラリの選択元を記録しています。
kitaqfc · 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月4日
fc.h は physics3d と audio_vblank の宣言を含みます。必要な実装ファイルは別途リンクしてください。entity_update_all と entity_draw_all は、使用中のスロットを ID の昇順でコールバックへ渡します。コールバックは共通バンク0に配置するか、呼び出し中は該当 PRG バンクを維持してください。ZX0 を使う場合は zx0.h を明示的にインクルードします。audio_vblank のレコード順は delay, CH1, CH2, CH3, CH4 です。
現行C#ビルドと以前の配布版を29入力で比較しました。受理した15入力の生成ROMはバイト単位で一致し、受理しなかった14入力のエラー診断も一致しました。さらに2本のROMをKUROSAKIで4フレーム実行し、一方でエンティティの割り当て、コールバック、スロット再利用を、他方で符号付き演算、KQBody3D の配置、オーディオキュー定数を確認しました。検証記録にはRAMの期待値18バイトと実際の値を収録しています。この検証は音声再生や全APIを対象としておらず、以前の回帰試験結果は BINARY_BUILD.json で区別しています。
1 機能を選んで組み込む
fc.h はまとめて宣言を読むヘッダー、core.h は型、intrinsics.h はコンパイラ命令です。通常のCライブラリには対応する .c が必要です。ヘッダーだけのマクロ・組み込み関数には、同名の .c が不要な場合があります。
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio.c .\kitaq-docs\samples\fc_sound.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\font.chr --mapper=nrom -o .\out\sound.nes同じ名前のGB用 lib を -I に指定しないでください。例えば __oam_dma() はNES側では引数なしです。GB版のポインタ引数付きAPIを持ち込むと意味が変わります。
2 runtimeとsystem
runtime.c はPPUレジスター、OAMシャドウ、VRAMキューなどのCヘルパーを持ちます。一方 __vramq_* のような組み込み経路もあるため、どちらのデータをNMIが処理するか確認します。似た名前だけを根拠に、別のキューを混ぜないでください。
system_init はフレーム状態を初期化してNMIを有効にします。system_wait_vblank はNMIを待ち、ソフト側のフレーム数を増やしてから、登録したコールバックを同期的に1回呼びます。system_set_vblank_callback(0) で解除できます。コールバックはNMIハンドラーの中ではなく、待機関数を呼んだ側で実行します。
3 PPU・タイル・属性・パレット
ppu_direct.h は直接PPU操作、vram_queue.h はNMI更新、tilemap / nametable_asset は素材と表、attribute は属性更新、palette はパレット操作です。初期ロードと毎フレーム更新を分けます。
背景パレットは16バイト、スプライトパレットも16バイトのまとまりです。パレット番号はRGB値ではなくNESの色コードです。属性テーブルは複数タイルの色をまとめて選ぶので、1枚だけ色を変えるつもりでも周辺に影響する場合があります。
ppu.h と ppu.c は画面のオン・オフ、32バイトのパレット転送、ネームテーブル全体の初期化を提供します。nes_ppu_seek_bytes(hi,lo) はアドレスラッチをリセットして2バイトのアドレスを設定します。runtime.c の16ビット引数の nes_ppu_seek(address) と併用できます。転送・初期化は描画を止めて実行してください。
4 OAM・メタスプライト・公平表示
NESは最大64スプライト、同一走査線に通常8個です。大量の敵や弾で9個以上が重なると、すべてを同時には表示できません。metasprite は複数OBJを1つの絵として置きます。確保範囲と終端データの形式を確認します。
oam_fair.h / oam_fair_impl.h は優先順位を保ちつつ候補の順序を回すための方式です。重要なプレイヤーやHUDを優先し、残りを時間で交替する設計に向きます。どれだけOAM順を入れ替えても、走査線あたりのハードウェア上限自体は変わりません。
5 入力・連打・周辺機器
input.c はNES生パッド値をGB風の BTN_* マスクへ変換します。生のNESパッドではA=0x01、ライブラリのBTN_Aは0x10 です。KUROSAKIの --pad1 にBTN_Aをそのまま渡さないでください。
pad は基本取得、input_repeat は押し続けたときのリピートです。zapper、keyboard、rob、mic、midi はそれぞれ機器の低水準窓口です。機器なしのゼロ値を「操作成功」と解釈せず、利用側の接続条件を確認します。
keyboard.h — キーボードの行列入力
接続パターンの検査、18バイトへの全行走査、1行・1列群の読み取りを提供します。文字の変換やキーリピートは行いません。
__fkb_detect · __fkb_scan · __fkb_read_row_col
zapper.h — 光線銃の入力線
各ポートの生ビット、トリガー、受光線を読みます。番号なし関数はポート2です。照準座標や命中判定はゲーム側で組み立てます。
__zapper_raw1 · __zapper_raw2 · __zapper_trigger1 · __zapper_trigger2 · __zapper_light1 · __zapper_light2 · __zapper_trigger · __zapper_light
intrinsics.h — シリアルの1ビット入出力
外部アダプター向けにOUT0出力とD4入力を扱います。1回の操作は1ビットだけで、通信速度やバイトの枠組みは呼び出し側が管理します。
__serial_tx_bit · __serial_rx_bit
掲載画像は配布サンプルを変更せずにKUROSAKIで実行した結果です。現在のKUROSAKIはパッドの入力モデルを持ちますが、キーボード・光線銃・シリアルアダプターの信号はモデル化していません。外部入力ビットが0になるため、画像は周辺機器の動作確認ではありません。実機のキー入力、受光、通信は未検証です。
6 音源
NMIで4音源の音楽を進めるには、audio_vblank.hを使います。RAMキューの補充、音の維持・停止、反復再生の説明と、実際に録音したサンプル音声を掲載しています。
nes_apu_init の後、nes_sfx_square1 / nes_sfx_square2 / nes_sfx_triangle / nes_sfx_noise を呼ぶと内蔵音源を鳴らせます。引数のperiodは周波数Hzそのものではなく、ハードウェアの周期値です。fc_sound.c はパルス音の最小例です。
DMCはサンプルのアドレス・長さ・アラインメント・再生レートの制限があります。ポインタを適当に渡さず、配置結果をマップで確認します。DMC DMAが入力読み取りと干渉する候補もあるため、安全パッド読み取りとKUROSAKIの診断を組み合わせます。
VRC6は追加パルス/鋸波、VRC7はFM音源のレジスター操作、FDSは波形音源です。対応マッパーのROMで使い、録音して変化を確認します。音源ライブラリはGBの Audio_* ドライバーとは別APIです。
7 シーン・アクター・エンティティ
actor / entity は固定配列のゲームオブジェクト、scene は場面の状態を扱います。配列の上限を超えた生成を検出し、破棄したIDを使い続けないでください。シーンの切り替え・更新・描画は、それぞれに登録したコールバックを同期的に呼び出します。各APIの呼び出し順と再入制約を確認してください。
chain は ChainBody による関節追従と Chain による座標履歴を提供し、collision は矩形などの接触を判定します。動かす、衝突を調べる、描く、の順序を揃えると1フレーム遅れの判定を避けられます。
8 数学と物理
fixed.h はQ8.8演算、math_fast / math_fixed は数値処理、math_lut は表による計算です。physics2d は矩形bodyの積分・重力・AABB衝突・面応答、physics3d は回転しない箱の3D衝突・反発・衝撃値を扱います。worldとbody配列を用意し、初期化、設定、stepの順に使います。座標と速度は呼び出し側で統一した整数単位です。
Q5.3の小数部は1/8画素単位です。整数座標、小数部、速度、方向を分けて保持し、ゲーム側で加算・桁上がりを処理します。fc_subpixel.c は毎回2/8画素を8回加えて、40画素から42画素へ移動する完全例です。Q8.8の値256をQ5.3へそのまま流用しないでください。
8.1 画面サイズを選べるワイヤーフレーム
wire3d.c は、独自に作成した頂点・辺のモデルを暗い背景にシアン色の線で描画します。 整数の回転計算、逆数表による透視投影、CPU側の描画バッファ、2面のCHR RAMを使います。 市販ゲームのコード・モデル・画像は含めていません。DAISUKE OBAによるMITライセンスのソースです。
画面サイズは64×48、96×64、128×96から選び、wire3d.cを読み込む前に WIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTの両方を定義します。省略時は64×48です。 表示領域は256×240の背景の中央に置かれます。大きい領域ほど線が長く、転送するタイルも 増えるため、実際の場面を計測して選んでください。
#define WIRE3D_FC_WIDTH 96
#define WIRE3D_FC_HEIGHT 64
#include "wire3d.c"
void main(void) {
Wire3DFC_Init();
Wire3DFC_BeginFrame();
Wire3DFC_DrawLine2D(4,4,91,59);
Wire3DFC_DrawLine2D(91,4,4,59);
Wire3DFC_EndFrame();
while(1) {}
}.\kitaqfc\kitaqfc.exe .\cross.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\cross.nesこのサンプルは、選んだ表示領域に交差する2本の対角線を描きます。 ソース内で実装を読み込んでいるため、ビルド時にwire3d.cを重ねて指定しません。 ビルドには--nes-chr-ramを指定します。8 KiBのCHR RAMと、カートリッジ側の $6800–$70BFを読み書きできるPRG RAMが必要です。ゼロページ$00–$0Aも専有します。 CHRの両パターンテーブル、ネームテーブル0、スクロール、背景パレットを他の描画処理と共有しません。 --nes-chrや--chr-romによる画像ファイルの指定とは併用できず、CNROMでも使用できません。
| 関数 | 用途と条件 |
|---|---|
Wire3DFC_Init() | CHRの両画面とCPU側バッファを消去し、中央のタイル配置と配色を設定します。NMIを無効にして背景を表示します。最初に1回呼びます。 |
Wire3DFC_BeginFrame() | 直前の描画で使ったCPU側タイルを消します。表示中の画像は保持します。次の画像を組み立てる前に呼びます。 |
Wire3DFC_DrawLine2D(ax,ay,bx,by) | 両端を含む線をCPU側バッファに描きます。端点は−512~511です。領域外の画素は描かず、両端が同じ側の外にある線は省略します。領域内の線は専用の6502ループで描きます。 |
Wire3DFC_RotatePoint(x,y,z,rx,ry,rz) | 別々のs16変数を指す3本のポインターを渡し、Y・X・Zの順に回転します。32段階で1周し、開始時の各成分は−63~63に収めます。小数部分は0方向に切り捨てます。いずれかのポインターがNULLなら変更しません。 |
Wire3DFC_ProjectPoint(x,y,z,sx,sy) | カメラ座標を画面座標へ変換します。X/Yは−127~127、奥行きは32~255です。成功時は1を返し、表示領域に切り詰める前の符号付き座標を書きます。範囲外や出力ポインターがNULLなら0を返し、出力は保持します。Xの正方向が右、Yの正方向が上です。 |
Wire3DFC_DrawLine3D(ax,ay,az,bx,by,bz) | 両端を投影して線を描きます。一端でも投影条件に合わなければ線全体を省略します。近面・遠面との交点は計算しません。 |
Wire3DFC_DrawModel(vertices,count,edges,edge_count,x,y,z,rx,ry,rz) | 先頭24頂点までを各1回だけ変換・投影して保存し、頂点番号を指定した辺を描きます。頂点はs8の3成分、辺はu8の頂点番号2個です。NULLの配列、不正な頂点番号、投影できない端点は無視します。平行移動の加算をs16の範囲に収めてください。隠面処理は行いません。 |
Wire3DFC_EndFrame() | 非表示側に必要なタイルを書き込み、古い線があったタイルも消去します。1回のVBlankで最大16タイルを送り、転送完了後のVBlankで表示を切り替えます。完了まで待機します。 |
wire3d_uploaded_tilesは転送したタイル数、wire3d_transfer_framesは転送バッチ数と 最後の表示切り替え待機の合計です。頂点計算・線画・準備の時間を含む総フレーム数ではありません。 初期化はNMIを無効にし、描画と転送は呼び出した処理を専有します。 ゲームや音声を並行更新する仕組みは含まれません。割り込みからの再入や、他のPPU更新処理との 併用は避けてください。逆数表の値は整数化され、128幅の最も近い奥行きでは係数256を255に制限します。
HTML説明書の立方体サンプルは、頂点の共有、回転、投影、12本の辺、完成した画像の表示を確認するためのものです。 検証画像ではY軸回転した立方体の辺がつながり、前の画像の線が残らないことを確認します。
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_cube.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\wire_cube.nes


KUROSAKIで3サイズ×3種類の画像を照合し、線・立方体・消去の9ケースを確認しました。さらに、3サイズと4種類のコンパイラ設定で、投影の境界値・NULLポインター・全32段階の各軸回転・複合回転・無効な辺の除外を確認しています。通常の描画サンプルはNROM、境界条件をまとめた大きなテストROMはMMC3を使います。実機での計測結果ではありません。
zx0
ZX0互換の圧縮素材をRAMやVRAMへ展開するライブラリです。PC側のkitaqfc-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`でビルドできます。
.\kitaqfc-zx0.exe tiles.bin tiles.zx0
.\kitaqfc-zx0.exe tiles.bin tiles.h --header=tiles
.\kitaqfc-zx0.exe map.bin map.kqa --format=auto
.\kitaqfc-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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm実装とソース
kitaqfc/lib/zx0.h:16
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.
kitaqfc/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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm実装とソース
kitaqfc/lib/zx0.h:19
Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto. Empty raw assets return zero with zx0_error==ZX0_OK.
kitaqfc/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
ZX0素材全体を呼び出し側のRAM作業領域へ展開し、成功したら128バイト以下の単位でPPUへ転送します。CPUから直接読めないVRAMを履歴に使う代わりに、RAMを使って一致列を復元します。
書式
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size);引数
ppu_addr転送開始PPUアドレス0x0000~0x2FFFです。展開後データ全体が0x3000未満に収まる必要があります。CHR領域ではCHR RAMを搭載したROM構成にしてください。
workspace展開先の書き込み可能なRAMの先頭です。NULLは使えません。容量は配列の要素数ではなくバイト数で指定します。
capacityRAM作業領域の容量です。素材の展開後サイズ全体が入る必要があります。PPUへの転送単位128バイトとは別の制限です。
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側で作った素材と展開後サイズを把握し、十分な容量を確保してください。
__ppu_mask_set(0)などで描画を停止してから呼びます。PPUCTRLの増分を一時的に1へ設定し、終了時にPPUCTRLを復元します。PPUADDR・PPUDATAの操作でPPUアドレスとラッチが変わるため、描画再開前にスクロールを設定してください。割り込み側もPPUを操作しないようにします。CHR ROMやパレットへの転送には使いません。出力先範囲の検査はRAM展開後なので、範囲エラーでも作業領域は変更され得ます。
使用例
count=zx0_decompress_vram(0x0800,tile_workspace,48,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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm実装とソース
kitaqfc/lib/zx0.h:23
Decode into caller-owned RAM, then transfer to CHR RAM or nametable memory. Rendering must be off. Preserve PPUCTRL; set scroll before enabling rendering. The workspace must hold the whole output. No palette upload or CHR ROM writes.
kitaqfc/lib/zx0.c:134
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size) {
u16 size; u16 left; const u8* data; u8 chunk; u8 saved_ctrl;
zx0_error=ZX0_OK;
if(ppu_addr>=0x3000) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
if(__ppu_mask_get()&0x18) { zx0_error=ZX0_DISPLAY_ACTIVE; return 0; }
size=zx0_decompress(workspace,capacity,src,packed_size);
if(zx0_error) return 0;
if(size>0x3000-ppu_addr) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
saved_ctrl=__ppu_ctrl_get(); __ppu_ctrl_set((u8)(saved_ctrl&0xFB));
left=size; data=(const u8*)workspace;
while(left!=0) {
chunk=left>128?128:(u8)left;
__vram_write(ppu_addr,data,chunk);
ppu_addr+=chunk; data+=chunk; left-=chunk;
}
__ppu_ctrl_set(saved_ctrl); return size;
}wire3d — 各関数の使い方
Wire3DFC_Init
CHR RAMと背景マップ、ステージ、変更タイルの履歴を初期化し、水色の線を描く背景表示を有効にします。現在の画面内容は消えます。
書式
void Wire3DFC_Init(void);戻り値
戻り値はありません。
使用条件と制限
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_Init();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:34
Own both CHR-RAM pattern tables and nametable zero. Requires writable 8 KiB CHR RAM and PRG RAM 0x6800..0x70BF; reserves ZP 0x00..0x0A for drawing/transfer. Build with --nes-chr-ram and provide cartridge PRG RAM for the stage. Center the viewport, clear video memory, select cyan-on-dark colors and turn on background rendering with NMI disabled. Do not run another PPU writer.
kitaqfc/lib/wire3d.c:40
void Wire3DFC_Init(void) {
u16 address;u8 x,y,tile;
__ppu_mask_set(0);__ppu_ctrl_set(0);
// Both pattern tables have a zero high bitplane; all subsequent transfers
// update only low bitplanes in the currently hidden table.
for(address=0;address<8192;address+=128)__vram_fill(address,0,128);
for(address=0x2000;address<0x2400;address+=128)__vram_fill(address,255,128);
__vram_fill(0x23C0,0,64);__palette_bg_load(w3dfc_palette);
tile=0;
for(y=0;y<WIRE3D_FC_HEIGHT/8;y++) {
for(x=0;x<WIRE3D_FC_WIDTH/8;x++) {
address=(u16)(0x2000+(u16)(y+(30-WIRE3D_FC_HEIGHT/8)/2)*32+x+(32-WIRE3D_FC_WIDTH/8)/2);
__ppu_addr(address);__ppu_data(tile);tile++;
}
}
__memset(w3dfc_pixels,0,1536);
__memset(w3dfc_dirty,0,192);
__memset(w3dfc_bank0_dirty,0,192);
__memset(w3dfc_bank1_dirty,0,192);
w3dfc_front=0;w3dfc_batch_count=0;
wire3d_transfer_frames=0;wire3d_uploaded_tiles=0;
__scroll_set(0,0);__ppu_ctrl_set(0);__ppu_mask_set(0x0A);
}Wire3DFC_BeginFrame
前の描画で触れたタイルのステージデータを消し、新しい図を描ける状態にします。表示中のCHRテーブルと画面は書き換えません。
書式
void Wire3DFC_BeginFrame(void);戻り値
戻り値はありません。
使用条件と制限
描画のたびに最初に呼びます。EndFrameの前にもう一度呼ぶと、その時点までのステージ上の線を消します。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_BeginFrame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:36
Clear the previous staged drawing. The displayed front image remains intact.
kitaqfc/lib/wire3d.c:63
void Wire3DFC_BeginFrame(void) {
w3dfc_tile_count=WIRE3D_FC_TILES;
// Clear exactly eight bytes per previously touched tile, using the same
// zero-page pointer as upload. No nested C indexing runs in the inner loop.
__asm {
PHA
TXA
PHA
TYA
PHA
LDX #0
w3dfc_clear_next:
LDA w3dfc_dirty,X
BEQ w3dfc_clear_skip
TXA
ASL A
ASL A
ASL A
STA w3dfc_upload_ptr
TXA
LSR A
LSR A
LSR A
LSR A
LSR A
CLC
ADC #104
STA w3dfc_upload_ptr+1
LDA #0
STA w3dfc_dirty,X
LDY #7
w3dfc_clear_byte:
STA (w3dfc_upload_ptr),Y
DEY
BPL w3dfc_clear_byte
w3dfc_clear_skip:
INX
CPX w3dfc_tile_count
BNE w3dfc_clear_next
PLA
TAY
PLA
TAX
PLA
}
}Wire3DFC_DrawLine2D
両端を含む線をステージへ描き、触れたタイルを転送対象として記録します。カメラ投影は行いません。
書式
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by);引数
ax始点の横座標です。表示領域左上を(0,0)とするピクセル値で、−512~511を指定します。
ay始点の縦座標です。表示領域左上を(0,0)とするピクセル値で、−512~511を指定します。
bx終点の横座標です。表示領域左上を(0,0)とするピクセル値で、−512~511を指定します。
by終点の縦座標です。表示領域左上を(0,0)とするピクセル値で、−512~511を指定します。
戻り値
戻り値はありません。
使用条件と制限
座標が−512~511の範囲を外れると線全体を描きません。有効な座標では表示領域外のピクセルを捨てます。描画済みの線とはORで合成し、画面への反映はEndFrameで行います。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_DrawLine2D(0,0,WIRE3D_FC_WIDTH-1,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:39
Draw a connected, inclusive segment. Endpoints must be -512..511. Pixels outside the viewport are clipped; completely outside bounding boxes return.
kitaqfc/lib/wire3d.c:193
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by) {
s16 dx,dy,sx,sy,error,twice;u16 address;u8 tile;
if(ax < -512 || ax>511 || ay < -512 || ay>511 || bx < -512 || bx>511 || by < -512 || by>511)return;
if((ax<0 && bx<0)||(ay<0 && by<0)||(ax>=WIRE3D_FC_WIDTH && bx>=WIRE3D_FC_WIDTH)||(ay>=WIRE3D_FC_HEIGHT && by>=WIRE3D_FC_HEIGHT))return;
dx=bx-ax;if(dx<0)dx=0-dx;
dy=by-ay;if(dy<0)dy=0-dy;
if(ax>=0 && ay>=0 && bx>=0 && by>=0 && ax<WIRE3D_FC_WIDTH && bx<WIRE3D_FC_WIDTH && ay<WIRE3D_FC_HEIGHT && by<WIRE3D_FC_HEIGHT) {
w3dfc_lx=(u8)ax;w3dfc_ly=(u8)ay;
w3dfc_sx=ax<bx?1:255;w3dfc_sy=ay<by?1:255;
if(dx>=dy){w3dfc_major=(u8)dx;w3dfc_minor=(u8)dy;w3dfc_y_major=0;}
else{w3dfc_major=(u8)dy;w3dfc_minor=(u8)dx;w3dfc_y_major=1;}
w3dfc_error=(u8)(w3dfc_major>>1);w3dfc_remaining=(u8)(w3dfc_major+1);
w3dfc_line_inside();return;
}
sx=ax<bx?1:-1;sy=ay<by?1:-1;error=dx-dy;
while(1) {
if(ax>=0 && ax<WIRE3D_FC_WIDTH && ay>=0 && ay<WIRE3D_FC_HEIGHT) {
tile=(u8)(((u8)ay>>3)*(WIRE3D_FC_WIDTH/8)+((u8)ax>>3));
address=(u16)tile*8+((u8)ay&7);
w3dfc_pixels[address]=(u8)(w3dfc_pixels[address]|w3dfc_bits[(u8)ax&7]);
w3dfc_dirty[tile]=1;
}
if(ax==bx && ay==by)return;
twice=error*2;
if(twice > (0-dy)){error-=dy;ax+=sx;}
if(twice < dx){error+=dx;ay+=sy;}
}
}Wire3DFC_RotatePoint
モデル空間の1点をその場で回転します。位置の平行移動や透視投影は行いません。
書式
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz);引数
xx成分を読み書きするs16へのポインターです。3成分は別々の有効な領域を指し、入力値を各−63~63に保ちます。
yy成分を読み書きするs16へのポインターです。3成分は別々の有効な領域を指し、入力値を各−63~63に保ちます。
zz成分を読み書きするs16へのポインターです。3成分は別々の有効な領域を指し、入力値を各−63~63に保ちます。
rxX軸の回転角です。32段階で1周します。
ryY軸の回転角です。32段階で1周します。
rzZ軸の回転角です。32段階で1周します。
戻り値
戻り値はありません。
使用条件と制限
回転量は32段階で1周です。0は回転なし、8は90度、16は180度です。値の下位5ビットを使います。回転の順序はY→X→Zです。
いずれかのポインターが0なら何も変更せず戻ります。Q6整数演算の小数部は0方向へ丸めます。入力範囲を自動的に制限する処理はありません。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_RotatePoint(&px,&py,&pz,0,0,8);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。
サンプルの(24,0,0)はZ軸の90度回転で(0,24,0)になり、投影後の十字が立方体より上に現れます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:42
Rotate a small model-space point in Y/X/Z order. Angles wrap in 32 steps. Start with components in -63..63; supply three distinct writable pointers.
kitaqfc/lib/wire3d.c:227
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz) {
s16 a,b,s,c;
if(x==0 || y==0 || z==0)return;
ry=(u8)(ry&31);rx=(u8)(rx&31);rz=(u8)(rz&31);
if(ry!=0){a=*x;b=*z;s=w3dfc_sin[ry];c=w3dfc_sin[(u8)((ry+8)&31)];*x=w3dfc_q6(a*c+b*s);*z=w3dfc_q6(b*c-a*s);}
if(rx!=0){a=*y;b=*z;s=w3dfc_sin[rx];c=w3dfc_sin[(u8)((rx+8)&31)];*y=w3dfc_q6(a*c-b*s);*z=w3dfc_q6(a*s+b*c);}
if(rz!=0){a=*x;b=*y;s=w3dfc_sin[rz];c=w3dfc_sin[(u8)((rz+8)&31)];*x=w3dfc_q6(a*c-b*s);*y=w3dfc_q6(a*s+b*c);}
}Wire3DFC_ProjectPoint
カメラ空間の1点を表示領域のピクセル座標へ変換します。画面中央が投影の中心です。描画やマスクの変更は行いません。
書式
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy);引数
xカメラから見たX座標です。−127~127を指定します。正のXは右、正のYは上です。
yカメラから見たY座標です。−127~127を指定します。正のXは右、正のYは上です。
zカメラから見たZ座標です。奥行き32~255を指定します。値が大きいほど遠くなります。
sx投影した横座標を書き込むs16ポインターです。2出力は異なる有効な領域を指します。
sy投影した縦座標を書き込むs16ポインターです。2出力は異なる有効な領域を指します。
戻り値
成功は1、座標範囲外または出力ポインターが0なら0です。失敗時は出力先を変更しません。
使用条件と制限
有効な奥行きでも、投影結果は表示領域外になることがあります。画面端への制限は行いません。逆数表による整数近似を使うため、浮動小数点による理想的な投影とは数ピクセル異なる場合があります。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
projected=Wire3DFC_ProjectPoint(px,py,pz+96,&sx,&sy);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。
十字の中心は、64×48で(32,16)、96×64で(48,20)、128×96で(64,33)です。座標は画面全体ではなく各表示領域の左上が原点です。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:46
Project camera-space coordinates using an integer reciprocal table. Require x/y within -127..127 and z within 32..255. Return zero without touching outputs on invalid input; success returns one and writes unclipped screen coordinates.
kitaqfc/lib/wire3d.c:235
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy) {
u8 scale;u16 magnitude;
if(sx==0 || sy==0 || x < -127 || x>127 || y < -127 || y>127 || z<32 || z>255)return 0;
// Focal length follows half the selected width. Use unsigned magnitude
// products and shifts so negative coordinates mirror positive ones exactly.
scale=w3dfc_recip[(u8)z];
if(x<0){magnitude=__mul16x8((u16)(0-x),scale);*sx=WIRE3D_FC_WIDTH/2-(s16)(magnitude>>7);}
else{magnitude=__mul16x8((u16)x,scale);*sx=WIRE3D_FC_WIDTH/2+(s16)(magnitude>>7);}
if(y<0){magnitude=__mul16x8((u16)(0-y),scale);*sy=WIRE3D_FC_HEIGHT/2+(s16)(magnitude>>7);}
else{magnitude=__mul16x8((u16)y,scale);*sy=WIRE3D_FC_HEIGHT/2-(s16)(magnitude>>7);}
return 1;
}Wire3DFC_DrawLine3D
カメラ空間の両端点を投影し、両方が有効なら線を描きます。モデルの回転や平行移動は呼び出し側で済ませます。
書式
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz);引数
ax始点のカメラ空間X座標です。−127~127を指定します。
ay始点のカメラ空間Y座標です。−127~127を指定します。
az始点のカメラ空間Z座標です。奥行き32~255を指定します。
bx終点のカメラ空間X座標です。−127~127を指定します。
by終点のカメラ空間Y座標です。−127~127を指定します。
bz終点のカメラ空間Z座標です。奥行き32~255を指定します。
戻り値
戻り値はありません。
使用条件と制限
片方だけでも投影条件を満たさなければ線全体を捨てます。近接面との交点を求めるクリッピングは行いません。有効な線の画面外ピクセルはDrawLine2Dで除外します。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_DrawLine3D(-24,-24,96,24,-24,96);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:49
Draw when both endpoints pass the projection range. Near-plane crossings are rejected, not intersected with the near plane; screen pixels are clipped.
kitaqfc/lib/wire3d.c:247
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz) {
s16 x0,y0,x1,y1;
if(Wire3DFC_ProjectPoint(ax,ay,az,&x0,&y0)==0)return;
if(Wire3DFC_ProjectPoint(bx,by,bz,&x1,&y1)==0)return;
Wire3DFC_DrawLine2D(x0,y0,x1,y1);
}Wire3DFC_DrawModel
頂点を回転・移動・投影して共有し、辺の配列に従って線を描きます。各辺で同じ頂点を計算し直すことを避けます。
書式
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz);引数
verticess8のx/y/zを持つWire3DFC_Vec3の配列です。頂点成分は各−63~63に保ち、宣言した個数分を用意します。
vertex_count頂点数です。24を超える場合は先頭24頂点だけを扱います。
edges頂点番号a/bを持つWire3DFC_Edgeの配列です。edge_count個の要素を用意します。
edge_count処理する辺の数(0~255)です。使用頂点数以上の番号を含む辺は省略します。
x回転後の頂点へ加えるX方向の位置です。加算後の各点がProjectPointの入力範囲に収まるようにします。
y回転後の頂点へ加えるY方向の位置です。加算後の各点がProjectPointの入力範囲に収まるようにします。
z回転後の頂点へ加えるZ方向の位置です。加算後の各点がProjectPointの入力範囲に収まるようにします。
rxX軸の回転角です。32段階で1周します。
ryY軸の回転角です。32段階で1周します。
rzZ軸の回転角です。32段階で1周します。
戻り値
戻り値はありません。
使用条件と制限
回転量は32段階で1周です。0は回転なし、8は90度、16は180度です。値の下位5ビットを使います。回転の順序はY→X→Zです。
verticesまたはedgesが0なら描画しません。表は現在のPRGマッピングで読み取れる必要があります。面データや奥行きマスクは使わず、有効な辺をすべて描きます。立方体の奥側の辺も見えます。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_DrawModel(example_vertices,8,example_edges,12,0,0,96,0,3,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:53
Rotate, translate and project up to 24 vertices, then draw indexed edges. Invalid edge indices/depth endpoints are skipped. Model data must remain readable in the current PRG mapping. This draws all edges, without face culling.
kitaqfc/lib/wire3d.c:253
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz) {
u8 i,a,b;s16 px,py,pz;
if(vertices==0 || edges==0)return;
if(vertex_count>24)vertex_count=24;
for(i=0;i<vertex_count;i++) {
px=vertices[i].x;py=vertices[i].y;pz=vertices[i].z;
Wire3DFC_RotatePoint(&px,&py,&pz,rx,ry,rz);
w3dfc_valid[i]=Wire3DFC_ProjectPoint(px+x,py+y,pz+z,&w3dfc_projected_x[i],&w3dfc_projected_y[i]);
}
for(i=0;i<edge_count;i++) {
a=edges[i].a;b=edges[i].b;
if(a<vertex_count && b<vertex_count && w3dfc_valid[a]!=0 && w3dfc_valid[b]!=0)
Wire3DFC_DrawLine2D(w3dfc_projected_x[a],w3dfc_projected_y[a],w3dfc_projected_x[b],w3dfc_projected_y[b]);
}
}Wire3DFC_EndFrame
触れたタイルと、裏側CHRテーブルの消去が必要なタイルを転送し、完了後のVBlankで表示面を切り替えます。
書式
void Wire3DFC_EndFrame(void);戻り値
戻り値はありません。
使用条件と制限
1回のVBlankで最大16タイル(低位ビットプレーン128バイト)を転送します。必要な回数だけ待つ同期処理です。wire3d_uploaded_tilesは転送したタイル数、wire3d_transfer_framesは転送バッチ数+表示切り替えの待機1回であり、ゲーム全体で経過したフレーム数ではありません。
裏側の過去の描画も考慮するため、物体が移動した後に古い線を消せます。タイルの全内容を前後比較する差分検出ではなく、描画で触れたタイルの履歴を使います。
CHR RAM 8 KiBと、書き込み可能なカートリッジPRG RAM $6800~$70BF、ゼロページ$00~$0Aを使います。--nes-chr-ramでビルドしてください。共有作業領域を使うため再入できません。
Init→BeginFrame→描画→EndFrameの順に使います。背景パターンテーブル2面、ネームテーブル0、背景パレット、スクロールをライブラリが管理し、NMIを無効にします。他のPPU書き込み処理と同時に動かさないでください。
表示領域は64×48(既定)、96×64、128×96です。wire3d.cをインクルードする前にWIRE3D_FC_WIDTHとWIRE3D_FC_HEIGHTを組で指定します。画面中央へ配置され、小さいサイズほど描画・転送量を減らせますが、予約するRAM容量は同じです。
掲載画像はKUROSAKIで3サイズを実行し、全256×240画素の位置・色とRAMの計算結果を照合したものです。実機の速度を保証する測定ではありません。
使用例
Wire3DFC_EndFrame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成画像は黒地に水色の枠、中央の立方体、上側の十字、下側の横線です。枠は2D座標、十字は点の回転と投影、横線は3D線、立方体は頂点・辺の配列を使う例です。各関数の役割を同じ画面で見比べられます。



ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png実装とソース
kitaqfc/lib/wire3d.h:57
Upload changed tiles to the hidden CHR table in bounded VBlank batches and swap only after completion. Blocks across as many frames as needed. The two counters report this call's upload batches plus swap, and tile count.
kitaqfc/lib/wire3d.c:336
void Wire3DFC_EndFrame(void) {
u8 tile,back,old;u16 source,destination;
back=(u8)(w3dfc_front^16);wire3d_transfer_frames=0;wire3d_uploaded_tiles=0;w3dfc_batch_count=0;
for(tile=0;tile<WIRE3D_FC_TILES;tile++) {
if(back==0)old=w3dfc_bank0_dirty[tile];else old=w3dfc_bank1_dirty[tile];
if(old!=0 || w3dfc_dirty[tile]!=0) {
source=(u16)(0x6800+(u16)tile*8);
destination=(u16)((u16)back*256+(u16)tile*16);
w3dfc_src_lo[w3dfc_batch_count]=(u8)source;w3dfc_src_hi[w3dfc_batch_count]=(u8)(source>>8);
w3dfc_dst_lo[w3dfc_batch_count]=(u8)destination;w3dfc_dst_hi[w3dfc_batch_count]=(u8)(destination>>8);
w3dfc_batch_count++;wire3d_uploaded_tiles++;
if(back==0)w3dfc_bank0_dirty[tile]=w3dfc_dirty[tile];else w3dfc_bank1_dirty[tile]=w3dfc_dirty[tile];
if(w3dfc_batch_count==16)w3dfc_upload_batch();
}
}
if(w3dfc_batch_count!=0)w3dfc_upload_batch();
// Switch pattern tables only at a fresh VBlank, after every transfer finishes.
while((w3dfc_status&128)!=0){}
while((w3dfc_status&128)==0){}
w3dfc_front=back;__ppu_ctrl_set(back);__scroll_set(0,0);
wire3d_transfer_frames++;
}9 素材・マッパー・FDS
bank / asset はPRGバンクと素材の記述を扱います。mapper.h の操作が実際に有効かは、ビルド時に選んだマッパーによります。スクロールIRQを使うときは、IRQの設定・有効化・ack・禁止を一組で設計します。
FDSは fds_file、fds_overlay、fds_save、fds_sound に分かれます。オーバーレイを切り替えると同じメモリに別コードが入るため、戻り先やデータの寿命を特に確認します。通常のカートリッジ用farcallと同じと決めつけないでください。
10 機能別の辞典と使用例
以下の辞典は公開ヘッダーごとに整理しています。関数、関数形式のマクロ、別名を区別し、構造体や定数はヘッダー全文からも読めます。実装のない宣言、保存だけのコールバック、特殊機器用の窓口は、動作を確認した一般機能と区別します。原ソースのコメントは正確さを保つため原文も残しています。
vram_get_queue_capacity() はコマンドバッファの総容量(128バイト)、vram_get_queue_free() は総容量から vram_get_queue_used() を引いた空きバイト数を返します。アドレスなどの管理情報を含むコマンドのサイズであり、実際のVRAMの空き容量ではありません。タイル1個の書き込みには4バイト、同じ値での埋め込みには5バイト、ポインターを使ったコピーには6バイト必要です。commit後はNMIでキューが消費されるため、空きの確認はcommit前に行ってください。
完全なサンプルプログラム
各プログラムの本体、共通ヘッダー、指定ascii.cとその変換素材を同梱しています。説明中のm_*は本書専用ヘルパーです。ビルドと操作・期待結果を一組として参照してください。
英数字・記号の一覧表示 fc_font.c
目的:同梱の自作フォントで使える文字と、その表示方法を確認します。
処理の流れ:m_initで画面とフォントを準備し、m_textで文字列を行ごとに表示します。二重引用符は文字コード34をm_putへ渡して描きます。
操作と期待する結果:大文字26字、小文字26字、数字10字、記号30字が並びます。各文字が欠けずに表示されるか確認してください。操作は不要です。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_font.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_font.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Display the letters, digits and punctuation from the supplied ascii.c font.
// Expected: Uppercase, lowercase, numbers and all 30 supplied punctuation glyphs
#include "fc_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(void) {
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(); }
}最初の文字表示 fc_hello.c
目的:ROMを起動し、文字列と数値を背景に表示する最小の流れを学びます。
処理の流れ:m_initの後にm_textで挨拶を描き、m_numberに42を渡します。最後のループは画面を保ったままフレームを待ちます。
操作と期待する結果:HELLO WORLDと042が表示されます。042は3桁にそろえた十進数42です。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_hello.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_hello.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Display a first message on screen.
// Expected: HELLO WORLD and 042
#include "fc_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(void) {
m_init();
m_text(2,3,"HELLO");
m_text(2,5,"HELLO WORLD");
m_number(42);
while (1) { m_wait(); }
}桁あふれを避ける算術 fc_arithmetic.c
目的:8ビットに収まらない途中結果を16ビットで扱う方法を学びます。
処理の流れ:(u16)200 + 100で300を作り、10で割ってから表示用にu8へ変換します。足し算の前の型変換に注目してください。
操作と期待する結果:030、つまり計算結果30が表示されます。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_arithmetic.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_arithmetic.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Variables, arithmetic and type conversion.
// Expected: 030
#include "fc_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(void) {
m_init();
m_text(2,3,"ARITHMETIC");
u16 sum;
sum = (u16)200 + 100;
m_number((u8)(sum / 10));
while (1) { m_wait(); }
}条件分岐と繰り返し fc_control.c
目的:for、continue、while、if/elseを小さな計算で試します。
処理の流れ:0~7のうち3を飛ばして合計25を作り、whileで32まで増やします。ifで32との一致を判定し、成功側で42を代入します。
操作と期待する結果:042が表示されます。このプログラムで使う分岐はif/elseです。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_control.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_control.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Conditional branches and loops.
// Expected: 042
#include "fc_common.h"
// Exercise loop control and a final branch, then display the selected value 42.
void main(void) {
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<32) { sum++; }
if (sum==32) sum=42; else sum=0;
m_number(sum);
while (1) { m_wait(); }
}関数・配列・構造体・ポインタ fc_aggregate.c
目的:複数の値をまとめ、関数やポインタを通して扱う方法を学びます。
処理の流れ:Pointの(10,20)を別の構造体へコピーし、ポインタから配列に1~4を書きます。addの戻り値30に配列の1、2、4と定数5を加えます。
操作と期待する結果:合計42が042と表示されます。構造体コピー、ptr[i]による書き込み、関数の戻り値を順に追ってください。
![関数・配列・構造体・ポインタ: 合計42が042と表示されます。構造体コピー、ptr[i]による書き込み、関数の戻り値を順に追ってください。](verification/fc_aggregate.png)
.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_aggregate.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_aggregate.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Functions, arrays, structures and pointers.
// Expected: 042
#include "fc_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(void) {
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(); }
}ビット操作とバッファのコピー fc_bits_memory.c
目的:複数のフラグを1バイトにまとめ、バッファを別の領域へコピーします。
処理の流れ:__memsetで4バイトを0にし、ビット3を立て、ビット1を反転します。__memcpyで4バイトすべてを別配列にコピーします。
操作と期待する結果:コピー先の先頭バイトが010になります。ビット3の8とビット1の2を合わせた十進数10です。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_bits_memory.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_bits_memory.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Bit operations and memory copying.
// Expected: 010
#include "fc_common.h"
// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main(void) {
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(); }
}押した瞬間だけを数える fc_input.c
目的:ボタンを押し続ける状態と、新しく押した瞬間を区別します。
処理の流れ:毎フレームinput_updateを呼び、input_pressed(BTN_A)が真のときだけカウンターを増やします。
操作と期待する結果:最初は000です。Aを押すと001になり、押し続けても増えません。離して押し直すと002になります。255の次は000に戻ります。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/input.c kitaq-docs/samples/fc_input.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_input.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Distinguish a new button press from a held button.
// Expected: 000 initially; A increments once per press
#include "fc_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(void) {
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固定小数点の掛け算 fc_fixed.c
目的:整数とQ8.8固定小数点の相互変換、および掛け算の基本を学びます。
処理の流れ:3と4をfix_from_intでQ8.8へ変換し、fix_mulで掛け、fix_to_intで整数へ戻します。
操作と期待する結果:3×4の結果12が012と表示されます。画面に出るのは内部の256倍された値ではなく、整数へ戻した値です。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/fixed.c kitaq-docs/samples/fc_fixed.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_fixed.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Calculate with signed Q8.8 fixed-point values.
// Expected: 012
#include "fc_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(void) {
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(); }
}固定長プールからの生成 fc_entity.c
目的:動的メモリ確保を使わず、決まった個数の領域からオブジェクトを作ります。
処理の流れ:entity_initで初期化し、種類1のオブジェクトを(20,30)に作ります。IDが失敗値0xFFでないことを確認してvxへ2を設定します。
操作と期待する結果:001は使用中オブジェクトの個数です。IDや座標ではありません。この例は更新・描画を行わないので、動くキャラクターは表示されません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/entity.c kitaq-docs/samples/fc_entity.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_entity.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Allocate an entity from a fixed-capacity object pool.
// Expected: 001
#include "fc_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(void) {
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へ記録する fc_debug.c
目的:画面表示とは別に、ゲーム中の変数を診断用バッファへ残します。
処理の流れ:debug_initで初期化し、フレームタグ1を設定してHPという名前で42を記録します。
操作と期待する結果:画面の001は記録件数です。HP=42という値そのものは画面ではなく診断バッファにあります。画面だけでは記録内容までは確認できません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/debug.c kitaq-docs/samples/fc_debug.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_debug.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Record diagnostic values in the debug trace.
// Expected: 001; trace log contains HP=42
#include "fc_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(void) {
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(); }
}座標履歴のリングバッファ fc_chain.c
目的:キャラクターの軌跡などに使う座標履歴の格納方法を学びます。
処理の流れ:4点分の配列をchain_initに渡し、chain_push_headで(20,30)を1点追加します。
操作と期待する結果:042は処理後に表示する固定の目印です。保存した座標や件数を表していません。履歴の内容を確かめるにはtrailとpointsを観察します。追従する胴体の描画例ではありません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/chain.c kitaq-docs/samples/fc_chain.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_chain.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Store a position history in a ring buffer.
// Expected: 042; history contains (20,30)
#include "fc_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(void) {
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(); }
}ライブラリのフレームカウンター fc_system.c
目的:フレーム待機とライブラリが管理する経過フレーム数の関係を確認します。
処理の流れ:system_initの後にsystem_wait_vblankを2回呼び、system_get_frame8の値を表示します。
操作と期待する結果:002が表示されます。その後のループは本書のm_waitを使うため、この表示値は増え続けません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/system.c kitaq-docs/samples/fc_system.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_system.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Count completed frame waits.
// Expected: 002
#include "fc_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(void) {
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を選ぶ fc_scene.c
目的:タイトル画面やゲーム本編などを区別するシーン番号の選択方法を学びます。
処理の流れ:0で初期化された2件のSceneDefを登録し、scene_change(1)でID 1を選択します。
操作と期待する結果:001はscene_get_currentが返す現在のIDです。この例にはシーンのコールバックや画面切り替えの描画は登録していません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/runtime.c kitaqfc/lib/scene.c kitaq-docs/samples/fc_scene.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_scene.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// The scene streaming module shares runtime.c queue symbols, even though this demo only changes IDs.
#define MANUAL_FC_RUNTIME 1
// Switch the current scene ID.
// Expected: 001; no callbacks registered
#include "fc_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(void) {
m_init();
m_text(2,3,"SCENE");
scene_init(manual_scenes,2); scene_change(1);
m_number(scene_get_current());
while (1) { m_wait(); }
}Q5.3で端数の移動を積み重ねる fc_subpixel.c
目的:1ピクセル未満の移動を端数として保持し、整数座標へ繰り上げる方法を学びます。
処理の流れ:X=40から、2/8ピクセルの移動を8回加算します。右シフトで整数部分を取り出し、マスクで端数を残します。
操作と期待する結果:合計2ピクセル進み、X=42が042と表示されます。これは正方向の手動積算例です。負方向には対応する繰り下げ処理が必要です。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_subpixel.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_subpixel.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Accumulate Q5.3 eighth-pixel motion into an integer coordinate.
// Expected: 042; explicit game-side integration using header constants
#include "fc_common.h"
#include "physics2d.h"
// Integrate eight increments of 2/8 pixel. Carry whole pixels into X and
// retain the fractional remainder with the supplied mask, moving X from 40 to 42.
void main(void) {
m_init();
m_text(2,3,"SUBPIXEL");
KQ2DPosition x; KQ2DFraction frac; KQ2DSpeed speed; u8 i;
// This example demonstrates positive motion only; signed negative motion needs a matching carry/borrow path.
x=40; frac=0; speed=2;
for(i=0;i<8;i++) {
frac=(u8)(frac+speed);
x=(u8)(x+(frac>>KQ2D_SUBPIXEL_BITS));
frac=(u8)(frac & KQ2D_SUBPIXEL_MASK);
}
m_number(x);
while (1) { m_wait(); }
}NES背景を横へスクロールする fc_scroll.c
目的:PPUのスクロール位置を更新して背景を動かします。
処理の流れ:__scroll_set(0,0)で開始し、フレームごとにphaseを1増やして横位置へ設定します。
操作と期待する結果:SCROLLという文字の横位置が変わります。phaseは255から0へ戻ります。複数のネームテーブルを使う広いマップの例ではありません。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_scroll.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_scroll.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Scroll the NES background horizontally.
// Expected: title scrolls horizontally
#include "fc_common.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(void) {
m_init();
m_text(2,3,"SCROLL");
__scroll_set(0,0);
while (1) { m_wait(); phase++; __scroll_set(phase,0); }
}NMIでタイル更新を反映する fc_vram_queue.c
目的:メイン処理からPPU更新をキューへ送り、NMI側で反映する流れを学びます。
処理の流れ:__vramq_putでネームテーブルの0x2103へタイル52を予約し、__vramq_commitで送信します。0x2103はタイル座標(3,8)です。
操作と期待する結果:背景の(3,8)に数字4が表示されます。番号52は同梱フォントの数字4に対応します。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_vram_queue.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_vram_queue.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Submit a VRAM update for the NMI handler.
// Expected: 4 at (3,8)
#include "fc_common.h"
// Queue tile 52 at nametable address 0x2103, corresponding to (3,8), and
// commit the queue so the NMI update path displays the digit 4.
void main(void) {
m_init();
m_text(2,3,"VRAM QUEUE");
__vramq_put(0x2103,52); __vramq_commit();
while (1) { m_wait(); }
}NESのOAMスプライト fc_sprite.c
目的:スプライト用の色とOAM情報を準備して、背景とは別の画像を表示します。
処理の流れ:スプライトパレットを読み込み、スロット0へ位置(70,80)、タイル65、属性0を設定します。表示を有効にしてOAM DMAを繰り返します。
操作と期待する結果:文字Aのスプライトが固定位置に表示されます。動く画像ではなく、OAM設定と転送の基本例です。

.\kitaqfc\kitaqfc.exe kitaq-docs/samples/fc_sprite.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_sprite.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Display a sprite using NES OAM.
// Expected: A sprite on screen
#include "fc_common.h"
// Load the sprite palette, assign font tile 65 to OAM slot zero, enable
// background/sprite display, and transfer OAM after each frame wait.
void main(void) {
m_init();
m_text(2,3,"SPRITE");
__palette_sp_load(manual_pal); __sprite_set(0,70,80,65,0);
__ppu_mask_set(0x1E);
while (1) { m_wait(); __oam_dma(); }
}APUのパルス音を鳴らす fc_sound.c
目的:NES本体のパルスチャンネルへ音量・タイマー・長さを設定します。
処理の流れ:nes_apu_initの後にnes_sfx_square1(0xBF,400,20)を呼びます。0xBFは一定音量と長さカウンター停止を指定するため、音は持続します。20は長さテーブルの番号で、20フレームという意味ではありません。
操作と期待する結果:042を表示しながらパルス音が鳴り続けます。音を止めるにはnes_apu_silence_allを呼びます。042自体は発音確認の結果ではありません。

.\kitaqfc\kitaqfc.exe kitaqfc/lib/audio.c kitaq-docs/samples/fc_sound.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_sound.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr// Play a pulse-channel effect on the built-in NES APU.
// Expected: 042 and a pulse tone
#include "fc_common.h"
#include "audio.h"
// Initialize the APU, trigger pulse channel 1 with the chosen timer/length
// values, and leave the screen displaying 42 while the hardware plays the tone.
void main(void) {
m_init();
m_text(2,3,"SOUND");
// Control BF selects constant volume and halts the length counter, so this tone sustains.
// Call nes_apu_silence_all when the game should stop it; 20 is a length-table index, not frames.
nes_apu_init(); nes_sfx_square1(0xBF,400,20);
m_number(42);
while (1) { m_wait(); }
}ライブラリAPI辞典
全294項目。各項目では、機能、引数、戻り値、制約、使用例と期待する結果を説明します。
actor.h — ゲームアクターの配列
ワールド座標・画面座標・矩形サイズをNesActorにまとめます。カメラからの相対座標、メタスプライトOAM生成、ワールド矩形の衝突を扱い、移動速度・画面外の非表示・OAM DMAはゲーム側で管理します。
nes_actor_collide
ワールド座標の軸に平行な矩形同士が重なるか調べます。
書式
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b);引数
a1つ目のアクター。
b2つ目のアクター。
戻り値
重なれば1、離れているか辺が接するだけなら0。
使用条件と制限
幅と高さは1以上、座標+サイズは65535以下にします。画面座標・attr・flagsは判定に使いません。
使用例
result[3]=nes_actor_collide(&a,&b);nes_actor_set_world(&b,315,95);result[4]=nes_actor_collide(&a,&b);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ワールド(300,95)のアクターへカメラ(268,32)を適用し、画面(32,64)と(40,64)へ赤と青の8×8パーツを並べます。生のOAM Y=63が表示では64になること、辺が接するだけでは衝突せず1ピクセル重なると衝突することを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/actor.h:29
Test overlap in world coordinates with exclusive right/bottom edges. Keep position-plus-size sums within u16; touching edges are not collisions. Require positive widths and heights; zero extents are not rejected before the edge tests.
kitaqfc/lib/actor.c:49
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b)
{
unsigned short a_right;
unsigned short b_right;
unsigned short a_bottom;
unsigned short b_bottom;
a_right = (unsigned short)(a->world_x + a->width);
b_right = (unsigned short)(b->world_x + b->width);
a_bottom = (unsigned short)(a->world_y + a->height);
b_bottom = (unsigned short)(b->world_y + b->height);
if (a_right <= b->world_x)
{
return 0;
}
if (b_right <= a->world_x)
{
return 0;
}
if (a_bottom <= b->world_y)
{
return 0;
}
if (b_bottom <= a->world_y)
{
return 0;
}
return 1;
}nes_actor_draw_metasprite
保存済み画面座標を基点に、メタスプライトをシャドーOAMへ書きます。
書式
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite);引数
actor書き込み可能なNesActorへのポインタ。
oam_index開始スロット0~63。全パーツ分の空きを確保します。
metaspritedx,dy,tile,attrの4バイト列。dx=255が終端です。
戻り値
次のOAMスロット番号。64スロット末尾を越えると周回します。
使用条件と制限
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
attr/flagsフィールドは適用しません。Yは生のOAM値なので表示上端は1ピクセル下です。実際の表示にはOAM DMAが別途必要です。
使用例
result[5]=nes_actor_draw_metasprite(&a,0,parts);result[6]=nes_oam_shadow[3];result[7]=nes_oam_shadow[7];nes_oam_dma(2);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ワールド(300,95)のアクターへカメラ(268,32)を適用し、画面(32,64)と(40,64)へ赤と青の8×8パーツを並べます。生のOAM Y=63が表示では64になること、辺が接するだけでは衝突せず1ピクセル重なると衝突することを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/actor.h:25
Draw the byte-stream metasprite at cached screen coordinates and return its next OAM index. Actor attr/flags are not applied by this wrapper.
kitaqfc/lib/actor.c:41
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite)
{
return nes_metasprite_draw(oam_index, actor->screen_x, actor->screen_y, metasprite);
}nes_actor_set_world
アクターのワールドX/Yを更新します。
書式
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y);引数
actor書き込み可能なNesActorへのポインタ。
xワールドX、0~65535ピクセル。
yワールドY、0~65535ピクセル。
戻り値
戻り値はありません。
使用条件と制限
screen_x/screen_yは更新しません。カメラを反映するにはupdate_screenを呼びます。
使用例
nes_actor_set_world(&a,300,95);nes_actor_set_world(&b,316,95);result[0]=a.world_x;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ワールド(300,95)のアクターへカメラ(268,32)を適用し、画面(32,64)と(40,64)へ赤と青の8×8パーツを並べます。生のOAM Y=63が表示では64になること、辺が接するだけでは衝突せず1ピクセル重なると衝突することを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/actor.h:19
Set the actor's 16-bit world position without updating cached screen coordinates.
kitaqfc/lib/actor.c:19
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y)
{
actor->world_x = x;
actor->world_y = y;
}nes_actor_update_screen
ワールド座標からカメラ座標を引き、下位8ビットを画面座標へ保存します。
書式
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y);引数
actor書き込み可能なNesActorへのポインタ。
camera_xカメラのワールドX。
camera_yカメラのワールドY。
戻り値
戻り値はありません。
使用条件と制限
負や256以上の結果は周回します。画面外のアクターを自動で隠さないので、描画前にゲーム側で判定します。
使用例
nes_actor_update_screen(&a,268,32);result[1]=a.screen_x;result[2]=a.screen_y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ワールド(300,95)のアクターへカメラ(268,32)を適用し、画面(32,64)と(40,64)へ赤と青の8×8パーツを並べます。生のOAM Y=63が表示では64になること、辺が接するだけでは衝突せず1ピクセル重なると衝突することを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/actor.h:22
Subtract the camera and retain only the low coordinate bytes. This performs wrapping projection, not clipping; callers must hide actors outside the viewport.
kitaqfc/lib/actor.c:27
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y)
{
unsigned short dx;
unsigned short dy;
dx = (unsigned short)(actor->world_x - camera_x);
dy = (unsigned short)(actor->world_y - camera_y);
actor->screen_x = (unsigned char)dx;
actor->screen_y = (unsigned char)dy;
}asset.h — 素材IDとデータ記述表
素材ライブラリは、ROMバンクに置いた素材を8ビットのIDで検索します。asset.cを取り込み、FC版ではbank.cも取り込みます。パックされた6バイトのAssetDescには、1バイトの種別、1バイトの素材バンク番号、16ビットのアドレス、バイト単位の長さを格納します。一覧と素材本体は別のバンクに配置できます。
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
RAWまたはTILES種別の素材を検索し、そのROMバンクへ切り替え、__vram_writeで最大128バイトずつ全データを転送します。最後にbank_get_current()で保存したバンクへ戻します。分割転送の途中で次のフレームを待つ処理はありません。
絵柄データには書き込み可能なCHR RAMを使い、大きな転送では描画を停止します。VBlank中に転送するなら処理全体がその時間内に収まる必要があり、128バイト分割だけでは時間制限を守れません。このサンプルは--mapper=mmc1 --board=surom512でビルドします。汎用ボードで--nes-chrを省略しても、作られるのは空のCHR ROMで、CHR RAMにはなりません。
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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:25
Copy a descriptor from its ROM bank into caller storage. Null output or an out-of-range ID returns zero without reading the table.
kitaqfc/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;
__far_memcpy((u8*)out_desc, asset_table_bank, (u16)(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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:33
Return the asset bank or zero on lookup failure; bank zero may also be valid.
kitaqfc/lib/asset.c:74
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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:38
Return the descriptor byte length, or zero for an invalid ID.
kitaqfc/lib/asset.c:91
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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:36
Return the stored address without switching banks. Pair it with the descriptor bank before reading banked ROM; a failed lookup returns null.
kitaqfc/lib/asset.c:83
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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:28
Copy the smaller of the asset length and max_len using a bank-aware transfer. Return one for a valid ID even when the payload is truncated.
kitaqfc/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;
__far_memcpy((u8*)dst, desc.bank, (u16)desc.ptr, len);
return 1;
}asset_load_tiles
RAWまたはTILES種別の素材を検索し、そのROMバンクへ切り替え、__vram_writeで最大128バイトずつ全データを転送します。最後にbank_get_current()で保存したバンクへ戻します。分割転送の途中で次のフレームを待つ処理はありません。
書式
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);引数
asset_id0から始まる素材番号です。登録済み一覧の件数を
countとすると、asset_id < countの範囲が有効です。vram_dstCPUアドレスやタイル番号ではなく、PPUのバイトアドレスです。タイル絵柄の転送には
$0000~$1FFFの書き込み可能なCHR RAMを使い、1タイルは16バイトです。全転送が目的のPPU領域内に収まるようにしてください。
戻り値
IDが無効、または種別がRAW/TILES以外なら0です。受け付けた転送を終えると1で、長さ0の素材も含みます。成功値はタイル形式やVRAM表示の正しさを検証した結果ではありません。
使用条件と制限
検索前に一覧を登録してください。登録情報は共有のグローバル状態なので、検索と同時に変更してはいけません。ライブラリはIDとasset_getの出力ポインタを確認しますが、登録した一覧や素材のアドレス、カートリッジ内容の正しさまでは検査しません。
サンプルの呼び出し側コードは固定バンク0に配置し、コピー元の切り替えで実行中のコードが見えなくなることを防ぎます。転送は同期処理です。転送中に割り込み側から別のバンク切り替えコピーを実行したり、この関数だけでアプリ全体の割り込み安全性が確保されると考えたりしないでください。
転送はバイトを書き込むだけで、マップへのタイル配置、配色、VRAMの割り当ては行いません。タイルマップとパレットは別途設定します。サンプルでは描画を止めて転送し、画像の準備ができてから表示を有効にします。
asset_load_tilesの復帰先は、マッパーから読み取った値ではなくbank.cが記録した値です。bank_switchで初期化・更新してください。マッパーへの直接書き込みや__bankswitchだけの呼び出しでは、ライブラリ側の記録は更新されません。バンク間コピーの組み込み関数は、これとは別にコンパイラ側のバンク状態を復帰します。
絵柄データには書き込み可能なCHR RAMを使い、大きな転送では描画を停止します。VBlank中に転送するなら処理全体がその時間内に収まる必要があり、128バイト分割だけでは時間制限を守れません。このサンプルは--mapper=mmc1 --board=surom512でビルドします。汎用ボードで--nes-chrを省略しても、作られるのは空のCHR ROMで、CHR RAMにはなりません。
使用例
loaded=asset_load_tiles(0,0x0010); // 144 bytes: library uploads chunks of 128 and 16 bytes.
expect(loaded==1 && bank_get_current()==1 && *asset_bank_marker==0xA1,24);
// LCD and NMI are off; CHR RAM is writable. The library selects payload bank 2 and restores bank 1.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
タイル転送例は独自の8×8ピクセルの絵柄9枚、合計144バイトを使います。MAP種別と無効IDが拒否されることも確認します。FC版では144バイトを転送することで、128バイトの塊と残り16バイトの両方を検証します。
期待画像では、赤い横一列がピクセル座標(16,32)から始まり、72×8ピクセルを占めます。8×8の三角形、1ピクセル枠の8×8の正方形、左4列を塗ったタイルの順に、3回繰り返します。三角形は左上の1ピクセルから最下段の8ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:31
Switch to the asset bank, upload tile/raw data in chunks of at most 128 bytes, then restore the bank recorded by this library. The caller supplies safe PPU timing.
kitaqfc/lib/asset.c:41
u8 asset_load_tiles(u8 asset_id, u16 vram_dst)
{
AssetDesc desc;
const u8* src;
u16 len;
u8 chunk;
u8 old_bank;
if (asset_get(asset_id, &desc) == 0) return 0;
if (desc.type != ASSET_TYPE_TILES && desc.type != ASSET_TYPE_RAW) return 0;
// Restoration uses the library shadow, not a mapper readback; initialize and maintain that shadow.
old_bank = bank_get_current();
bank_switch(desc.bank);
src = desc.ptr;
len = desc.len;
// This loop uploads the entire asset synchronously. Chunking does not wait for another frame.
// Use writable CHR RAM for tile data and keep the source bank visible throughout the transfer.
while (len != 0) {
if (len > 128) chunk = 128;
else chunk = (u8)len;
__vram_write(vram_dst, src, chunk);
vram_dst = (u16)(vram_dst + chunk);
src = src + chunk;
len = (u16)(len - chunk);
}
bank_switch(old_bank);
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ピクセルまで広がります。
FC版では、(16,64)の青い三角形と枠が__far_memcpyの結果、(16,96)の緑の組がasset_load_rawの後にCHRへ転送した結果です。黒地の各組は16×8ピクセルです。数値だけでなく、コピーした絵柄のバイト列が正しいことを目で確認できます。
期待する表示はFAILED CHECKS 000とFIRST FAILURE 00です。検証では300フレーム実行し、文字、図形の形、色を期待値と比較します。これはエミュレータ上の検証であり、実機試験ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm実装とソース
kitaqfc/lib/asset.h:22
Retain a bank-qualified descriptor table; the caller keeps its storage valid.
kitaqfc/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;
}attribute.h — ネームテーブル属性
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
パレット区画の矩形編集と、必要な属性バイトだけのキュー転送を扱います。32×30タイルの範囲検査を行います。palette_map入力は完成済みの属性64バイトで、1タイル1色の配列ではありません。
nes_attr_queue_rect
タイル矩形が触れる属性バイトを行単位でキューへコピーします。
書式
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
width幅(タイル数)。
height高さ(タイル数)。
戻り値
全行登録または空矩形なら1、範囲外または容量不足なら0。範囲外では登録を始めません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
1属性バイトは4×4タイルです。関数は矩形に触れるバイト全体を転送するため、隣接区画の値もシャドーへ正しく保持しておきます。
使用例
result[5]=nes_attr_queue_rect(0x2000,2,4,4,4);result[6]=nes_vram_queue_used;result[7]=nes_attr_queue_rect(0x2000,10,4,4,4);result[8]=nes_attr_queue_rect(0x2000,30,28,2,2);nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
属性の矩形転送を学びます。(16,32)に赤の32×32、(80,32)に青の32×32、画面右下(240,224)に緑の16×16を表示します。隣の区画を壊さず、右下の属性バイトまで正しく転送する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/attribute.h:15
Queue the attribute bytes covering a tile rectangle within 32x30 tiles. Empty rectangles succeed; invalid bounds fail without queuing. Capacity failure may leave earlier rows queued.
kitaqfc/lib/attribute.c:81
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height)
{
unsigned char qx0;
unsigned char qy0;
unsigned char qx1;
unsigned char qy1;
unsigned char row;
unsigned char col;
// The runtime queue copies these local bytes before return; they are not retained pointers.
// Caller coordinates must keep each computed row length within eight bytes.
unsigned char line[8];
unsigned short ppu_addr;
unsigned char len;
if (width == 0 || height == 0) return 1;
if (tile_x >= 32 || tile_y >= 30) return 0;
if (width > (unsigned char)(32 - tile_x) || height > (unsigned char)(30 - tile_y)) return 0;
qx0 = (unsigned char)(tile_x >> 2);
qy0 = (unsigned char)(tile_y >> 2);
qx1 = (unsigned char)((tile_x + width - 1) >> 2);
qy1 = (unsigned char)((tile_y + height - 1) >> 2);
row = qy0;
while (1)
{
len = (unsigned char)(qx1 - qx0 + 1);
col = 0;
while (col != len)
{
line[col] = nes_attr_shadow[(unsigned char)(row * 8 + qx0 + col)];
col = (unsigned char)(col + 1);
}
ppu_addr = (unsigned short)(nes_attr_base_from_nt(nt_base) + (unsigned short)(row * 8 + qx0));
if (nes_vram_queue_try_write(ppu_addr, line, len) == 0)
{
return 0;
}
if (row == qy1)
{
break;
}
row = (unsigned char)(row + 1);
}
return 1;
}nes_attr_shadow_build_from_palette_map
完成済みの属性64バイトを共有シャドーへコピーします。
書式
void nes_attr_shadow_build_from_palette_map(unsigned char* src64);引数
src64読み取り可能な64バイトのpacked属性表。1タイルごとのパレット番号配列ではありません。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
使用例
for(i=0;i<64;i++)table[i]=0;table[0]=0xE4;nes_attr_shadow_build_from_palette_map(table);result[0]=nes_attr_shadow[0];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
属性の矩形転送を学びます。(16,32)に赤の32×32、(80,32)に青の32×32、画面右下(240,224)に緑の16×16を表示します。隣の区画を壊さず、右下の属性バイトまで正しく転送する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/attribute.h:8
KITAQFC phase15 attribute patch helpers. Copy 64 already-packed attribute bytes into shadow storage. This does not convert one palette ID per tile into NES attribute bitfields. The source must expose all 64 packed bytes and stay readable for the duration of this copy.
kitaqfc/lib/attribute.c:26
void nes_attr_shadow_build_from_palette_map(unsigned char* src64)
{
unsigned char i;
i = 0;
while (i != 64)
{
nes_attr_shadow[i] = (unsigned char)(src64[i] & 0xFF);
i = (unsigned char)(i + 1);
}
}nes_attr_shadow_fill_rect
矩形が触れる2×2タイル区画すべてのパレット番号を変更します。
書式
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index);引数
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
width幅(タイル数)。
height高さ(タイル数)。
pal_indexパレット番号。下位2ビットを使います。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
矩形全体を32×30タイル内に収めます。空または範囲外の矩形では何も変更しません。奇数の境界は区画へ広がるので、矩形の外側の隣接タイルも同じパレットになります。
使用例
nes_attr_shadow_fill_rect(2,4,4,4,1);nes_attr_shadow_fill_rect(10,4,4,4,2);nes_attr_shadow_fill_rect(30,28,2,2,3);result[1]=nes_attr_shadow[8];result[2]=nes_attr_shadow[9];result[3]=nes_attr_shadow[15];result[4]=nes_attr_shadow[63];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
属性の矩形転送を学びます。(16,32)に赤の32×32、(80,32)に青の32×32、画面右下(240,224)に緑の16×16を表示します。隣の区画を壊さず、右下の属性バイトまで正しく転送する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/attribute.h:11
Apply a palette to every two-by-two tile quadrant touched by the rectangle. Empty or out-of-range rectangles leave the shadow unchanged.
kitaqfc/lib/attribute.c:39
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index)
{
unsigned char qx0;
unsigned char qy0;
unsigned char qx1;
unsigned char qy1;
unsigned char qx;
unsigned char qy;
if (width == 0 || height == 0) return;
if (tile_x >= 32 || tile_y >= 30) return;
if (width > (unsigned char)(32 - tile_x) || height > (unsigned char)(30 - tile_y)) return;
qx0 = nes_attr_rect_left(tile_x);
qy0 = nes_attr_rect_top(tile_y);
qx1 = nes_attr_rect_left((unsigned char)(tile_x + width - 1));
qy1 = nes_attr_rect_top((unsigned char)(tile_y + height - 1));
qy = qy0;
while (1)
{
qx = qx0;
while (1)
{
nes_attr_shadow_set_quad((unsigned char)(qx << 1), (unsigned char)(qy << 1), pal_index);
if (qx == qx1)
{
break;
}
qx = (unsigned char)(qx + 1);
}
if (qy == qy1)
{
break;
}
qy = (unsigned char)(qy + 1);
}
}audio.h — 音楽と効果音・音源制御
標準APUの2パルス・三角波・ノイズ・DMCを直接設定します。音楽シーケンサーや自動ミキサーではなく、ゲーム側が発音タイミングと音源の所有権を管理します。
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
DMCは$C000~$FFC0の64バイト境界からROMを読みます。長さは16×n+1バイト(1~4081)です。ヘルパーは境界・範囲を検査せず、バンクも切り替えません。サンプルは固定バンク0に独自データを__aligned(64)で置き、配置を実行時にも検査します。
nes_apu_channel_enable
APUの5チャンネルの有効化状態を、指定マスクで丸ごと置き換えます。
書式
void nes_apu_channel_enable(u8 mask);引数
maskAPU_STATUSへ書く完全な有効化マスク。bit0~4はpulse1、pulse2、triangle、noise、DMCで、上位3ビットは捨てます。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:17
Replace all five channel-enable bits in APU_STATUS; this is not an additive enable.
kitaqfc/lib/audio.c:43
void nes_apu_channel_enable(u8 mask)
{
APU_STATUS = (u8)(mask & 0x1F);
}nes_apu_init
フレームIRQを無効にして4音源を有効化し、無音の制御値を設定します。DMCは無効です。
書式
void nes_apu_init(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:15
Disable frame IRQs, enable the four tonal/noise channels and install quiet control defaults. DMC remains disabled.
kitaqfc/lib/audio.c:28
void nes_apu_init(void)
{
APU_FRAME = 0x40;
APU_STATUS = 0x0F;
SQ1_VOL = 0x10;
SQ1_SWEEP = 0x08;
SQ2_VOL = 0x10;
SQ2_SWEEP = 0x08;
TRI_LINEAR = 0x80;
NOISE_VOL = 0x10;
DMC_FREQ = 0x00;
DMC_RAW = 0x00;
}nes_apu_silence_all
全チャンネルを無効化し、パルス・ノイズの音量とDMC出力を消します。
書式
void nes_apu_silence_all(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:19
Disable every channel and clear volume/DMC output settings used by this helper.
kitaqfc/lib/audio.c:49
void nes_apu_silence_all(void)
{
APU_STATUS = 0x00;
SQ1_VOL = 0x10;
SQ2_VOL = 0x10;
TRI_LINEAR = 0x80;
NOISE_VOL = 0x10;
DMC_FREQ = 0x00;
DMC_RAW = 0x00;
}nes_dmc_config
DMCの制御・出力初期値・開始アドレス・長さをレジスター単位へ変換して設定します。
書式
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);引数
flags_rateDMCのbit7=IRQ、bit6=ループ、bit3~0=レート番号。サンプルではIRQを使いません。
output_levelDMC出力カウンターの初期値0~127。下位7ビットだけを書き込みます。
sample_addrDMCデータ先頭のCPUアドレス。$C000~$FFC0の64バイト境界です。
sample_len16×n+1のバイト数、1~4081。0や未対応の長さは渡さないでください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
DMCは$C000~$FFC0の64バイト境界からROMを読みます。長さは16×n+1バイト(1~4081)です。ヘルパーは境界・範囲を検査せず、バンクも切り替えません。サンプルは固定バンク0に独自データを__aligned(64)で置き、配置を実行時にも検査します。
使用例
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
sound_begin("DMC ORIGINAL DELTAS");
sound_result[0]=(u8)(((u16)delta&63)==0);
sound_result[1]=(u8)((u16)delta>=0xC000);
// Loop at NTSC rate index 10; start the output accumulator at its midpoint.
nes_dmc_config(0x4A,64,(u16)delta,17);
nes_dmc_start();
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
nes_dmc_play(0x4C,64,(u16)delta,17);
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
sound_end();
}期待する結果
独自のデルタ列をレート10、無音区間、より速いレート12の順で再生します。途中のDMC停止後は出力カウンターが保持されるため、録音では過渡成分が残る場合があります。最後はAPU全停止です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:38
Convert a CPU sample address and byte length to DMC register units. Supply an address in 0xC000-0xFFC0 aligned to 64 bytes and a representable length 16*n+1 in 1..4081; inputs are narrowed without validation or bank management.
kitaqfc/lib/audio.c:115
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len)
{
DMC_FREQ = flags_rate;
DMC_RAW = (u8)(output_level & 0x7F);
// Keep the sample bytes mapped at these CPU addresses during playback; this does not select a ROM bank.
DMC_START = (u8)((sample_addr - 0xC000) >> 6);
DMC_LEN = (u8)((sample_len - 1) >> 4);
}nes_dmc_play
サンプルのDMC設定を行い、直後にDMCを開始します。
書式
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);引数
flags_rateDMCのbit7=IRQ、bit6=ループ、bit3~0=レート番号。サンプルではIRQを使いません。
output_levelDMC出力カウンターの初期値0~127。下位7ビットだけを書き込みます。
sample_addrDMCデータ先頭のCPUアドレス。$C000~$FFC0の64バイト境界です。
sample_len16×n+1のバイト数、1~4081。0や未対応の長さは渡さないでください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
DMCは$C000~$FFC0の64バイト境界からROMを読みます。長さは16×n+1バイト(1~4081)です。ヘルパーは境界・範囲を検査せず、バンクも切り替えません。サンプルは固定バンク0に独自データを__aligned(64)で置き、配置を実行時にも検査します。
使用例
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
sound_begin("DMC ORIGINAL DELTAS");
sound_result[0]=(u8)(((u16)delta&63)==0);
sound_result[1]=(u8)((u16)delta>=0xC000);
// Loop at NTSC rate index 10; start the output accumulator at its midpoint.
nes_dmc_config(0x4A,64,(u16)delta,17);
nes_dmc_start();
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
nes_dmc_play(0x4C,64,(u16)delta,17);
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
sound_end();
}期待する結果
独自のデルタ列をレート10、無音区間、より速いレート12の順で再生します。途中のDMC停止後は出力カウンターが保持されるため、録音では過渡成分が残る場合があります。最後はAPU全停止です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:44
Install sample parameters, then start DMC using the full-channel enable mask.
kitaqfc/lib/audio.c:137
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len)
{
nes_dmc_config(flags_rate, output_level, sample_addr, sample_len);
nes_dmc_start();
}nes_dmc_start
APU_STATUS=0x1FとしてDMCとほかの4音源を有効化します。
書式
void nes_dmc_start(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
DMCは$C000~$FFC0の64バイト境界からROMを読みます。長さは16×n+1バイト(1~4081)です。ヘルパーは境界・範囲を検査せず、バンクも切り替えません。サンプルは固定バンク0に独自データを__aligned(64)で置き、配置を実行時にも検査します。
使用例
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
sound_begin("DMC ORIGINAL DELTAS");
sound_result[0]=(u8)(((u16)delta&63)==0);
sound_result[1]=(u8)((u16)delta>=0xC000);
// Loop at NTSC rate index 10; start the output accumulator at its midpoint.
nes_dmc_config(0x4A,64,(u16)delta,17);
nes_dmc_start();
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
nes_dmc_play(0x4C,64,(u16)delta,17);
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
sound_end();
}期待する結果
独自のデルタ列をレート10、無音区間、より速いレート12の順で再生します。途中のDMC停止後は出力カウンターが保持されるため、録音では過渡成分が残る場合があります。最後はAPU全停止です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:40
Enable DMC together with all four other channels by replacing the status mask.
kitaqfc/lib/audio.c:125
void nes_dmc_start(void)
{
APU_STATUS = 0x1F;
}nes_dmc_stop
APU_STATUS=0x0FとしてDMCを止め、ほかの4音源の有効化ビットを立てます。
書式
void nes_dmc_stop(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
DMCは$C000~$FFC0の64バイト境界からROMを読みます。長さは16×n+1バイト(1~4081)です。ヘルパーは境界・範囲を検査せず、バンクも切り替えません。サンプルは固定バンク0に独自データを__aligned(64)で置き、配置を実行時にも検査します。
使用例
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
sound_begin("DMC ORIGINAL DELTAS");
sound_result[0]=(u8)(((u16)delta&63)==0);
sound_result[1]=(u8)((u16)delta>=0xC000);
// Loop at NTSC rate index 10; start the output accumulator at its midpoint.
nes_dmc_config(0x4A,64,(u16)delta,17);
nes_dmc_start();
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
nes_dmc_play(0x4C,64,(u16)delta,17);
sound_wait(60);
nes_dmc_stop(); sound_wait(30);
sound_end();
}期待する結果
独自のデルタ列をレート10、無音区間、より速いレート12の順で再生します。途中のDMC停止後は出力カウンターが保持されるため、録音では過渡成分が残る場合があります。最後はAPU全停止です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:42
Disable DMC while leaving all four other channel-enable bits set.
kitaqfc/lib/audio.c:131
void nes_dmc_stop(void)
{
APU_STATUS = 0x0F;
}nes_sfx_move_blip
パルス1を0x5A・タイマー0x0280・長さ番号2で鳴らし、tickより高く短いmove音を作ります。
書式
void nes_sfx_move_blip(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:33
Trigger the predefined pulse-1 movement effect.
kitaqfc/lib/audio.c:107
void nes_sfx_move_blip(void)
{
nes_sfx_square1(0x5A, 0x0280, 2);
}nes_sfx_noise
ノイズだけを有効にし、エンベロープ・周期モード・長さ番号を書き込みます。
書式
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index);引数
volumeノイズ制御の生値。bit5=長さ停止、bit4=固定音量、bit3~0=音量またはエンベロープ間隔です。
period_modeノイズのbit7=短周期モード、bit3~0=周期テーブル番号。
length_index長さテーブル番号0~31。フレーム数そのものではなく、値の下位5ビットをレジスターへ格納します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:29
Enable only noise and load envelope/control, period/mode and length fields.
kitaqfc/lib/audio.c:92
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index)
{
APU_STATUS = 0x08;
NOISE_VOL = volume;
NOISE_LO = period_mode;
NOISE_HI = (u8)(length_index << 3);
}nes_sfx_square1
パルス1だけを有効にし、スイープを止め、制御値・タイマー・長さ番号を書き込みます。
書式
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index);引数
duty_volumeパルス制御レジスターの生値。bit7~6=デューティ、bit5=長さ停止、bit4=固定音量、bit3~0=音量またはエンベロープ間隔。
period11ビットタイマー値0~2047。パルスはCPU周波数/[16×(period+1)]、三角波はCPU周波数/[32×(period+1)]です。
length_index長さテーブル番号0~31。フレーム数そのものではなく、値の下位5ビットをレジスターへ格納します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:23
Enable only pulse 1, disable its sweep and load control, 11-bit timer and length-table index. Starting this effect disables the other APU channels.
kitaqfc/lib/audio.c:62
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index)
{
APU_STATUS = 0x01;
SQ1_SWEEP = 0x08;
SQ1_VOL = duty_volume;
SQ1_LO = (u8)period;
// Only the low five length-index bits and low eleven timer bits reach the registers.
SQ1_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}nes_sfx_square2
パルス2だけを有効にし、スイープを止め、制御値・タイマー・長さ番号を書き込みます。
書式
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index);引数
duty_volumeパルス制御レジスターの生値。bit7~6=デューティ、bit5=長さ停止、bit4=固定音量、bit3~0=音量またはエンベロープ間隔。
period11ビットタイマー値0~2047。パルスはCPU周波数/[16×(period+1)]、三角波はCPU周波数/[32×(period+1)]です。
length_index長さテーブル番号0~31。フレーム数そのものではなく、値の下位5ビットをレジスターへ格納します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:25
Enable only pulse 2 and load its control/timer/length fields, disabling other channels.
kitaqfc/lib/audio.c:73
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index)
{
APU_STATUS = 0x02;
SQ2_SWEEP = 0x08;
SQ2_VOL = duty_volume;
SQ2_LO = (u8)period;
SQ2_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}nes_sfx_tick_blip
パルス1を0x9A・タイマー0x03F0・長さ番号4で鳴らし、短い低音のtickを作ります。
書式
void nes_sfx_tick_blip(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:31
Trigger the predefined pulse-1 tick effect; it inherits the exclusive-channel behavior.
kitaqfc/lib/audio.c:101
void nes_sfx_tick_blip(void)
{
nes_sfx_square1(0x9A, 0x03F0, 4);
}nes_sfx_triangle
三角波だけを有効にし、線形カウンター・タイマー・長さ番号を書き込みます。
書式
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index);引数
linear三角波の線形カウンター。bit7=制御・長さ停止、下位7ビット=リロード値。
period11ビットタイマー値0~2047。パルスはCPU周波数/[16×(period+1)]、三角波はCPU周波数/[32×(period+1)]です。
length_index長さテーブル番号0~31。フレーム数そのものではなく、値の下位5ビットをレジスターへ格納します。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このヘルパーはAPU_STATUSへ単一チャンネルのマスクを書き、ほかのチャンネルを無効化します。複数音を同時に鳴らすミキサーではありません。periodはパルス・三角波のタイマー値で、小さいほど高い音です。サンプルの周波数と時間はNTSC設定で確認しています。
使用例
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
sound_begin("APU CHANNELS AND BLIPS");
// Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
nes_sfx_square1(0xBC,253,0);
sound_result[0]=(u8)(APU_STATUS&0x1F);
sound_wait(30);
nes_apu_channel_enable(0); sound_wait(15);
nes_sfx_square2(0x7C,253,0);
sound_result[1]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_triangle(0xFF,253,0);
sound_result[2]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_noise(0x3C,5,0);
sound_result[3]=(u8)(APU_STATUS&0x1F);
sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
sound_end();
}期待する結果
pulse1、pulse2、三角波、ノイズ、tick、moveの順です。タイマー253はNTSCでパルス約440Hz、三角波約220Hzになります。tickは約111Hz、moveは約174Hzで、moveの方が短く終わります。最後は無音です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/audio.h:27
Enable only triangle and load linear-counter, timer and length fields.
kitaqfc/lib/audio.c:83
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index)
{
APU_STATUS = 0x04;
TRI_LINEAR = linear;
TRI_LO = (u8)period;
TRI_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}audio_vblank.h — NMIによる4音源の音楽再生
NMIで進む4音源の音楽ライブラリです。初期化→曲の設定と初回補充→NMIを有効にして再生→前景でrefillの順で使います。NMIはRAMキューを読むだけなので、補充を続ければ長い曲も小さなRAM使用量で再生できます。各音源は独立したHOLD/STOPを持ちます。
1レコードは5バイトで、待機時間, CH1, CH2, CH3, CH4の順です。待機時間1~255は、その音を維持するNMI回数です。0は終端で、残り4バイトは無視します。CH1~CH3の音階0~71はC2~B7(24=C4)。CH4の0~15は通常ノイズの周期番号、16~31は短周期モードです。各音源の255=NES_AUDIO_HOLDは変更せず、254=NES_AUDIO_STOPは停止します。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
待機時間が尽きて次のBGMレコードがなければBGMの音を停止し、補充後のNMIで再開します。独立したSFXは進み続けます。補充不足は空の状態へ移った回数として数えます。HOLDだけを補充しても停止前のBGM音は復元しません。通常は毎フレームrefillを呼び、重い処理の前に十分な待機時間をキューへ入れてください。
| 待機時間 | CH1: パルス1 | CH2: パルス2 | CH3: 三角波 | CH4: ノイズ |
|---|---|---|---|---|
| 24 | 24 (C4) | 12 (C3) | 0 (C2) | 2 |
nes_audio_vblank_init
APUとキューを初期化し、音楽・補充状態と補充不足カウンターをリセットします。
書式
void nes_audio_vblank_init(void);戻り値
戻り値はありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
APUフレームIRQとDMCを無効にし、パルスのスイープも無効にします。既定音色はCH1=50%デューティ・音量12、CH2=25%・音量10、三角波=発音、ノイズ=音量8です。初期化だけでは音楽もNMIも開始しません。
使用例
nes_audio_vblank_init();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:19
Reset the queue, feeder and diagnostic counters; silence and initialize the APU. Owns the four basic channels and disables DMC and APU frame IRQs.
kitaqfc/lib/audio_vblank.c:67
void nes_audio_vblank_init(void){
nes_audio_vblank_stop();nav_underruns=0;nav_frame=0x40;
nav_sweep1=8;nav_sweep2=8;nav_dmc_flags=0;nav_dmc_level=0;
nav_timbre[0]=0xBC;nav_timbre[1]=0x7A;nav_timbre[2]=0xFF;nav_timbre[3]=0x38;
}nes_audio_vblank_enqueue
1レコードを検査してRAMキューへコピーします。ROM補充関数を使わず、ゲーム側で音楽を組み立てる場合にも使えます。
書式
u8 nes_audio_vblank_enqueue(const u8* record);引数
recordコピーする5バイトの先頭。コピーが返るまで読める領域を指定します。返った後は元の配列を再利用できます。0は拒否します。
戻り値
受け入れたら1。満杯、null、不正な音階・ノイズ値なら0で、キューは変更しません。終端レコードは音源の4バイトを検査しません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
RAMの40バイトに8スロットを用意し、満杯と空を区別するため1スロットを空けて使います。実際に予約できるのは7レコードです。生産側は前景処理1か所とし、NMIからenqueue/refillや初期化を呼ばないでください。コピー完了後に1バイトの書き込み位置を公開するため、途中のレコードをNMIが読むことはありません。
1レコードは5バイトで、待機時間, CH1, CH2, CH3, CH4の順です。待機時間1~255は、その音を維持するNMI回数です。0は終端で、残り4バイトは無視します。CH1~CH3の音階0~71はC2~B7(24=C4)。CH4の0~15は通常ノイズの周期番号、16~31は短周期モードです。各音源の255=NES_AUDIO_HOLDは変更せず、254=NES_AUDIO_STOPは停止します。
使用例
result[6]=nes_audio_vblank_enqueue(quiet_record); // Copy five bytes into RAM.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:22
Copy one complete validated record to RAM and publish it atomically last. Return 1 on success, 0 for null/invalid/full. Call from one foreground producer.
kitaqfc/lib/audio_vblank.c:74
u8 nes_audio_vblank_enqueue(const u8* record){
u8 next;u8 i;u8 value;
nav_invalid=0;
if(record==0){nav_invalid=1;return 0;}
next=nav_head+5;if(next==40)next=0;
if(next==nav_tail)return 0;
if(record[0]!=0){
for(i=1;i<5;i++){
value=record[i];
if(value<254){
if(i<4){if(value>71){nav_invalid=1;return 0;}}
else{if(value>31){nav_invalid=1;return 0;}}
}
}
}
for(i=0;i<5;i++)nav_queue[(__safe_index u8)(nav_head+i)]=record[i];
nav_head=next;return 1;
}nes_audio_vblank_queued
まだNMIに取り出されていないレコード数を返します。
書式
u8 nes_audio_vblank_queued(void);戻り値
BGMの0~7レコードです。現在の待機時間とSFXレコードは数えません。NMIが消費を進めるため、読み取り時点の値です。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
RAMの40バイトに8スロットを用意し、満杯と空を区別するため1スロットを空けて使います。実際に予約できるのは7レコードです。生産側は前景処理1か所とし、NMIからenqueue/refillや初期化を呼ばないでください。コピー完了後に1バイトの書き込み位置を公開するため、途中のレコードをNMIが読むことはありません。
使用例
result[1]=nes_audio_vblank_queued(); // Seven records after prefill.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:24
Return queued records (0..7), excluding the record whose delay is in progress.
kitaqfc/lib/audio_vblank.c:94
u8 nes_audio_vblank_queued(void){
u8 head;u8 tail;head=nav_head;tail=nav_tail;
if(head<tail)head=head+40;
return (head-tail)/5;
}nes_audio_vblank_free
キューへ追加できるレコードの空きスロット数を返します。
書式
u8 nes_audio_vblank_free(void);戻り値
BGMキューの0~7スロットで、7−queued()です。バイト数ではなく、1スロットに5バイトを格納します。SFXの35バイト領域はこのキューと別です。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
RAMの40バイトに8スロットを用意し、満杯と空を区別するため1スロットを空けて使います。実際に予約できるのは7レコードです。生産側は前景処理1か所とし、NMIからenqueue/refillや初期化を呼ばないでください。コピー完了後に1バイトの書き込み位置を公開するため、途中のレコードをNMIが読むことはありません。
使用例
result[2]=nes_audio_vblank_free(); // No free slots immediately after prefill.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:26
Return available record slots (0..7), not bytes.
kitaqfc/lib/audio_vblank.c:99
u8 nes_audio_vblank_free(void){return 7-nes_audio_vblank_queued();}nes_audio_vblank_start
RAMキューの消費を有効にします。最初のレコードは次のNMIで取り出します。
書式
void nes_audio_vblank_start(void);戻り値
戻り値はありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
初期化と補充を先に行ってください。NMI自体は有効にせず、現在の待機時間や読み取り位置も変更しません。一時停止の解除にはpause(0)を使います。startだけでは一時停止を解除しません。
待機時間が尽きて次のBGMレコードがなければBGMの音を停止し、補充後のNMIで再開します。独立したSFXは進み続けます。補充不足は空の状態へ移った回数として数えます。HOLDだけを補充しても停止前のBGM音は復元しません。通常は毎フレームrefillを呼び、重い処理の前に十分な待機時間をキューへ入れてください。
使用例
nes_audio_vblank_start(); // Consume the copied record on the next NMI.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:28
Enable consumption at the next NMI. Does not enable PPU NMI itself.
kitaqfc/lib/audio_vblank.c:100
void nes_audio_vblank_start(void){nav_active=1;}nes_audio_vblank_stop
再生を無効にして4音源を消音し、キューとROM補充状態を破棄します。
書式
void nes_audio_vblank_stop(void);戻り値
戻り値はありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
BGMとSFXをともに停止し、キュー、補充元、両方の再生位置、一時停止状態を破棄します。音色と補充不足カウンターは保持し、NMIは無効にしません。位置を保って休止するにはpauseを使います。set_musicとplay_musicも内部でこの停止を行うため、SFXを取り消します。
使用例
nes_audio_vblank_stop(); // Clear the feeder and queued records; silence now.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:30
Disable consumption, silence, discard queued records and clear the feeder.
kitaqfc/lib/audio_vblank.c:29
void nes_audio_vblank_stop(void){
u8 i;
nav_paused=1;nav_active=0;nav_sfx_active=0;nav_status=0;
nav_delay=0;nav_enabled=0;nav_head=0;nav_tail=0;
nav_source=0;nav_cursor=0;nav_count=0;nav_left=0;nav_starved=0;
nav_previous_mask=0;nav_effective_mask=0;nav_refresh=0;
for(i=0;i<4;i++){nav_notes[(__safe_index u8)i]=254;nav_sfx_notes[(__safe_index u8)i]=254;}
nav_paused=0;
}nes_audio_vblank_set_music
再生を停止して音楽ストリームを補充元に設定します。キューへのコピーと再生開始は別操作です。
書式
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop);引数
records音楽データの先頭。読み取り可能なROMまたはRAMを渡し、補充が終わるまで保持します。バンク切り替えは行わないので、refillを呼ぶ間は該当ROMバンクを読めるようにしてください。NMIはコピー済みのRAMだけを読みます。
count読み取れる5バイトレコードの個数、1~13107です。バイト数ではありません。count×5バイトが有効なCPUアドレス範囲に連続して存在する必要があります。
loop0は1回再生、0以外は先頭から反復します。反復するデータに待機時間0の終端レコードを入れないでください。
戻り値
ポインターと個数が有効なら1、null・0個・13107超なら0です。失敗時もそれまでの再生とキューは停止・破棄します。各レコードの内容は補充時に検査します。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
1レコードは5バイトで、待機時間, CH1, CH2, CH3, CH4の順です。待機時間1~255は、その音を維持するNMI回数です。0は終端で、残り4バイトは無視します。CH1~CH3の音階0~71はC2~B7(24=C4)。CH4の0~15は通常ノイズの周期番号、16~31は短周期モードです。各音源の255=NES_AUDIO_HOLDは変更せず、254=NES_AUDIO_STOPは停止します。
使用例
result[5]=nes_audio_vblank_set_music(song,8,0); // Attach without starting.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:33
Stop and attach a readable five-byte-record stream; return 0 for null, zero count or a count above 13107. Nonzero loop repeats the stream. Does not start.
kitaqfc/lib/audio_vblank.c:110
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop){
nes_audio_vblank_stop();
if(records==0||count==0||count>13107)return 0;
nav_source=records;nav_cursor=records;nav_count=count;nav_left=count;nav_loop=loop;
return 1;
}nes_audio_vblank_refill
設定したストリームから、空きスロットへ最大7レコードを補充します。長い曲を小さなRAMキューで再生するための前景処理です。
書式
u8 nes_audio_vblank_refill(void);戻り値
受け入れた0~7レコードの個数です。1回再生では最後に自動追加する終端も1個に数えます。補充元がない場合や満杯なら0です。不正なレコードに到達した場合は再生とキューを停止・破棄して0を返します。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
音楽データの先頭。読み取り可能なROMまたはRAMを渡し、補充が終わるまで保持します。バンク切り替えは行わないので、refillを呼ぶ間は該当ROMバンクを読めるようにしてください。NMIはコピー済みのRAMだけを読みます。
0は1回再生、0以外は先頭から反復します。反復するデータに待機時間0の終端レコードを入れないでください。
待機時間が尽きて次のBGMレコードがなければBGMの音を停止し、補充後のNMIで再開します。独立したSFXは進み続けます。補充不足は空の状態へ移った回数として数えます。HOLDだけを補充しても停止前のBGM音は復元しません。通常は毎フレームrefillを呼び、重い処理の前に十分な待機時間をキューへ入れてください。
ROM補充と直接enqueueは同じキューを使います。意図しない順番で混ぜないでください。refillは音源レジスターを書かず、再生開始もしません。ただし不正データを検出して停止するときは消音します。
使用例
nes_audio_vblank_refill();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:37
Copy at most seven records from the attached stream. Return copies accepted. A one-shot feeder appends a delay-zero terminator after the last timed record. Keep the stream's ROM bank mapped during this foreground call only.
kitaqfc/lib/audio_vblank.c:118
u8 nes_audio_vblank_refill(void){
u8 copied;copied=0;
while(copied<7&&nav_source!=0){
if(nav_left==0){
if(nav_loop!=0){nav_cursor=nav_source;nav_left=nav_count;}
else {if(nes_audio_vblank_enqueue(nav_end)!=0){copied++;nav_source=0;}return copied;}
}
if(nes_audio_vblank_enqueue(nav_cursor)==0){
// The producer-only flag distinguishes malformed data from full.
// Re-reading occupancy here would race NMI freeing a full queue.
if(nav_invalid!=0){nes_audio_vblank_stop();return 0;}
return copied;
}
copied++;nav_cursor=nav_cursor+5;nav_left--;
}
return copied;
}nes_audio_vblank_play_music
音楽の設定、最初の補充、再生有効化をまとめて行います。短い曲を始める基本の入口です。
書式
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop);引数
records音楽データの先頭。読み取り可能なROMまたはRAMを渡し、補充が終わるまで保持します。バンク切り替えは行わないので、refillを呼ぶ間は該当ROMバンクを読めるようにしてください。NMIはコピー済みのRAMだけを読みます。
count読み取れる5バイトレコードの個数、1~13107です。バイト数ではありません。count×5バイトが有効なCPUアドレス範囲に連続して存在する必要があります。
loop0は1回再生、0以外は先頭から反復します。反復するデータに待機時間0の終端レコードを入れないでください。
戻り値
設定と最初の補充が成功したら1、設定不正または初回補充で不正データを見つけたら0です。曲全体の妥当性を事前保証する値ではありません。後続の不正レコードは後のrefillで検出します。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
最初にinitを1回呼びます。play_musicは音色と補充不足カウンターを保持します。長い曲や反復する曲では、その後も定期的にrefillしてください。
使用例
result[0]=nes_audio_vblank_play_music(song,8,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:39
Set the stream, prefill the queue and start. Return 0 for invalid setup/data.
kitaqfc/lib/audio_vblank.c:135
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop){
if(nes_audio_vblank_set_music(records,count,loop)==0)return 0;
if(nes_audio_vblank_refill()==0)return 0;
nes_audio_vblank_start();return 1;
}nes_audio_vblank_is_playing
NMIによるキュー消費が有効かを返します。
書式
u8 nes_audio_vblank_is_playing(void);戻り値
BGM消費が有効なら1、停止・終端後は0です。一時停止中や補充不足中も1のままです。SFXだけが鳴っている場合は0です。この値は可聴音やSFXの再生中判定ではありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
待機時間が尽きて次のBGMレコードがなければBGMの音を停止し、補充後のNMIで再開します。独立したSFXは進み続けます。補充不足は空の状態へ移った回数として数えます。HOLDだけを補充しても停止前のBGM音は復元しません。通常は毎フレームrefillを呼び、重い処理の前に十分な待機時間をキューへ入れてください。
使用例
while(nes_audio_vblank_is_playing()!=0){
// example:nes_audio_vblank_refill:start
nes_audio_vblank_refill();
// example:nes_audio_vblank_refill:end
__nmi_wait();
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:41
Return 1 while the BGM consumer is enabled, including a recoverable queue underrun.
kitaqfc/lib/audio_vblank.c:101
u8 nes_audio_vblank_is_playing(void){return nav_active;}nes_audio_vblank_underruns
音楽の次のレコードが間に合わなかった回数を返します。補充の余裕を確認するために使います。
書式
u8 nes_audio_vblank_underruns(void);戻り値
0~255で、255に達すると増えません。空のまま待ったフレーム数ではなく、補充不足の発生回数です。initで0に戻り、stopや曲変更では保持します。正常な終端は数えません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
待機時間が尽きて次のBGMレコードがなければBGMの音を停止し、補充後のNMIで再開します。独立したSFXは進み続けます。補充不足は空の状態へ移った回数として数えます。HOLDだけを補充しても停止前のBGM音は復元しません。通常は毎フレームrefillを呼び、重い処理の前に十分な待機時間をキューへ入れてください。
使用例
result[4]=nes_audio_vblank_underruns(); // Zero: queued time covered the busy section.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは独自の4音源フレーズを1回再生します。前景処理が90フレーム補充を止めて整数計算をしている間も音楽が進みます。画面の「PHRASE COMPLETE / SILENT」「UNDERRUNS 0 / MATH OK」は、終了・補充不足0・計算結果一致を表します。添付の音声でパルスの旋律と伴奏、三角波の低音、ノイズを確認できます。終了後は無音です。画像は制御結果の表示であり、音の確認には音声を使ってください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:43
Saturating count of starvation episodes, cleared by init; END is not starvation.
kitaqfc/lib/audio_vblank.c:102
u8 nes_audio_vblank_underruns(void){return nav_underruns;}nes_audio_vblank_set_timbre
指定した音源で、次に発音する音のデューティや音量を設定します。再生中の音は再トリガーしません。
書式
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control);引数
channel0=CH1パルス1、1=CH2パルス2、2=CH3三角波、3=CH4ノイズです。4以上は拒否します。
controlパルスは上位2ビットがデューティ(0=12.5%、1=25%、2=50%、3=75%)、下位4ビットが固定音量0~15です。ノイズは通常、下位4ビットが固定音量です。NES_AUDIO_NOISE_ENVELOPE(128)をORすると、下位4ビットは減衰周期0~15になり、次のノイズ音は音量15から一度だけ減衰します。周期が大きいほど遅く、15では約1秒です。ノイズの長さカウンターは減衰が完了する長さに設定します。三角波は0で消音設定、0以外で発音設定です。
戻り値
有効なチャンネルなら1、4以上なら変更せず0です。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
使用例
nes_audio_vblank_set_timbre(3,NES_AUDIO_NOISE_ENVELOPE|15);
result[3]=nes_audio_vblank_play_sfx(noise,1,8); // Bit 3 owns CH4; no BGM needed.
// The slow one-shot envelope fades by itself. It does not loop at zero.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは操作不要です。CH1の約220HzのBGMに約880HzのSFXを30フレーム重ね、現在のBGMへ戻ります。30フレームの無音の休止後にBGMが再開します。次のSFXは15フレームで明示的に打ち切ります。その後、CH4のノイズが一度だけゆっくり減衰します。画面のCOMPLETE / ALL CHANNELS OFFは全工程の終了を示します。添付音声で高低の切り替え、休止区間、ノイズの減衰を確認してください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:50
Configure the next note: pulse bits 7..6 select duty and 3..0 select volume; noise uses volume 3..0, or NES_AUDIO_NOISE_ENVELOPE | decay_period (0..15) for a one-shot hardware envelope (larger periods decay more slowly). The next noise note starts at volume 15; its length permits a full decay. Triangle uses zero for silent, nonzero for full level. Return 0 for channel >=4. Hardware has no triangle volume control.
kitaqfc/lib/audio_vblank.c:103
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control){
if(channel>=4)return 0;
if(channel<2)nav_timbre[(__safe_index u8)channel]=(control&0xCF)|0x30;
else if(channel==2){if(control==0)nav_timbre[2]=0x80;else nav_timbre[2]=0xFF;}
else {if((control&128)!=0)nav_timbre[3]=control&15;else nav_timbre[3]=(control&15)|0x30;}
return 1;
}nes_audio_vblank_pause
BGMとSFXの再生位置を保ったまま両方を休止・再開します。
書式
void nes_audio_vblank_pause(u8 on);引数
on0以外で休止、0で再開です。休止はAPUの4音源を直ちに無効にします。
戻り値
戻り値はありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
前景処理から呼びます。NMIはコピー済みのSFXデータを使います。別のAPUドライバーを並行して動かさず、BGMとSFXをこのライブラリで管理してください。
休止中はBGMの待機時間・キュー読取位置とSFXの待機時間・読取位置が進みません。refillによる補充は可能です。再開後の最初のNMIで現在の音を再トリガーして通常の刻みを進めます。波形位相や減衰エンベロープの途中の音量は保存しません。is_playingのBGM有効フラグも変更しません。0は休止中でなくても音の復元を予約するため、毎フレーム呼ぶ用途には使わないでください。
使用例
nes_audio_vblank_pause(1); // Freeze BGM and SFX, and mute immediately.
phase=3;wait_frames(30); // Refill may copy data; neither timeline advances.
nes_audio_vblank_pause(0); // Restore held notes on the next NMI.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは操作不要です。CH1の約220HzのBGMに約880HzのSFXを30フレーム重ね、現在のBGMへ戻ります。30フレームの無音の休止後にBGMが再開します。次のSFXは15フレームで明示的に打ち切ります。その後、CH4のノイズが一度だけゆっくり減衰します。画面のCOMPLETE / ALL CHANNELS OFFは全工程の終了を示します。添付音声で高低の切り替え、休止区間、ノイズの減衰を確認してください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:54
Any nonzero argument freezes both timelines and mutes immediately. Zero resumes from the stored delays and restores held notes on the next NMI. BGM is_playing remains enabled during pause. Refill may still fill the queue.
kitaqfc/lib/audio_vblank.c:40
void nes_audio_vblank_pause(u8 on){
if(on!=0){nav_paused=1;nav_status=0;}
else{nav_refresh=1;nav_paused=0;}
}nes_audio_vblank_play_sfx
短い効果音をRAMへコピーし、選んだ音源の出力を次のNMIから一時的に置き換えます。
書式
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask);引数
records5バイトレコード列の先頭。戻るまでcount×5バイトを読み取れるように保持・マッピングします。コピー後は元の配列を再利用できます。
count1~7レコード(最大35バイト)。各待機時間は1~255です。SFXでは待機時間0の終端を使わず、指定個数を再生すると終了します。
mask下位4ビットが対象音源です。1=CH1、2=CH2、4=CH3、8=CH4。ORで複数選べます。上位4ビットは無視します。下位が全て0なら拒否します。
戻り値
コピーして有効にしたら1。null、範囲外の個数、対象音源なし、待機時間0、不正な音階・ノイズ値なら0です。不正入力では現在のSFXを変更しません。対象外の音源の値も検査します。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
前景処理から呼びます。NMIはコピー済みのSFXデータを使います。別のAPUドライバーを並行して動かさず、BGMとSFXをこのライブラリで管理してください。
BGMは背後で進み続け、効果音が終わるとその時点のBGM音へ戻ります。SFXは1本で、新しい呼び出しは前の効果音を置き換えます。HOLD/STOPの値はBGMと共通ですが、SFX開始時の保持音は停止状態なので、最初のHOLDはBGMの音を引き継ぎません。BGMが無効でもSFXは再生できます。一時停止中に予約したSFXは再開まで進みません。音色はBGMと共用です。
使用例
result[1]=nes_audio_vblank_play_sfx(effect,1,1); // Mask bit 0 owns CH1.
// The BGM timeline keeps advancing; its current CH1 note returns after 30 ticks.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは操作不要です。CH1の約220HzのBGMに約880HzのSFXを30フレーム重ね、現在のBGMへ戻ります。30フレームの無音の休止後にBGMが再開します。次のSFXは15フレームで明示的に打ち切ります。その後、CH4のノイズが一度だけゆっくり減衰します。画面のCOMPLETE / ALL CHANNELS OFFは全工程の終了を示します。添付音声で高低の切り替え、休止区間、ノイズの減衰を確認してください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:62
Copy 1..7 timed records. Bits 0..3 select channels temporarily owned by SFX. BGM continues underneath and its current notes return when the effect ends. Return 1 after copying; 0 for null, invalid count/data or an empty low mask. Ignore mask bits 4..7. All fields are validated, even unselected channels. Delay zero is not accepted for SFX. Invalid input preserves the current SFX. Replacement restarts the effect; the first HOLD starts from a stopped note. The source need only stay mapped/readable until this foreground call returns.
kitaqfc/lib/audio_vblank.c:47
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask){
u8 i;u8 ch;u8 value;u8 bytes;
if(records==0||count==0||count>7||(mask&15)==0)return 0;
bytes=(u8)(count+count+count+count+count);
i=0;
while(i<bytes){
if(records[i]==0)return 0;
ch=1;while(ch<5){
value=records[i+ch];
if(value<254){if(ch<4){if(value>71)return 0;}else{if(value>31)return 0;}}
ch++;
}
i=i+5;
}
nav_sfx_active=0;
for(i=0;i<bytes;i++)nav_sfx_queue[(__safe_index u8)i]=records[i];
for(i=0;i<4;i++)nav_sfx_notes[(__safe_index u8)i]=254;
nav_sfx_mask=mask&15;nav_sfx_cursor=0;nav_sfx_length=bytes;nav_sfx_delay=0;
nav_sfx_active=1;return 1;
}nes_audio_vblank_stop_sfx
効果音による音源の占有を取り消し、次のNMIで現在のBGM音へ戻します。
書式
void nes_audio_vblank_stop_sfx(void);戻り値
戻り値はありません。
使用条件と制限
audio_vblank.hを読み込み、audio_vblank.cを1回だけビルドへ加えます。コンパイラは、このライブラリのフックを既定または独自のNMIハンドラーから1回ずつ呼びます。PPUのNMIを有効にしてください。音楽の更新を前景ループから呼ぶ必要はありません。
NTSC向けの4音源ドライバーです。CH1=パルス1、CH2=パルス2、CH3=三角波、CH4=ノイズで、チャンネル添字は0~3です。DMCと拡張音源は再生しません。audio.cの効果音関数など、同じAPUを直接書き換える処理とは同時に使わないでください。PAL用の音程表やテンポ補正はありません。
前景処理から呼びます。NMIはコピー済みのSFXデータを使います。別のAPUドライバーを並行して動かさず、BGMとSFXをこのライブラリで管理してください。
APUレジスターをこの呼び出しでは書き換えません。BGMのキュー・位置・有効状態は保ちます。SFXだけを鳴らしていた場合は次のNMIで停止状態へ戻ります。一時停止中は再開後に反映されます。音を直ちに止めて両方の位置を破棄するにはstopを使います。
使用例
nes_audio_vblank_stop_sfx(); // Release effect ownership at the next NMI.
// BGM continues at its current position; this does not restart the song.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルは操作不要です。CH1の約220HzのBGMに約880HzのSFXを30フレーム重ね、現在のBGMへ戻ります。30フレームの無音の休止後にBGMが再開します。次のSFXは15フレームで明示的に打ち切ります。その後、CH4のノイズが一度だけゆっくり減衰します。画面のCOMPLETE / ALL CHANNELS OFFは全工程の終了を示します。添付音声で高低の切り替え、休止区間、ノイズの減衰を確認してください。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/audio_vblank.h:65
Release effect ownership at the next NMI, restoring current BGM notes. This does not stop or restart the BGM and does not write the APU immediately.
kitaqfc/lib/audio_vblank.c:44
void nes_audio_vblank_stop_sfx(void){nav_sfx_active=0;}bank.h — バンク番号付きのコード・データアクセス
ROMバンクは、カートリッジ内の記憶領域をCPUのアドレス空間へ割り当てる単位です。同じCPUアドレスに別のバンクを表示できるため、16ビットのポインターだけではバンク付きデータを特定できません。BankPtrは、1バイトのバンク番号と2バイトのポインターを詰めた3バイトの記述子です。farptr_makeで作成し、farptr_read8、farptr_read16、farptr_readで内容にアクセスします。
FCの例はMMC3の論理バンクを使用します。バンク0の共通コードを$C000〜$FFFFに置き、選んだ16 KiBのバンクを$8000〜$BFFFに表示します。この番号はコンパイラのバンク番号であり、マッパーの8 KiB単位のレジスター値とは異なります。bank_switchでライブラリの記録を初期化してください。FCのfar_callは名前付き関数呼び出しのマクロです。関数のバンクを渡し、番号の取得には__bankofを使うと明確です。
bank_get_current
bank_switchが最後に記録したバンク番号を返します。カートリッジのレジスターではなく、ソフトウェア側の記録を読みます。
書式
u8 bank_get_current(void);戻り値
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:18
Read this wrapper's bank shadow; arbitrary mapper writes do not update it.
kitaqfc/lib/bank.c:14
u8 bank_get_current(void)
{
return kq_bank_current;
}bank_switch
要求したバンク番号をライブラリに記録し、コンパイラのマッパー別PRGバンク切り替え処理で選択します。
書式
void bank_switch(u8 bank);引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
戻り値
ありません(void)。
使用条件と制限
切り替えを維持する操作では、実行中のコードや戻り先を見えなくしてはいけません。サンプルのように、この呼び出し、使用するbank_switch本体、その後に続くコードを共通・固定バンク0に置いてください。
ライブラリの記録は0で始まり、カートリッジの起動時のマッピングを調べるものではありません。bank_switchで初期化してください。組み込み関数による直接切り替えやマッパーへの直接書き込みではこの記録を更新しないため、bank_get_currentで実際のハードウェア状態を検証することはできません。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:16
Record the bank in the library shadow and invoke the mapper-aware compiler intrinsic.
kitaqfc/lib/bank.c:7
void bank_switch(u8 bank)
{
kq_bank_current = bank;
__bankswitch(bank);
}far_call
__farcall((bank), func)へ展開するマクロです。コンパイラが関数名として処理できる形で対象を渡します。コールバックポインターによる間接呼び出しではありません。
書式
#define far_call(bank, func) __farcall((bank), func)引数
bank符号なし8ビットのROMバンク番号です。呼び出しでは関数の配置と一致させ、読み出しでは転送元のマッピングを指定します。対象カートリッジで有効な範囲を使用してください。
func宣言済みの、引数のない関数名です。この名前指定の組み込み呼び出しには、関数アドレスを格納した変数を渡せません。bank引数をコールバックの引数として渡すこともありません。
戻り値
戻り値の型は呼び出した関数に従います。voidのコールバックは値を返さず、u8・u16のコールバックはその値を返します。掲載サンプルはvoidのコールバックを使います。
使用条件と制限
これらの呼び出しは、ユーザー引数を渡したり、コールバックを割り込みに対して再入可能にしたりするものではありません。コールバック自身のデータ・スタック・割り込みの使い方を適切に管理してください。バンク復帰は入れ子呼び出しと反復呼び出しで検証していますが、この例で実機や任意の割り込みタイミングを検証したわけではありません。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:36
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か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:24
Copy len bytes from banked ROM to caller-owned writable storage.
kitaqfc/lib/bank.c:32
void far_data_read(u8 bank, const void* addr, void* dst, u16 len)
{
__far_memcpy((u8*)dst, bank, (u16)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か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:22
Read a 16-bit value through the bank-aware compiler intrinsic.
kitaqfc/lib/bank.c:26
u16 far_data_read16(u8 bank, const void* addr)
{
return __farpeek16(bank, (u16)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か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:20
Read one byte from an explicit ROM bank and 16-bit CPU address.
kitaqfc/lib/bank.c:20
u8 far_data_read8(u8 bank, const void* addr)
{
return __farpeek8(bank, (u16)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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:26
Store a bank/address pair without reading memory; null output is ignored.
kitaqfc/lib/bank.c:38
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か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:32
Copy len bytes from a far pointer; the caller provides a sufficiently large destination.
kitaqfc/lib/bank.c:58
void farptr_read(BankPtr ptr, void* dst, u16 len)
{
__far_memcpy((u8*)dst, ptr.bank, (u16)ptr.ptr, len);
}farptr_read16
BankPtrが保持するバンクとアドレスから、リトルエンディアンのワードを読みます。記述子は値渡しで、ポインターは進めません。
書式
u16 farptr_read16(BankPtr ptr);引数
ptrbankとptrを持つBankPtrの値です。アドレスではなく記述子そのものを渡します。呼び出し側の記述子は変更しません。
戻り値
読み出したリトルエンディアンのワードをu16(0〜65535)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:30
Read a 16-bit value using both components of the far pointer.
kitaqfc/lib/bank.c:52
u16 farptr_read16(BankPtr ptr)
{
return __farpeek16(ptr.bank, (u16)ptr.ptr);
}farptr_read8
値渡しされたBankPtrの両フィールドを使って1バイト読みます。通常の近ポインターには含まれないバンク番号を記述子から取得します。
書式
u8 farptr_read8(BankPtr ptr);引数
ptrbankとptrを持つBankPtrの値です。アドレスではなく記述子そのものを渡します。呼び出し側の記述子は変更しません。
戻り値
読み出したバイトをu8(0〜255)で返します。
使用条件と制限
対象のバイトまたはワード全体を、選択したCPUマッピングで読める必要があります。ポインターの検査、アドレスの切り詰め、ウィンドウ境界での次バンクへの自動切り替えは行いません。ワード読み出しには連続する有効な2バイトが必要です。
遠方読み出しとコピーは同期処理で、コンパイラの共有作業領域を使います。処理中に割り込みから別のバンク転送を実行し、同じ作業領域を上書きしないでください。GBのコピー補助処理は、復帰バンクの保存にもHRAMの共有領域を1か所使います。例では競合する転送割り込みを設けず、固定コードから実行します。
FCの切り替え番号はコンパイラの論理バンク番号です。0は共通ROMを示し、切り替え先ではありません。補助処理は0をバンク1として扱います。SUROM512の有効範囲は1〜30で、切り替え組み込み関数へ直接渡す範囲外の定数はビルドエラーです。範囲外の実行時値では1を選び、マッパー異常状態を設定します。共通ROMは、バンク0への切り替えを要求せず直接読んでください。
使用例
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は02です。far_callマクロと__farcallがそれぞれ1回コールバックを実行します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/bank.h:28
Read one byte using both the bank and address of the supplied far pointer.
kitaqfc/lib/bank.c:46
u8 farptr_read8(BankPtr ptr)
{
return __farpeek8(ptr.bank, (u16)ptr.ptr);
}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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_wrap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_wrap.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:39
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.
kitaqfc/lib/chain.c:103
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 (((u16)delta & 0x8000) != 0) {
if ((u16)(0 - delta) > (u16)half) delta = (s16)(delta + (s16)size);
} else if ((u16)delta > (u16)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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:57
Attach three non-overlapping capacity-byte arrays. Return 1 on success. Invalid storage, zero capacity or dimensions outside 1..256 disable the body.
kitaqfc/lib/chain.c:127
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:61
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.
kitaqfc/lib/chain.c:143
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading)
{
// Cache caller storage once; following math and old-heading propagation
// use these local values. Avoid decoding packed struct pointers per joint.
u8* coord_x;u8* coord_y;u8* directions;u16 width;u16 height;
u8 i;
u8 x;
u8 y;
s16 ox;
s16 oy;
if (body == 0) return 0;
if (count == 0 || count > body->capacity) return 0;
coord_x=body->x;coord_y=body->y;directions=body->heading;
width=body->width;height=body->height;
heading = (u8)(heading & 15);
x = chain_body_wrap((s16)head_x, width);
y = chain_body_wrap((s16)head_y, height);
ox = chain_body_offset_x(heading);
oy = chain_body_offset_y(heading);
i = 0;
while (i < count) {
coord_x[(__safe_index u8)i] = x;
coord_y[(__safe_index u8)i] = y;
directions[(__safe_index u8)i] = heading;
x = chain_body_wrap((s16)((s16)x - ox), width);
y = chain_body_wrap((s16)((s16)y - oy), 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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:66
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.
kitaqfc/lib/chain.c:175
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading)
{
// Cache caller storage once; following math and old-heading propagation
// use these local values. Avoid decoding packed struct pointers per joint.
u8* coord_x;u8* coord_y;u8* directions;u16 width;u16 height;
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;
coord_x=body->x;coord_y=body->y;directions=body->heading;
width=body->width;height=body->height;
heading = (u8)(heading & 15);
coord_x[0] = chain_body_wrap((s16)head_x, width);
coord_y[0] = chain_body_wrap((s16)head_y, height);
directions[0] = heading;
pull = heading;
i = 1;
while (i < body->count) {
previous = (u8)(i - 1);
old = directions[(__safe_index u8)i];
x = coord_x[(__safe_index u8)i];
y = coord_y[(__safe_index u8)i];
tx = chain_body_wrap((s16)((s16)coord_x[(__safe_index u8)previous] - chain_body_offset_x(pull)), width);
ty = chain_body_wrap((s16)((s16)coord_y[(__safe_index u8)previous] - chain_body_offset_y(pull)), height);
sx = chain_body_correction(chain_wrap_delta(tx, x, width));
sy = chain_body_correction(chain_wrap_delta(ty, y, height));
directions[(__safe_index u8)i] = pull;
coord_x[(__safe_index u8)i] = chain_body_wrap((s16)((s16)x + sx), width);
coord_y[(__safe_index u8)i] = chain_body_wrap((s16)((s16)y + sy), 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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:69
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.
kitaqfc/lib/chain.c:218
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/chain.h:71
Remove all joints while retaining caller-owned storage and playfield settings.
kitaqfc/lib/chain.c:233
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以下にしてください。
FC版は chain == 0 なら何もしません。
使用例
chain_clear(&trail);result[11]=chain_get_count(&trail);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4点を保持している履歴を消去し、点数が0になることを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/chain.h:24
Forget the recorded points without clearing or freeing the backing storage.
kitaqfc/lib/chain.c:16
void chain_clear(Chain* chain)
{
if (chain == 0) return;
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/chain.h:33
Return the number of stored points, or zero for a null chain.
kitaqfc/lib/chain.c:66
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/chain.h:31
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.
kitaqfc/lib/chain.c:42
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out)
{
u8 pos;
// Do not alias out with the backing ring: it is cleared before the requested point is read.
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[(__safe_index u8)pos].x;
out->y = chain->points[(__safe_index u8)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以下にしてください。
FC版は chain == 0 なら何もしません。
使用例
chain_init(&trail,storage,4);result[0]=chain_get_count(&trail);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4点分の保存領域を登録し、点数が0から始まることを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/chain.h:22
Attach caller-owned ring storage and start with no recorded points. The storage must remain valid for the lifetime of the chain.
kitaqfc/lib/chain.c:5
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity)
{
if (chain == 0) return;
// A nonzero capacity requires writable storage for that many complete ChainPoint records.
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/chain.h:27
Move the ring head backward and store the newest point. Once full, each push replaces the oldest point; zero capacity is a no-op.
kitaqfc/lib/chain.c:25
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 = (u8)(chain->head - 1);
chain->points[(__safe_index u8)chain->head].x = x;
chain->points[(__safe_index u8)chain->head].y = y;
if (chain->count < chain->capacity) chain->count = (u8)(chain->count + 1);
}collision.h — 矩形等の接触判定
FCのワールド座標用に、符号なし16ビット座標の矩形同士・矩形と点の重なりを調べます。
nes_box16_contains_point
FC用の矩形に点が含まれるかを調べます。左・上辺を含み、右・下辺を除きます。
書式
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py);引数
box読み取り可能な
NesBox16。ヌルは渡しません。px点の符号なし16ビットX座標。
py点の符号なし16ビットY座標。
戻り値
内部なら1、外部なら0です。
使用条件と制限
x, y は符号なし16ビット、幅・高さは8ビットです。正の大きさを指定し、座標と大きさの和を65535以下に保ってください。座標の折り返しをまたぐ矩形には使いません。
使用例
result[6]=nes_box16_contains_point(&na,9,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
座標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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/collision.h:18
Test a half-open box: left/top edges are included and right/bottom edges excluded. Callers provide valid pointers and non-overflowing edge coordinates.
kitaqfc/lib/collision.c:49
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py)
{
unsigned short right;
unsigned short bottom;
right = (unsigned short)(box->x + box->w);
bottom = (unsigned short)(box->y + box->h);
if (px < box->x)
{
return 0;
}
if (py < box->y)
{
return 0;
}
if (px >= right)
{
return 0;
}
if (py >= bottom)
{
return 0;
}
return 1;
}nes_box16_intersects
FC用の二つの矩形をポインターで渡し、面積のある重なりを調べます。
書式
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b);引数
a一つ目の読み取り可能な
NesBox16。ヌルは渡しません。b二つ目の読み取り可能な
NesBox16。
戻り値
重なれば1、離れているか辺が接するだけなら0です。
使用条件と制限
x, y は符号なし16ビット、幅・高さは8ビットです。正の大きさを指定し、座標と大きさの和を65535以下に保ってください。座標の折り返しをまたぐ矩形には使いません。
使用例
result[5]=nes_box16_intersects(&na,&nb);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
座標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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/collision.h:15
Test two axis-aligned world boxes using exclusive right/bottom edges. Coordinate-plus-size sums must fit u16; touching edges return false. Both boxes must have positive dimensions; the overlap test has no separate empty-box guard.
kitaqfc/lib/collision.c:15
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b)
{
unsigned short a_right;
unsigned short b_right;
unsigned short a_bottom;
unsigned short b_bottom;
a_right = (unsigned short)(a->x + a->w);
b_right = (unsigned short)(b->x + b->w);
a_bottom = (unsigned short)(a->y + a->h);
b_bottom = (unsigned short)(b->y + b->h);
if (a_right <= b->x)
{
return 0;
}
if (b_right <= a->x)
{
return 0;
}
if (a_bottom <= b->y)
{
return 0;
}
if (b_bottom <= a->y)
{
return 0;
}
return 1;
}danmaku — 64発の弾プール
danmakuは64発の固定小数点弾プールです。初期化→発生→stepで移動・判定→drawでOAM作成→VBlankでDMAの順に使います。自機の入力、ダメージ、得点、音はゲーム側で処理します。サンプルは独自に作成したひし形のタイルをCHR RAMへ書きますが、ライブラリ自体はCHR ROMでも使えます。
danmaku_reset
全弾を無効にし、命中・かすり・最大弾数・発生数の統計と、割り当て・表示順の巡回位置を0へ戻します。
書式
void danmaku_reset(void);戻り値
戻り値はありません。
使用条件と制限
dm_player_x、dm_player_y、dm_invulnerableは保持します。OAMや画面は消さないため、画面上の弾を取り除くには空になったプールをdrawしてDMAするか、別途OAMを隠します。
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
danmaku_reset();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:26
Reset the pool, counters and rotating OAM priority. Player settings survive.
kitaqfc/lib/danmaku.c:40
void danmaku_reset(void) {
danmaku_clear();
dm_peak=0;dm_spawned=0;dm_rejected=0;dm_cursor=0;dm_draw_phase=0;
}danmaku_clear
全弾を無効にし、dm_count・dm_hit・dm_grazeを0にします。プレイ中に弾だけを消す用途です。
書式
void danmaku_clear(void);戻り値
戻り値はありません。
使用条件と制限
dm_peak、dm_spawned、dm_rejected、割り当て位置と表示順の巡回位置、自機設定を保持します。サンプルでは1発発生させてclearした後、生存数は0、累計発生数は1になります。OAMの更新は別途必要です。
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
danmaku_clear();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:28
Remove all bullets and step events; preserve lifetime/peak counters.
kitaqfc/lib/danmaku.c:35
void danmaku_clear(void) {
u8 i;
for(i=0;i<64;i++)dm_active[i]=0;
dm_count=0;dm_hit=0;dm_graze=0;
}danmaku_spawn
空きスロットを巡回して1発を割り当て、位置・速度と未かすり状態を設定します。発生した時点では移動や命中判定をしません。
書式
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);引数
x発生位置の横中心座標です。8以上248未満のピクセル値を指定します。
y発生位置の縦中心座標です。24以上232未満のピクセル値を指定します。
vx横方向の速度です。符号付きQ4.4で、16は1更新あたり1ピクセル、−16は逆向き1ピクセルです。値域−128~127は−8~+7.9375ピクセルに相当します。
vy縦方向の速度です。符号付きQ4.4で、16は1更新あたり1ピクセル、−16は逆向き1ピクセルです。値域−128~127は−8~+7.9375ピクセルに相当します。
戻り値
成功時は1、位置が範囲外または64スロットすべて使用中なら0です。成功時はdm_count・dm_spawnedを増やし、必要ならdm_peakを更新します。失敗時はdm_rejectedを増やします。
使用条件と制限
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
danmaku_spawn(48,64,16,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:31
Allocate a bullet at pixel-center x/y with signed 1/16-pixel velocities. Return one on success, zero outside the playfield or when all 64 slots are full.
kitaqfc/lib/danmaku.c:44
u8 danmaku_spawn(u8 x,u8 y,s8 vx,s8 vy) {
u8 i,n;
if(x<8 || x>=248 || y<24 || y>=232){dm_rejected++;return 0;}
i=dm_cursor;
for(n=0;n<64;n++) {
if(dm_active[i]==0) {
dm_x[i]=x;dm_y[i]=y;dm_fraction_x[i]=0;dm_fraction_y[i]=0;
// Expand signed Q4.4 to a two-byte two's-complement Q8.8 step.
dm_vx_integer[i]=(u8)((s16)vx>>4);dm_vx_fraction[i]=(u8)((u8)vx<<4);
dm_vy_integer[i]=(u8)((s16)vy>>4);dm_vy_fraction[i]=(u8)((u8)vy<<4);
dm_grazed[i]=0;dm_active[i]=1;dm_cursor=(u8)((i+1)&63);
dm_count++;if(dm_count>dm_peak)dm_peak=dm_count;
dm_spawned++;return 1;
}
i=(u8)((i+1)&63);
}
dm_rejected++;return 0;
}danmaku_fan
方向を一定量ずつ進めながら複数の弾を発生させます。刻みと個数で扇形や全周の弾を作れます。
書式
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);引数
x発生位置の横中心座標です。8以上248未満のピクセル値を指定します。
y発生位置の縦中心座標です。24以上232未満のピクセル値を指定します。
direction最初の方向です。32段階で1周し、0=右、8=下、16=左、24=上です。下位5ビットを使います。
step1発ごとに加える方向の刻みです。加算後は32で折り返します。0ならすべて同方向です。
count発生を試みる弾数です。64を超える値は64になり、0では何もしません。
speed速度の大きさを1/16ピクセル単位で指定します。16は1、32は2ピクセル/更新です。64を超える値は64になります。
戻り値
戻り値はありません。
使用条件と制限
整数の正弦表から速度を求め、0方向へ丸めます。斜め方向の速度や半径には量子化誤差があります。満杯でも残りの発生試行を続け、失敗分をdm_rejectedに加えます。
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
danmaku_fan(160,104,0,2,16,32);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:34
Emit up to 64 bullets. Angles wrap in 32 steps: 0 right, 8 down, 16 left, 24 up. Speed is in 1/16 pixel, clamped to 64. Each failed allocation counts.
kitaqfc/lib/danmaku.c:62
void danmaku_fan(u8 x,u8 y,u8 direction,u8 step,u8 count,u8 speed) {
u8 i,angle;s16 vx,vy;
if(count>64)count=64;if(speed>64)speed=64;
angle=(u8)(direction&31);
for(i=0;i<count;i++) {
vx=(s16)Danmaku_Sin[(u8)((angle+8)&31)]*(s16)speed;
vy=(s16)Danmaku_Sin[angle]*(s16)speed;
danmaku_spawn(x,y,(s8)(vx/64),(s8)(vy/64));
angle=(u8)((angle+step)&31);
}
}danmaku_step
有効な弾を1回移動し、画面外の弾を除去して、自機との命中・かすりを判定します。呼び出し回数が弾の時間を進めます。
書式
void danmaku_step(void);戻り値
戻り値はありません。
使用条件と制限
呼び出す前にdm_player_x・dm_player_yへ自機の中心を指定します。移動後の位置で、各軸の距離が3以下なら命中です。dm_invulnerableが0のときdm_hitを1にして弾を消し、無敵なら弾を残します。
命中範囲外で各軸の距離が11以下なら、弾ごとに1回だけかすりを数えます。dm_hitとdm_grazeは呼び出しの冒頭で0に戻る、その更新回だけの結果です。無敵で命中範囲内にある弾はかすりとして数えません。
この処理はOAMを書かず、ダメージ処理や効果音のコールバックも呼びません。dm_hit・dm_grazeを読んで、ゲーム側で自機の状態や得点を更新します。
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
danmaku_step();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:38
Move all live bullets once. Remove offscreen/hitting bullets. Hit uses a +/-3-pixel box; graze uses +/-11, once per bullet. Events describe this step. This updates logic only; it neither writes OAM nor calls a game damage handler.
kitaqfc/lib/danmaku.c:73
void danmaku_step(void) {
// Byte arrays let the 6502 move every slot without multiplying a structure
// index. Carry propagates fractional motion into the signed integer step.
__asm {
LDX #0
STX dm_count
STX dm_hit
STX dm_graze
dm_tick_loop:
LDA dm_active,X
BNE dm_tick_live
JMP dm_tick_next
dm_tick_live:
CLC
LDA dm_fraction_x,X
ADC dm_vx_fraction,X
STA dm_fraction_x,X
LDA dm_x,X
ADC dm_vx_integer,X
STA dm_x,X
CMP #8
BCC dm_tick_kill
CMP #248
BCS dm_tick_kill
CLC
LDA dm_fraction_y,X
ADC dm_vy_fraction,X
STA dm_fraction_y,X
LDA dm_y,X
ADC dm_vy_integer,X
STA dm_y,X
CMP #24
BCC dm_tick_kill
CMP #232
BCS dm_tick_kill
// Unsigned absolute distances avoid false collisions across screen wrap.
LDA dm_x,X
SEC
SBC dm_player_x
BCS dm_tick_dx
EOR #255
CLC
ADC #1
dm_tick_dx:
STA dm_distance_x
LDA dm_y,X
SEC
SBC dm_player_y
BCS dm_tick_dy
EOR #255
CLC
ADC #1
dm_tick_dy:
STA dm_distance_y
CMP #4
BCS dm_tick_graze
LDA dm_distance_x
CMP #4
BCS dm_tick_graze
LDA dm_invulnerable
BNE dm_tick_count
LDA #1
STA dm_hit
dm_tick_kill:
LDA #0
STA dm_active,X
JMP dm_tick_next
dm_tick_graze:
LDA dm_grazed,X
BNE dm_tick_count
LDA dm_distance_x
CMP #12
BCS dm_tick_count
LDA dm_distance_y
CMP #12
BCS dm_tick_count
LDA #1
STA dm_grazed,X
INC dm_graze
dm_tick_count:
INC dm_count
dm_tick_next:
INX
CPX #64
BEQ dm_tick_done
JMP dm_tick_loop
dm_tick_done:
}
}danmaku_draw
弾の中心座標から8×8スプライトを組み立て、指定したシャドーOAM範囲へ書きます。弾の移動や命中判定は行いません。
書式
u8 danmaku_draw(u8 first_oam, u8 slots, u8 tile, u8 attributes);引数
first_oam先頭OAMスロット番号(0~63)です。64以上なら書かずに0を返します。サンプルは0を自機用に予約して1から使います。
slots使用できる連続スロット数です。残りの64−first_oam個に制限します。0なら書き込みませんが表示順の巡回位置は進みます。
tile全弾に使う8×8パターンのタイル番号です。スプライトを8×8モードにして、同じ形の弾に使います。
attributes全弾に使うNESスプライト属性です。下位2ビットはパレット、ビット5は背景の後ろ、6は左右反転、7は上下反転です。
戻り値
実際にOAMへ書いた弾数です。生存弾数と有効なスロット数の小さい方になります。
使用条件と制限
開始する弾スロットを呼び出しごとに13ずつ、64で折り返して進めます。固定した弾だけが常に優先されることを避けます。指定範囲の未使用スロットはY=240で隠し、範囲外のOAMは保持します。
中心からXは4、OAMのYは5を引き、ハードウェアの表示Y+1を含めて中央を合わせます。DMAは呼び出さないため、VBlankで__oam_dmaを実行してください。1走査線8スプライトというハードウェアの上限は残ります。
64発の弾のため、書き込み可能なカートリッジPRG RAM $6000~$627F(640バイト)を予約します。使用するマッパーでRAMを有効にしてください。共有状態を使うため再入できません。
dm_countは生存弾数、dm_peakは最大生存弾数です。dm_spawnedとdm_rejectedは成功・失敗した発生数を16ビットで数え、65535の次は0へ戻ります。dm_x・dm_yは弾の中心座標で、dm_activeが0のスロットの値は使わないでください。
KUROSAKIで4種類のコンパイル設定を実行し、全画素、弾の数と位置、予約したOAM、表示順の回転を照合しています。命中・かすり・画面外除去・満杯の処理は別の数値検証でも確認しています。実機での検証ではありません。
使用例
bullet_result[9]=danmaku_draw(1,24,1,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
左側の黄色い4発は中心(48,64)から上下左右へ16ピクセル移動した単発弾です。右側の黄色い16発は中心(160,104)から約32ピクセル広がった円形弾です。水色の自機は(112,192)で、OAMスロット0を予約しています。画像は16回の更新後に止めてあります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png実装とソース
kitaqfc/lib/danmaku.h:43
Write up to slots consecutive shadow-OAM entries starting at first_oam. Rotate bullet priority by 13 modulo 64 and hide unused entries in that range. tile is an 8x8 sprite centered at (4,4). Return the number emitted. DMA is caller-owned and belongs in VBlank. Hardware's eight-per-scanline limit remains.
kitaqfc/lib/danmaku.c:162
u8 danmaku_draw(u8 first_oam,u8 slots,u8 tile,u8 attributes) {
u8 i,n,drawn;
if(first_oam>=64)return 0;
if(slots>(u8)(64-first_oam))slots=(u8)(64-first_oam);
dm_draw_phase=(u8)((dm_draw_phase+13)&63);i=dm_draw_phase;drawn=0;
for(n=0;n<64;n++) {
if(dm_active[i]!=0 && drawn<slots) {
__sprite_set((u8)(first_oam+drawn),(u8)(dm_x[i]-4),(u8)(dm_y[i]-5),tile,attributes);
drawn++;
}
i=(u8)((i+1)&63);
}
for(n=drawn;n<slots;n++)__sprite_hide((u8)(first_oam+n));
return drawn;
}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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:36
Store the assertion code even if the log is full, then attempt an ASSERT entry. The helper records failure but does not stop execution.
kitaqfc/lib/debug.c:67
void debug_assert_fail(u16 code)
{
kq_debug_last_assert = code;
debug_push(debug_assert_label, code);
}debug_get_last_assert
最後に記録されたアサートコードを、ログの空きに関係なく取得します。
書式
u16 debug_get_last_assert(void);戻り値
最後の16ビットコード。初期化直後は0です。
使用条件と制限
0は初期値であり、コード0の失敗と区別できません。読み出しても値を消去しません。
使用例
result[22]=debug_get_last_assert();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
マクロで記録した77、満杯後の999、再初期化後の0を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:42
Read the latest assertion code, initially zero after debug_init.
kitaqfc/lib/debug.c:86
u16 debug_get_last_assert(void)
{
return kq_debug_last_assert;
}debug_get_trace_count
ログ配列に入っている有効エントリー数を調べます。
書式
u8 debug_get_trace_count(void);戻り値
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:38
Read the number of valid entries in the fixed trace buffer.
kitaqfc/lib/debug.c:74
u8 debug_get_trace_count(void)
{
return kq_debug_trace_count;
}debug_get_trace_log
記録済みの名前・値・フレームを読み出すため、内部配列への参照を取得します。
書式
const DebugTraceEntry* debug_get_trace_log(void);戻り値
内部 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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:40
Borrow the internal trace array; bound inspection by debug_get_trace_count.
kitaqfc/lib/debug.c:80
const DebugTraceEntry* debug_get_trace_log(void)
{
return kq_debug_trace_log;
}debug_init
デバッグログの有効件数・フレームタグ・最後のアサートコードを初期化します。
書式
void debug_init(void);戻り値
戻り値はありません。
使用条件と制限
既存エントリーのバイト列は消去しません。初回の記録前に呼び出してください。ログ容量は既定32件です。満杯では追加を捨てます。DEBUG_TRACE_MAX を変更するなら1~255で全翻訳単位を統一してください。名前の文字列はコピーせずポインターを保存するため、検査が終わるまで有効に保ちます。割り込みとメイン処理の同時書き込みは呼び出し側で防いでください。
使用例
debug_init();result[0]=debug_get_trace_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期件数0を確認し、記録後に再初期化して件数とアサートコードだけが0に戻り、配列の古い値は残ることを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:25
Build the RAM-resident ASSERT label and reset log length, frame tag and last assertion code. Old log storage bytes are not erased.
kitaqfc/lib/debug.c:14
void debug_init(void)
{
debug_assert_label[0] = 'A';
debug_assert_label[1] = 'S';
debug_assert_label[2] = 'S';
debug_assert_label[3] = 'E';
debug_assert_label[4] = 'R';
debug_assert_label[5] = 'T';
debug_assert_label[6] = 0;
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:29
Append a named marker with the reserved marker value 0xFFFF.
kitaqfc/lib/debug.c:48
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:27
Choose the frame tag stored in subsequent debug entries.
kitaqfc/lib/debug.c:29
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:33
Append a 16-bit value with the current frame tag.
kitaqfc/lib/debug.c:60
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:31
Widen a byte value into the trace entry's 16-bit value field.
kitaqfc/lib/debug.c:54
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:48
#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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:49
#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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:47
#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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:46
#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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:45
#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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/debug.h:44
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))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のスロットを作成・解放すると、同じ巡回に反映されます。更新用の巡回が終わってから描画用の巡回を行う構成にすると、役割を分けやすくなります。
コールバックは共通PRGバンク0に置くか、呼び出し中はそのバンクが見える状態を保ってください。関数ポインタは16ビットのCPUアドレスであり、バンクを選択しません。FCの関数引数とローカル変数は固定領域を使うため、エンティティの巡回を再帰的に呼び出したり、割り込みから同じ巡回処理に再入したりしないでください。
entity_clear_all
全エンティティを解放し、スプライト未割当の印を含む各項目を初期値へ戻します。
書式
void entity_clear_all(void);戻り値
ありません(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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:47
Release every entity and restore deterministic defaults, including the no-sprite sentinel.
kitaqfc/lib/entity.c:14
void entity_clear_all(void)
{
u8 i;
i = 0;
while (i < ENTITY_MAX) {
entity_pool[(__safe_index u8)i].active = 0;
entity_pool[(__safe_index u8)i].type = 0;
entity_pool[(__safe_index u8)i].x = 0;
entity_pool[(__safe_index u8)i].y = 0;
entity_pool[(__safe_index u8)i].vx = 0;
entity_pool[(__safe_index u8)i].vy = 0;
entity_pool[(__safe_index u8)i].state = 0;
entity_pool[(__safe_index u8)i].timer = 0;
entity_pool[(__safe_index u8)i].sprite = 0xFF;
i = (u8)(i + 1);
}
}entity_count_active
エンティティの状態を変えずに、使用中スロットの数を返します。
書式
u8 entity_count_active(void);戻り値
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:45
Count occupied slots without changing entity state.
kitaqfc/lib/entity.c:94
u8 entity_count_active(void)
{
u8 i;
u8 count;
i = 0;
count = 0;
while (i < ENTITY_MAX) {
if (entity_pool[(__safe_index u8)i].active != 0) count = (u8)(count + 1);
i = (u8)(i + 1);
}
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:32
Returns a reusable slot ID, or 0xFF if no slot is free.
kitaqfc/lib/entity.c:34
u8 entity_create(u8 type, s16 x, s16 y)
{
u8 i;
i = 0;
while (i < ENTITY_MAX) {
if (entity_pool[(__safe_index u8)i].active == 0) {
entity_pool[(__safe_index u8)i].active = 1;
entity_pool[(__safe_index u8)i].type = type;
entity_pool[(__safe_index u8)i].x = x;
entity_pool[(__safe_index u8)i].y = y;
entity_pool[(__safe_index u8)i].vx = 0;
entity_pool[(__safe_index u8)i].vy = 0;
entity_pool[(__safe_index u8)i].state = 0;
entity_pool[(__safe_index u8)i].timer = 0;
entity_pool[(__safe_index u8)i].sprite = 0xFF;
return i;
}
i = (u8)(i + 1);
}
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:35
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.
kitaqfc/lib/entity.c:58
void entity_destroy(u8 id)
{
if (id >= ENTITY_MAX) return;
entity_pool[(__safe_index u8)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 は失敗を表します。
巡回がそのスロットに到達した時点で使用状態を調べます。コールバックで後方のスロットを解放すれば処理を飛ばし、後方を確保すれば同じ巡回で処理することがあります。開始時の対象一覧を固定して処理する方式ではありません。
コールバックは共通PRGバンク0に置くか、呼び出し中はそのバンクが見える状態を保ってください。関数ポインタは16ビットのCPUアドレスであり、バンクを選択しません。FCの関数引数とローカル変数は固定領域を使うため、エンティティの巡回を再帰的に呼び出したり、割り込みから同じ巡回処理に再入したりしないでください。
使用例
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:43
Call the drawing callback for each active slot, using the same traversal rules as update. The callback submits drawing data; the pool does not allocate sprites.
kitaqfc/lib/entity.c:88
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:37
Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.
kitaqfc/lib/entity.c:66
Entity* entity_get(u8 id)
{
if (id >= ENTITY_MAX) return 0;
if (entity_pool[(__safe_index u8)id].active == 0) return 0;
return &entity_pool[(__safe_index u8)id];
}entity_init
エンティティプールの全スロットを初期化します。
書式
void entity_init(void);戻り値
ありません(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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:30
Reset every slot before the game starts using the pool.
kitaqfc/lib/entity.c:8
void entity_init(void)
{
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 は失敗を表します。
巡回がそのスロットに到達した時点で使用状態を調べます。コールバックで後方のスロットを解放すれば処理を飛ばし、後方を確保すれば同じ巡回で処理することがあります。開始時の対象一覧を固定して処理する方式ではありません。
コールバックは共通PRGバンク0に置くか、呼び出し中はそのバンクが見える状態を保ってください。関数ポインタは16ビットのCPUアドレスであり、バンクを選択しません。FCの関数引数とローカル変数は固定領域を使うため、エンティティの巡回を再帰的に呼び出したり、割り込みから同じ巡回処理に再入したりしないでください。
使用例
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 は期待値に一致したことを表します。
FC版の巡回は使用中の2個に対してそれぞれコールバックを呼ぶので、UPDATE CALLS 002、DRAW CALLS 002 になります。サンプルの更新コールバックは両方を右へ1タイル動かします。描画コールバックは2個の物体を表す A をタイル (3,12)、B を (7,12) に置きます。コールバックが描いた背景の文字タイルであり、自動確保されたスプライトではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm実装とソース
kitaqfc/lib/entity.h:40
Call fn(id) for each active slot in ascending order. A null callback does nothing. Changes to later slots take effect during the same traversal. Do not reenter this traversal.
kitaqfc/lib/entity.c:75
void entity_update_all(EntityFn fn)
{
u8 i;
if (fn == 0) return;
i = 0;
while (i < ENTITY_MAX) {
if (entity_pool[(__safe_index u8)i].active != 0) fn(i);
i = (u8)(i + 1);
}
}FDSファイルと固定長セーブ領域
fds_file.hは、ファイルをゲーム用バッファへ読む処理と、ディスクに確保したセーブ領域へ書く処理を提供します。fds_save.hは同じ宣言を読み込むためのヘッダーです。オーバーレイの関数呼び出しにはfds_overlay.hを使います。
__fds_load_file() / __fds_save_file()
fds_file_io.c fds_file_io_manifest.json fds_file_io_payload.bin
fds_sound.h
64段階の波形、変調テーブル、周波数、音量、エンベロープを操作する組み込み関数の宣言です。実装はKITAQFCコンパイラが生成します。
--mapper=fdsで.fdsを生成します。サンプルはディスク入出力を使わず音源を操作します。録音はKUROSAKIのRAM直接起動と音源モデルで確認しました。検証用の空のファームウェア領域はBIOS機能を提供せず、配布物にも含みません。通常のディスク起動や実機では別途適切な起動環境が必要です。
関数の個別説明: __fds_sound_enable, __fds_wave_load, __fds_mod_load, __fds_freq_set, __fds_volume_set, __fds_env_set
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:35
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.
kitaqfc/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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:26
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.
kitaqfc/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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:38
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.
kitaqfc/lib/fixed.c:34
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8)
{
fix8 d;
// The endpoint difference must fit s16 before the reduced-precision interpolation.
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:31
Approximate 8.8 multiplication by discarding four low bits from each operand before multiplying. This trades precision for a smaller intermediate product.
kitaqfc/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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:28
Discard the eight fractional bits using a signed right shift.
kitaqfc/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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:41
Return the magnitude in s16. The most-negative s16 has no positive s16 representation and must be excluded by callers needing a nonnegative result.
kitaqfc/lib/fixed.c:44
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:47
Clamp to an inclusive interval; callers must provide lo <= hi.
kitaqfc/lib/fixed.c:65
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:45
Select the larger signed value.
kitaqfc/lib/fixed.c:58
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:43
Select the smaller signed value.
kitaqfc/lib/fixed.c:51
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);__vramq_commit();__vramq_exec();
}}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
座標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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:54
Test a half-open rectangle: left/top edges are included, right/bottom excluded. The coordinate-plus-size sums must remain representable as s16.
kitaqfc/lib/fixed.c:86
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/fixed.h:51
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.
kitaqfc/lib/fixed.c:75
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 はそれぞれの取得関数の別名です。
__pad_read1_safe でコントローラ1を読み、NES形式のビット順を共通の BTN_* 形式へ変換します。前回値を保存し、押下・解放・リピートの各マスクを求めます。コントローラ2は対象外です。入力処理1回につき一度呼んでください。
共通マスクは 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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:53
#define btn_down input_downkitaqfc/lib/input.c:85
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:54
#define btn_pressed input_pressedkitaqfc/lib/input.c:87
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:55
#define btn_released input_releasedkitaqfc/lib/input.c:89
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:56
#define btn_repeat input_repeatkitaqfc/lib/input.c:91
u8 input_repeat(u8 mask) { return (u8)((kq_input_repeat_keys & mask) != 0); }input_current
直近の入力更新で得た、押されている全ボタンのビットマスクを返します。
書式
u8 input_current(void);戻り値
共通の 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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:49
Read the complete translated held-button mask.
kitaqfc/lib/input.c:93
u8 input_current(void) { 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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:41
Return true when any masked logical button is held.
kitaqfc/lib/input.c:85
u8 input_down(u8 mask) { return (u8)((kq_input_keys & mask) != 0); }input_init
現在・前回・押下・解放・リピートの各マスクを0にし、8個のリピートタイマーを INPUT_REPEAT_DELAY に戻します。コントローラを読まずに入力ライブラリを初期化します。その後の最初の更新で押されているボタンは、新たな押下として扱います。
書式
void input_init(void);戻り値
ありません(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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:35
Clear held/edge/repeat masks and reset each independent repeat countdown.
kitaqfc/lib/input.c:34
void input_init(void)
{
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 = (u8)(i + 1);
}
}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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:43
Return true when any masked button became pressed in the latest input update.
kitaqfc/lib/input.c:87
u8 input_pressed(u8 mask) { return (u8)((kq_input_press_keys & mask) != 0); }input_previous
直近の入力更新より1回前に保存した、押されていたボタンのビットマスクを返します。
書式
u8 input_previous(void);戻り値
共通の 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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:51
Read the translated held-button mask from before the latest update.
kitaqfc/lib/input.c:95
u8 input_previous(void) { 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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:45
Return true when any masked button became released in the latest update.
kitaqfc/lib/input.c:89
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:47
Return true for a masked initial press or scheduled repeat pulse.
kitaqfc/lib/input.c:91
u8 input_repeat(u8 mask) { return (u8)((kq_input_repeat_keys & mask) != 0); }input_update
__pad_read1_safe でコントローラ1を読み、NES形式のビット順を共通の BTN_* 形式へ変換します。前回値を保存し、押下・解放・リピートの各マスクを求めます。コントローラ2は対象外です。入力処理1回につき一度呼んでください。
書式
void input_update(void);戻り値
ありません(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 のコンパイルにも同じマクロ値を適用してください。呼び出し側のヘッダーだけを変更してもライブラリの設定は変わりません。
safe版は全ボタンを2回ずつ読み、二つのマスクが一致するまで繰り返します。各読込で $4016 をストローブし、両コントローラのシリアル読込位置を先頭へ戻します。再試行回数に上限はありません。この一致確認だけで、すべてのDMC・周辺機器との組み合わせを保証するわけではありません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input.h:39
Read controller 1 through the safe intrinsic, translate its bit order and derive press/release edges. Repeat delays count calls to this function, so call it once per intended input tick.
kitaqfc/lib/input.c:52
void input_update(void)
{
u8 i;
u8 raw;
kq_input_prev_keys = kq_input_keys;
raw = __pad_read1_safe();
kq_input_keys = input_translate_pad(raw);
kq_input_press_keys = (u8)(kq_input_keys & (u8)(kq_input_keys ^ kq_input_prev_keys));
kq_input_release_keys = (u8)(kq_input_prev_keys & (u8)(kq_input_keys ^ kq_input_prev_keys));
kq_input_repeat_keys = kq_input_press_keys;
i = 0;
while (i < 8) {
u8 mask;
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] = (u8)(input_repeat_timer[i] - 1);
} 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 = (u8)(i + 1);
}
}input_repeat.h — 押し続け入力のリピート
pad.c と組み合わせて使い、コントローラ1のボタンごとに独立したリピートカウンタを追加します。待ち時間と間隔を実行時に設定し、パッドを読んでから入力処理ごとに一度リピートを更新します。押下・解放・押下中の取得関数はパッドの保存値を読み、リピートの取得関数だけが nes_pad_repeat_step() の結果を読みます。
初回のカウントダウン値で、0~255です。新たな押下による即時出力の後、delay + 1 回のリピート更新で次を出力します。単位は nes_pad_repeat_step() の呼び出し回数です。1フレームに一度呼ぶ場合に限り、フレーム数と一致します。
以降の出力間のカウントダウン値で、0~255です。実際の間隔は interval + 1 回のリピート更新です。0なら初期待ち終了後の各更新で出力します。8個のボタンはそれぞれ独立して数えます。
nes_pad_held
コントローラ1の直近の押下中マスクを取り出します。選んだボタンを押し続けている間、連続して動作させる用途です。保存された状態を読むだけで、ハードウェアを再度読みません。
書式
unsigned char nes_pad_held(unsigned char mask);引数
maskNESの生のビット順で、取り出すボタンを指定します。複数なら
|で組み合わせ、0なら何も選びません。コントローラのポート番号ではなく、ビットを選ぶマスクです。
戻り値
保存されたマスクのうち、該当するビットを元の位置のまま返します。1以外の値にもなり、0は該当なしです。読み取ってもイベントは消費されません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
if (nes_pad_held(0x02)!=0) held_ticks++; // B is bit 1 in NES raw format.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:28
Return the requested currently-held bits in the NES pad layout.
kitaqfc/lib/input_repeat.c:103
unsigned char nes_pad_held(unsigned char mask)
{
return (unsigned char)(nes_pad1_cur & mask);
}nes_pad_release
コントローラ1の直近の解放マスクを取り出します。前回は押下中で、今回は離された選択ボタンを識別します。離したまま次の読込を行っても、新たな解放イベントは生じません。
書式
unsigned char nes_pad_release(unsigned char mask);引数
maskNESの生のビット順で、取り出すボタンを指定します。複数なら
|で組み合わせ、0なら何も選びません。コントローラのポート番号ではなく、ビットを選ぶマスクです。
戻り値
保存されたマスクのうち、該当するビットを元の位置のまま返します。1以外の値にもなり、0は該当なしです。読み取ってもイベントは消費されません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
if (nes_pad_release(0x02)!=0) {
release_bits=nes_pad_release(0x02); // Returns the released B bit, 2.
if (nes_pad_held(0x02)!=0 || nes_pad_trigger(0x02)!=0 || nes_pad_repeat(0x02)!=0) failures++;
break;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:26
Return the requested newly-released bits from the latest pad poll.
kitaqfc/lib/input_repeat.c:97
unsigned char nes_pad_release(unsigned char mask)
{
return (unsigned char)(nes_pad1_released & mask);
}nes_pad_repeat
直近の nes_pad_repeat_step() が生成したリピート出力を取り出します。新規押下と、押し続けによる所定の繰り返しの両方を含みます。この取得関数を呼んでもタイマーは進みません。
書式
unsigned char nes_pad_repeat(unsigned char mask);引数
maskNESの生のビット順で、取り出すボタンを指定します。複数なら
|で組み合わせ、0なら何も選びません。コントローラのポート番号ではなく、ビットを選ぶマスクです。
戻り値
保存されたマスクのうち、該当するビットを元の位置のまま返します。1以外の値にもなり、0は該当なしです。読み取ってもイベントは消費されません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
if (nes_pad_repeat(0x02)!=0) {
if (pulse_count==1 && held_ticks-previous_pulse!=4) failures++;
if (pulse_count>1 && held_ticks-previous_pulse!=2) failures++;
previous_pulse=held_ticks; pulse_count++;
if (nes_pad_repeat(0x02)!=2) failures++; // Querying again preserves the pulse.
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:24
Return the requested repeat-pulse bits from the latest repeat step.
kitaqfc/lib/input_repeat.c:91
unsigned char nes_pad_repeat(unsigned char mask)
{
return (unsigned char)(nes_pad1_repeat & mask);
}nes_pad_repeat_config
実行時の初期待ち時間と繰り返し間隔を設定し、nes_pad1_repeat を0にして、8個のボタン別カウンタを delay に戻します。コントローラの読込や、押下中・押下・解放マスクの消去は行いません。
書式
void nes_pad_repeat_config(unsigned char delay, unsigned char interval);引数
delay初回のカウントダウン値で、0~255です。新たな押下による即時出力の後、
delay + 1回のリピート更新で次を出力します。単位はnes_pad_repeat_step()の呼び出し回数です。1フレームに一度呼ぶ場合に限り、フレーム数と一致します。interval以降の出力間のカウントダウン値で、0~255です。実際の間隔は
interval + 1回のリピート更新です。0なら初期待ち終了後の各更新で出力します。8個のボタンはそれぞれ独立して数えます。
戻り値
ありません(void)。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
使用例
nes_pad_repeat_config(3,1); // Next pulse after 4 held updates, then every 2.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:16
Set initial/repeat countdowns and clear repeat output. Zero countdowns are allowed and cause a repeat on the next held-button step. A fresh press emits immediately. Held repeats follow after delay+1 and then interval+1 steps. These are call counts, not milliseconds; use one fresh pad poll before each step.
kitaqfc/lib/input_repeat.c:21
void nes_pad_repeat_config(unsigned char delay, unsigned char interval)
{
unsigned char i;
nes_pad_repeat_delay = delay;
nes_pad_repeat_interval = interval;
nes_pad1_repeat = 0;
i = 0;
while (i != 8)
{
nes_pad1_repeat_counter[i] = delay;
i = i + 1;
}
}nes_pad_repeat_step
直近の読込で得た押下中・押下マスクから nes_pad1_repeat を作り直し、各ボタンのカウントダウンを1回進めます。先に nes_pad_poll() を呼び、その後に一度呼んでください。古い押下マスクのまま繰り返すと、初期待ちを毎回設定し直し、新規押下として繰り返し出力してしまいます。
書式
void nes_pad_repeat_step(void);戻り値
ありません(void)。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
nes_pad_repeat_step(); // Once after each fresh poll; getters do not advance time.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:20
Generate per-button repeat bits from the most recently polled pad state. Poll first, then call once per tick; a countdown reaching zero emits on the following step, so an interval N leaves N decrement steps between pulses.
kitaqfc/lib/input_repeat.c:40
void nes_pad_repeat_step(void)
{
unsigned char bit_index;
unsigned char mask;
unsigned char counter;
nes_pad1_repeat = 0;
bit_index = 0;
mask = 1;
while (mask != 0)
{
if ((nes_pad1_cur & mask) == 0)
{
nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_delay;
}
else
{
if ((nes_pad1_pressed & mask) != 0)
{
nes_pad1_repeat = nes_pad1_repeat | mask;
nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_delay;
}
else
{
counter = nes_pad1_repeat_counter[bit_index];
if (counter == 0)
{
nes_pad1_repeat = nes_pad1_repeat | mask;
nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_interval;
}
else
{
nes_pad1_repeat_counter[bit_index] = counter - 1;
}
}
}
bit_index = bit_index + 1;
// Byte overflow terminates the scan after the eighth button; each button has its own countdown.
mask = mask + mask;
}
}nes_pad_trigger
コントローラ1の直近の新規押下マスクから mask に該当するビットを取り出します。選んだボタンが離された状態から押された状態へ変わったときに、一度だけ動作させる用途です。
書式
unsigned char nes_pad_trigger(unsigned char mask);引数
maskNESの生のビット順で、取り出すボタンを指定します。複数なら
|で組み合わせ、0なら何も選びません。コントローラのポート番号ではなく、ビットを選ぶマスクです。
戻り値
保存されたマスクのうち、該当するビットを元の位置のまま返します。1以外の値にもなり、0は該当なしです。読み取ってもイベントは消費されません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
if (nes_pad_trigger(0x02)!=0) {
first_trigger=nes_pad_trigger(0x02); // Returns 2, not Boolean 1.
first_held=nes_pad_held(0x02); first_repeat=nes_pad_repeat(0x02);
if (first_trigger!=2 || first_held!=2 || first_repeat!=2) failures++;
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/input_repeat.h:22
Return the requested newly-pressed bits, not a normalized Boolean.
kitaqfc/lib/input_repeat.c:85
unsigned char nes_pad_trigger(unsigned char mask)
{
return (unsigned char)(nes_pad1_pressed & mask);
}metasprite.h — 複数OBJをまとめた画像
metasprite.cのC関数と、__metasprite_drawへ展開するnes_game.hのマクロがあります。どちらも同じ終端付きデータ列を読み、同じ4バイト形式のOAM項目を書きます。サンプルは各形式を別々にコンパイルします。
C版メタスプライト関数はnes_oam_shadow[256]を必要とします。runtime.cはこれを0x0200に定義し、VRAMキュー用に0x0300–0x03BFも確保して、__nes_nmiを提供します。各実装は一度だけ組み込み、割り込み処理をプログラム全体と整合させてください。
nes_metasprite_draw
metasprite.cのC関数と、__metasprite_drawへ展開するnes_game.hのマクロがあります。どちらも同じ終端付きデータ列を読み、同じ4バイト形式のOAM項目を書きます。サンプルは各形式を別々にコンパイルします。
終端付きの部品データ列を読み、ページ02のシャドーバッファの連続した項目へ展開します。各部品の1バイトのX・Yオフセットを基準位置へ加え、部品ごとのタイルと属性を設定します。戻り値は次のOAM番号です。
書式
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite);
// Macro from nes_game.h:
#define nes_metasprite_draw(i,x,y,data) __metasprite_draw((i),(x),(y),(data))引数
oam_index / iスプライトの項目番号0〜63です。バイト単位のオフセットではありません。内部では8ビットのカーソルで4倍するため、範囲外の値は64を周期に折り返し、別の項目を上書きします。確保済みかどうかの検査は行いません。
base_x / xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
base_y / yOAMに格納するY値です。画面の1行目以降に上端を置く場合は表示上端−1を渡します。239〜255は240行の表示範囲外です。
metasprite / datadx, dy, tile, attrの4バイトを1組とする読み出し可能なデータ列へのポインタです。次のdxの位置に0xFFがあれば終了し、その後のバイトは不要です。オフセットは256を周期に加算します。0xFEは−2、dyの0xFFは−1として使えますが、dxの0xFFは終端専用なので−1を表せません。現在選択中のメモリでポインタが有効である必要があります。
戻り値
次のOAM項目番号で、(oam_index + 書き込んだ部品数) modulo 64です。描いた部品数ではなく、次の書き込み位置を返します。空のデータ列ではoam_index modulo 64です。
使用条件と制限
使うヘッダーを明確に選んでください。nes_game.hと総合ヘッダーfc.hはマクロを定義します。C関数を使う場合はruntime.hやmetasprite.hと各実装ファイルを組み合わせ、同名マクロを含めないか、先に#undefで解除します。引数なしのDMAマクロにC関数のpage引数を渡すことはできません。
C版メタスプライト関数はnes_oam_shadow[256]を必要とします。runtime.cはこれを0x0200に定義し、VRAMキュー用に0x0300–0x03BFも確保して、__nes_nmiを提供します。各実装は一度だけ組み込み、割り込み処理をプログラム全体と整合させてください。
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
部品数・画面外クリッピング・容量の検査はありません。必ず終端を付け、部品数を64−oam_index以下にしてください。座標は256を周期に折り返し、63番の次は0番へ書き込みます。空のデータ列は何も変更しません。この形式は、スプライトライブラリのmetasprite_drawで使う個数指定のMetaSpritePart配列とは異なります。
使用例
demo_next=nes_metasprite_draw(4,144,31,demo_parts); // C function: returns 6.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
C-RUNTIMEはC関数を組み合わせて使う例です。PAIRには(144,32)と(152,32)に8×8矢印が並び、2本目は左右反転します。y=64の別の2本は隠すため、HIDE RANGEは空欄になります。描画でスロット4–5、6–7を順に使い、NEXT 08を表示します。ページ02をページ04へコピーし、スロット8だけ矢印のタイル128から四角の129へ変更します。nes_oam_dma(4)によりPAGE 04の横の(144,96)に緑の四角が表示され、ページ02にはタイル128が残ります。
矢印は赤い縁・緑の軸・先端の青い細部で構成し、四角のタイル129は緑です。検証では各画像の8,704ピクセルの色、転送したOAM全256バイト、表示ラベルを照合します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm使用例
fc_oam_next=nes_metasprite_draw(5,136,95,fc_oam_pair); // Returns 7 after two entries.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/metasprite.h:12
Optional declarations for the phase 10 NES metasprite helper. Metasprite data layout: [dx, dy, tile, attr] repeated, terminated by dx=0xFF Read four-byte records [dx,dy,tile,attr] until dx=0xFF, writing them to shadow OAM. Coordinates and destination offsets wrap as bytes. The caller must supply a terminator and enough unused slots; there is no capacity guard. Return the next slot index derived from the final wrapped byte offset.
kitaqfc/lib/metasprite.c:18
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite)
{
unsigned char dst;
unsigned char dx;
dst = oam_index + oam_index;
dst = dst + dst;
while (1)
{
dx = metasprite[0];
if (dx == 0xFF)
{
break;
}
// These are raw OAM Y bytes, not a corrected screen-top coordinate; no allocation bookkeeping is updated.
nes_oam_shadow[dst] = (unsigned char)(base_y + metasprite[1]);
nes_oam_shadow[(unsigned char)(dst + 1)] = metasprite[2];
nes_oam_shadow[(unsigned char)(dst + 2)] = metasprite[3];
nes_oam_shadow[(unsigned char)(dst + 3)] = (unsigned char)(base_x + dx);
dst = (unsigned char)(dst + 4);
metasprite = metasprite + 4;
}
return (unsigned char)(dst >> 2);
}nes_metasprite_hide_from
連続するOAMシャドウ項目のYバイトへ0xF8を書き、まとめて非表示にします。タイル・属性・X座標は保持します。このC関数はOAM転送やスプライトライブラリの割り当て解除を行いません。
書式
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count);引数
oam_indexスプライトの項目番号0〜63です。バイト単位のオフセットではありません。内部では8ビットのカーソルで4倍するため、範囲外の値は64を周期に折り返し、別の項目を上書きします。確保済みかどうかの検査は行いません。
sprite_count非表示にする項目数で、範囲は0から
64−oam_indexです。0なら何も変更しません。多すぎるとバイトカーソルが折り返し、OAM先頭の項目まで隠してしまいます。関数内で範囲は検査しません。
戻り値
ありません(void)。
使用条件と制限
C版メタスプライト関数はnes_oam_shadow[256]を必要とします。runtime.cはこれを0x0200に定義し、VRAMキュー用に0x0300–0x03BFも確保して、__nes_nmiを提供します。各実装は一度だけ組み込み、割り込み処理をプログラム全体と整合させてください。
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
使用例
nes_metasprite_hide_from(6,2); // Hide slots 6 and 7; the pair at slots 4 and 5 remains.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
C-RUNTIMEはC関数を組み合わせて使う例です。PAIRには(144,32)と(152,32)に8×8矢印が並び、2本目は左右反転します。y=64の別の2本は隠すため、HIDE RANGEは空欄になります。描画でスロット4–5、6–7を順に使い、NEXT 08を表示します。ページ02をページ04へコピーし、スロット8だけ矢印のタイル128から四角の129へ変更します。nes_oam_dma(4)によりPAGE 04の横の(144,96)に緑の四角が表示され、ページ02にはタイル128が残ります。
矢印は赤い縁・緑の軸・先端の青い細部で構成し、四角のタイル129は緑です。検証では各画像の8,704ピクセルの色、転送したOAM全256バイト、表示ラベルを照合します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/metasprite.h:17
Hide consecutive shadow slots by setting Y to 0xF8. Keep the requested range within 64 slots to avoid wrapping and hiding earlier entries.
kitaqfc/lib/metasprite.c:49
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count)
{
unsigned char dst;
dst = oam_index + oam_index;
dst = dst + dst;
while (sprite_count != 0)
{
nes_oam_shadow[dst] = 0xF8;
dst = (unsigned char)(dst + 4);
sprite_count = sprite_count - 1;
}
}nametable_asset.h — ネームテーブル用素材と配置
32タイルの行、pitch付き矩形、塗りつぶし、960+64バイトの画面全体を転送します。属性シャドーの編集と転送もここにあります。即時転送と後で消費するキューを用途に応じて選びます。
直接転送はPPUCTRLのアドレス増分を1にし、描画停止中か転送量が収まるVBlankで行います。これらのヘルパーは描画停止も待機もしません。
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
nes_attr_apply_now
共有シャドー64バイトを指定ネームテーブルの属性表へ即座に転送します。
書式
void nes_attr_apply_now(unsigned short nt_base);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
直接転送はPPUCTRLのアドレス増分を1にし、描画停止中か転送量が収まるVBlankで行います。これらのヘルパーは描画停止も待機もしません。
使用例
nes_attr_apply_now(0x2000);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:20
Upload all attribute shadow bytes immediately; the caller provides safe PPU timing.
kitaqfc/lib/nametable_asset.c:90
void nes_attr_apply_now(unsigned short nt_base)
{
nes_ppu_stream_write(nes_attr_base_from_nt(nt_base), nes_attr_shadow, 64);
}nes_attr_base_from_nt
ネームテーブル先頭に$3C0を足し、属性表の先頭を求めます。
書式
unsigned short nes_attr_base_from_nt(unsigned short nt_base);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
戻り値
nt_base+$3C0。入力の妥当性は検査しません。
使用例
result[1]=nes_attr_base_from_nt(0x2400);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:11
Locate the 64-byte attribute table at offset 0x03C0 from a nametable base.
kitaqfc/lib/nametable_asset.c:30
unsigned short nes_attr_base_from_nt(unsigned short nt_base)
{
return (unsigned short)(nt_base + 0x03C0);
}nes_attr_queue_all
属性シャドー全体をキューにコピーします。
書式
unsigned char nes_attr_queue_all(unsigned short nt_base);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
戻り値
登録成功で1、容量不足で0。1件67バイトを使います。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
使用例
result[5]=nes_attr_queue_all(0x2000);result[6]=nes_vram_queue_used;nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:22
Copy all shadow attribute bytes into the runtime queue and return its admission result.
kitaqfc/lib/nametable_asset.c:97
unsigned char nes_attr_queue_all(unsigned short nt_base)
{
return nes_vram_queue_try_write(nes_attr_base_from_nt(nt_base), nes_attr_shadow, 64);
}nes_attr_shadow_clear
属性シャドー64バイトを同じpacked値で埋めます。
書式
void nes_attr_shadow_clear(unsigned char value);引数
valuepacked属性値。例0x55は全区画パレット1、0xAAは2です。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
使用例
nes_attr_shadow_clear(0x55);result[2]=nes_attr_shadow[63];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:13
Fill all 64 shadow bytes with an already-packed attribute value.
kitaqfc/lib/nametable_asset.c:36
void nes_attr_shadow_clear(unsigned char value)
{
__memset(nes_attr_shadow, value, 64);
}nes_attr_shadow_copy
属性テーブル全体をRAMへコピーし、編集の初期値にします。
書式
void nes_attr_shadow_copy(unsigned char* src64);引数
src64読み取り可能な64バイトのpacked属性表。1タイルごとのパレット番号配列ではありません。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
使用例
for(i=0;i<64;i++)table[i]=0x55;nes_attr_shadow_copy(table);result[3]=nes_attr_shadow[0];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:15
Copy an entire packed attribute table into shadow RAM without writing the PPU.
kitaqfc/lib/nametable_asset.c:42
void nes_attr_shadow_copy(unsigned char* src64)
{
__memcpy(nes_attr_shadow, src64, 64);
}nes_attr_shadow_set_quad
1つの2×2タイル区画だけのパレットを変更し、同じバイト内の他の区画を保ちます。
書式
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index);引数
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
pal_indexパレット番号の下位2ビット。
戻り値
戻り値はありません。
使用条件と制限
共有属性シャドー64バイトはCPU RAM $03E0~$041Fです。ほかのデータと重ねないでください。属性1バイトは4×4タイル、各2ビットは2×2タイルのパレットを表します。
座標を32未満に保ちます。奇数座標もそのセルを含む2×2区画を選びます。可視行は0~29です。
使用例
nes_attr_shadow_set_quad(4,8,2);result[4]=nes_attr_shadow[17];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:18
Replace only the chosen quadrant's palette bits, preserving its neighbors. Palette IDs are masked to two bits; tile coordinates must fit the shadow table.
kitaqfc/lib/nametable_asset.c:72
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index)
{
unsigned char idx;
unsigned char shift;
unsigned char mask;
unsigned char value;
idx = nes_attr_index(tile_x, tile_y);
shift = nes_attr_shift(tile_x, tile_y);
pal_index = (unsigned char)(pal_index & 3);
value = nes_attr_shadow[idx];
mask = (unsigned char)(3 << shift);
value = (unsigned char)(value & (unsigned char)(~mask));
value = (unsigned char)(value | (unsigned char)(pal_index << shift));
nes_attr_shadow[idx] = value;
}nes_nametable_apply_now
960バイトのタイル番号に続けて64バイトの属性表を一括転送します。
書式
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
nam96032×30タイルの行優先配列。
attr6464バイトのpacked属性表。シャドーとは別の入力でも構いません。
戻り値
戻り値はありません。
使用条件と制限
直接転送はPPUCTRLのアドレス増分を1にし、描画停止中か転送量が収まるVBlankで行います。これらのヘルパーは描画停止も待機もしません。
1024バイトを同期転送するため、通常は起動時など描画を止めた場面で使います。フレームを分けた転送は行いません。
使用例
nes_nametable_apply_now(0x2000,empty,table);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:41
Upload 960 tile bytes followed by 64 attribute bytes. This bulk operation does not disable rendering or wait for VBlank on its own.
kitaqfc/lib/nametable_asset.c:178
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64)
{
nes_ppu_stream_write(nt_base, nam960, 960);
nes_ppu_stream_write(nes_attr_base_from_nt(nt_base), attr64, 64);
}nes_nt_base_from_index
番号の下位2ビットから論理ネームテーブルの先頭を求めます。
書式
unsigned short nes_nt_base_from_index(unsigned char index);引数
index番号。0~3を使い、4以上は下位2ビットへ周回します。
戻り値
$2000 + (index & 3)*$400。
使用例
result[0]=nes_nt_base_from_index(5);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:9
Mask the index to the four logical nametables and return its PPU base. Physical storage still depends on the cartridge's mirroring configuration.
kitaqfc/lib/nametable_asset.c:22
unsigned short nes_nt_base_from_index(unsigned char index)
{
index = (unsigned char)(index & 3);
return (unsigned short)(0x2000 + ((unsigned short)index << 10));
}nes_nt_queue_fill_rect
矩形の各行を同じタイルで埋めるキューへ登録します。
書式
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
width幅(タイル数)。
height高さ(タイル数)。
value塗りつぶすタイル番号0~255。
戻り値
全行成功または高さ0で1、途中の容量不足で0。
使用条件と制限
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
座標と幅・高さは可視ネームテーブル内に収め、pitchは通常width以上にします。クリッピングはありません。キュー版の1行幅は127以下、矩形の通常の最大幅は32です。
使用例
result[10]=nes_nt_queue_fill_rect(0x2000,16,8,4,3,1);result[11]=nes_vram_queue_used;nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:38
Queue one fill record per row, stopping on failure while retaining previously queued rows. The runtime fill-length and rectangle bounds requirements apply.
kitaqfc/lib/nametable_asset.c:157
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value)
{
unsigned char row;
unsigned short ppu_addr;
row = 0;
while (row != height)
{
ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
if (nes_vram_queue_try_fill(ppu_addr, value, width) == 0)
{
return 0;
}
row = (unsigned char)(row + 1);
}
return 1;
}nes_nt_queue_rect
矩形の各行をキューへコピーします。
書式
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
width幅(タイル数)。
height高さ(タイル数)。
pitch元配列で次の行まで進むバイト数。行の末尾の余白を飛ばせます。
src(height−1)*pitch+widthバイト以上の読み取り元。
戻り値
全行成功または高さ0で1、途中の容量不足で0。
使用条件と制限
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
座標と幅・高さは可視ネームテーブル内に収め、pitchは通常width以上にします。クリッピングはありません。キュー版の1行幅は127以下、矩形の通常の最大幅は32です。
使用例
result[8]=nes_nt_queue_rect(0x2000,10,8,2,3,4,patch);result[9]=nes_vram_queue_used;nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:35
Copy rows into the runtime queue until complete or full. Failure leaves earlier rows queued; it does not roll back a partially admitted rectangle. Widths must respect the queue literal-record limit. Pitch is source bytes per row. Each copied literal record costs width+3 queue bytes; check total admission needs when partial updates are unacceptable.
kitaqfc/lib/nametable_asset.c:137
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src)
{
unsigned char row;
unsigned short ppu_addr;
row = 0;
while (row != height)
{
ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
if (nes_vram_queue_try_write(ppu_addr, src, width) == 0)
{
return 0;
}
src = src + pitch;
row = (unsigned char)(row + 1);
}
return 1;
}nes_nt_queue_row
1行32タイルをキューへコピーします。
書式
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
row行番号0~29。範囲検査はありません。
src32読み取り可能な32バイトのタイル番号。
戻り値
成功1、容量不足0。キューを35バイト使います。
使用条件と制限
runtime.cをリンクします。キューはデータをコピーし、後のNMIまたは安全な描画停止中のflushで反映します。キューへの登録と消費を競合させないでください。複数行の途中で容量不足になった場合、それまでの行は残ります。
使用例
result[7]=nes_nt_queue_row(0x2000,4,row);nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:26
Copy one complete nametable row into the runtime queue.
kitaqfc/lib/nametable_asset.c:111
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32)
{
unsigned short ppu_addr;
ppu_addr = (unsigned short)(nt_base + ((unsigned short)row << 5));
return nes_vram_queue_try_write(ppu_addr, src32, 32);
}nes_nt_stream_rect
矩形の各行を元配列のpitch間隔で読み、PPUへ直接転送します。
書式
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
tile_x左端のタイル列。可視範囲は0~31。
tile_y上端のタイル行。可視範囲は0~29。
width幅(タイル数)。
height高さ(タイル数)。
pitch元配列で次の行まで進むバイト数。行の末尾の余白を飛ばせます。
src(height−1)*pitch+widthバイト以上の読み取り元。
戻り値
戻り値はありません。
使用条件と制限
直接転送はPPUCTRLのアドレス増分を1にし、描画停止中か転送量が収まるVBlankで行います。これらのヘルパーは描画停止も待機もしません。
座標と幅・高さは可視ネームテーブル内に収め、pitchは通常width以上にします。クリッピングはありません。キュー版の1行幅は127以下、矩形の通常の最大幅は32です。
使用例
// A pitch of four skips two source bytes at the end of each row.
nes_nt_stream_rect(0x2000,4,8,2,2,4,patch);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:29
Write each rectangle row immediately, advancing source by pitch rather than width. Caller-provided coordinates, storage and timing are not clipped or checked.
kitaqfc/lib/nametable_asset.c:120
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src)
{
unsigned char row;
unsigned short ppu_addr;
row = 0;
while (row != height)
{
ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
nes_ppu_stream_write(ppu_addr, src, width);
src = src + pitch;
row = (unsigned char)(row + 1);
}
}nes_nt_stream_row
1行32タイルをPPUへ直接書き込みます。
書式
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32);引数
nt_base論理ネームテーブル先頭$2000/$2400/$2800/$2C00。物理的な独立性はミラーリング設定によります。
row行番号0~29。範囲検査はありません。
src32読み取り可能な32バイトのタイル番号。
戻り値
戻り値はありません。
使用条件と制限
直接転送はPPUCTRLのアドレス増分を1にし、描画停止中か転送量が収まるVBlankで行います。これらのヘルパーは描画停止も待機もしません。
使用例
nes_nt_stream_row(0x2000,2,row);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
行・pitch付き矩形・塗りつぶし・一括転送を組み合わせます。上に赤い32×8の横棒2本、下に青い16×16、赤い16×24と32×24の矩形が並びます。直接転送とキュー転送で異なる位置へ描きます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/nametable_asset.h:24
Write one complete 32-tile row directly. The caller validates the row and PPU timing.
kitaqfc/lib/nametable_asset.c:103
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32)
{
unsigned short ppu_addr;
ppu_addr = (unsigned short)(nt_base + ((unsigned short)row << 5));
nes_ppu_stream_write(ppu_addr, src32, 32);
}nes_game.h — ゲーム向け操作名のマクロ
nes_game.hは、VRAMキュー、OAM・メタスプライト、コントローラー読み取り、マッパー割り込み、分割スクロール、FDS操作に読みやすいマクロ名を提供します。各マクロはコンパイラの組み込み関数へ展開されるため、診断やKUROSAKIメタデータにも実際のハードウェア操作が記録されます。キュー操作には組み込みキューを使い、OAM DMAマクロは引数を取りません。
使うヘッダーを明確に選んでください。nes_game.hと総合ヘッダーfc.hはマクロを定義します。C関数を使う場合はruntime.hやmetasprite.hと各実装ファイルを組み合わせ、同名マクロを含めないか、先に#undefで解除します。引数なしのDMAマクロにC関数のpage引数を渡すことはできません。
nes_fds_farcall
nes_game.hから使うFDS遠隔呼び出しマクロです。__fds_overlay_farcall((bank), (func))へ展開します。
書式
#define nes_fds_farcall(bank, func) __fds_overlay_farcall((bank), (func))引数
bankバンクを表す式。式は呼び出し前に一度だけ評価しますが、実際にロードするバンクはfuncのリンク時の配置から決まります。任意の番号へ同じアドレスを振り向ける機能ではありません。
__bankof(func)など、対象と一致する値を渡してください。func宣言済みで引数を取らない関数の名前。サンプルは
u8 overlay_answer(void)を渡します。数値アドレス、実行時の関数ポインター、引数を必要とする関数は受け付けません。引数を渡す場合は通常のtarget(a, b)形式を使います。
戻り値
対象関数が返した8ビット値。ロードに失敗して元のバンクを復元できた場合は、対象を呼ばずロードエラーのバイトを返します。そのため、関数の結果とエラーを区別する設計が必要です。
使用条件と制限
--mapper=fds --fds-layout=fds32向けです。バンク0は$A000~$DFFFの共通コード、バンク1は起動時の$6000~$9FFF、バンク2以降は同じ16 KiB窓へ読み込むオーバーレイです。バンク2以降は#pragma fixed_bank 2などで配置します。
バンク1は$6000・16384バイトの独立した起動PRGファイルから復元します。管理表を省略した標準構成ではID 0がバンク1、ID 1が共通コード、ID 2がCHRです。独自の管理表を使う場合は起動PRGを$6000と$A000の16384バイトずつに分けてください。バンク2以降のファイルIDは--fds-overlay-start-id(既定32)+ バンク番号 - 2です。
異なるバンクを呼ぶ場合は、呼び出し前のバンクを保存し、対象の実行後に復元してから戻ります。入れ子の呼び出しも同じ規則です。転送途中の失敗でも復元を試みます。復元そのものが失敗した場合は、上書きされたコードへ戻らず共通コード内で停止します。共通コード内の対象や同一バンクの対象には不要な転送を行いません。
BIOS呼び出しは同期処理です。読み込み中も毎フレームの処理が進むAPIではありません。割り込みハンドラーから呼ばず、共通コードに置いた通常の制御処理から使ってください。表示・音声の継続や実機でのBIOSとの共存は別途確認が必要です。
掲載画像は、独自に作成したLoadFilesの呼び出し規約テスト用処理で、コンパイルしたコード・メモリ転送・画面の全画素を確認したものです。実際のBIOS、ディスク転送タイミング、CRC、実機起動を検証した画像ではありません。KUROSAKIのFDSコントローラーは簡易モデルで、この検証用処理なしの通常実行だけでは同じ転送結果を再現できません。
下記コマンドの最初の行で配布サンプルをビルドします。Pythonの検証コマンドは5本のサンプルを最適化あり・なしでビルドし、KUROSAKIで確認します。検証用処理はscripts/fds_abi_fixture.pyから一時生成して削除するため、外部BIOSを用意する必要はありません。通常のゲームでこの検証用処理を使うものではありません。
使用例
#pragma fixed_bank 0
#include "fc_fds_scene.h"
u8 choose_overlay(void){fds_result[2]++;return 2;}
#pragma fixed_bank 2
u8 overlay_answer(void){fds_result[1]++;fds_result[3]=__fds_current_bank();return 42;}
#pragma fixed_bank 0
void main(void) {
__irq_disable();fds_result[1]=0;fds_result[2]=0;
fds_result[0]=nes_fds_farcall(choose_overlay(),overlay_answer);
fds_result[4]=__fds_current_bank();fds_result[15]=165;
fds_call_screen();while(1){m_wait();}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
独自のLoadFiles呼び出し規約テストでは、RETURN VALUEが042、CALLBACK COUNTとBANK EXPR COUNTが各001、BANK IN CALLBACKが002、BANK AFTER RETURNが001になります。対象とバンク式が一度ずつ実行され、バンク2で関数を呼んだ後にバンク1へ復元したことを表します。実際のディスク転送の検証画像ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\fds_call_alias.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fds_call_alias.fds --mapper=fds --fds-layout=fds32 --fds-no-license-bypass --fds-overlay-trim --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
python .\kitaq-docs\tools\check_fds_load_examples.py実装とソース
kitaqfc/lib/nes_game.h:41
nes_fds_load_bank
nes_game.hが提供する__fds_load_bank()のマクロ名です。ゲーム用ヘッダーの表記で論理バンクを明示的に読み込みます。
書式
#define nes_fds_load_bank(bank) __fds_load_bank((bank))引数
bankロードする論理バンク番号1~254。0は共通コードなのでロード対象ではなく、255は不明状態の予約値です。バンク2以降では、計算したファイルIDも0~254に収まる必要があります。
戻り値
成功時は0。番号0・255、ファイルIDの桁あふれ、復元用ファイルがないバンク1などは255。BIOS転送が失敗した場合はそのエラーバイトです。転送前の引数エラーでは常駐記録を変更せず、BIOS転送失敗時は255(不明)にします。
使用条件と制限
--mapper=fds --fds-layout=fds32向けです。バンク0は$A000~$DFFFの共通コード、バンク1は起動時の$6000~$9FFF、バンク2以降は同じ16 KiB窓へ読み込むオーバーレイです。バンク2以降は#pragma fixed_bank 2などで配置します。
__fds_load_bank((bank))へ展開します。bank式の重複評価はありません。引数範囲、常駐ガード、エラー処理は組み込み関数と同じです。
バンク1は$6000・16384バイトの独立した起動PRGファイルから復元します。管理表を省略した標準構成ではID 0がバンク1、ID 1が共通コード、ID 2がCHRです。独自の管理表を使う場合は起動PRGを$6000と$A000の16384バイトずつに分けてください。バンク2以降のファイルIDは--fds-overlay-start-id(既定32)+ バンク番号 - 2です。
既定の常駐ガードが有効で、指定バンクがすでに記録されていれば、ディスクを読まず0を返します。--fds-no-overlay-guardは__fds_load_bank()のこの省略処理を無効にします。ロード成功後はバンク番号を記録します。記録はメモリの検査結果ではありません。
ロード元と同じ$6000~$9FFF窓に置いた関数から直接バンクを切り替えると、自分のコードや復帰先を上書きします。このサンプルは呼び出し元と表示処理を#pragma fixed_bank 0へ置きます。別バンクの関数を呼んで戻る用途には__fds_overlay_farcall()または通常の型付き関数呼び出しを使います。
BIOS呼び出しは同期処理です。読み込み中も毎フレームの処理が進むAPIではありません。割り込みハンドラーから呼ばず、共通コードに置いた通常の制御処理から使ってください。表示・音声の継続や実機でのBIOSとの共存は別途確認が必要です。
掲載画像は、独自に作成したLoadFilesの呼び出し規約テスト用処理で、コンパイルしたコード・メモリ転送・画面の全画素を確認したものです。実際のBIOS、ディスク転送タイミング、CRC、実機起動を検証した画像ではありません。KUROSAKIのFDSコントローラーは簡易モデルで、この検証用処理なしの通常実行だけでは同じ転送結果を再現できません。
下記コマンドの最初の行で配布サンプルをビルドします。Pythonの検証コマンドは5本のサンプルを最適化あり・なしでビルドし、KUROSAKIで確認します。検証用処理はscripts/fds_abi_fixture.pyから一時生成して削除するため、外部BIOSを用意する必要はありません。通常のゲームでこの検証用処理を使うものではありません。
使用例
#pragma fixed_bank 0
#include "fc_fds_scene.h"
#pragma fixed_bank 2
__prg_rom u8 bank_payload[4]={11,22,33,44};
#pragma fixed_bank 0
void main(void) {
__irq_disable();
fds_result[0]=__fds_load_bank(2);
fds_result[1]=__fds_current_bank();
fds_result[2]=__fds_require_bank(2);
fds_result[3]=bank_payload[0];fds_result[4]=bank_payload[3];
fds_result[5]=nes_fds_load_bank(1);
fds_result[6]=__fds_current_bank();
fds_result[7]=__fds_require_bank(1);
fds_result[8]=__fds_load_bank(0);
fds_result[9]=__fds_load_bank(255);
fds_result[15]=165;
m_init();m_text(2,2,"FDS DATA WINDOW");m_wait();
m_text(2,5,"LOAD BANK 2 STATUS");fds_number(25,5,fds_result[0]);m_wait();
m_text(2,7,"FIRST / LAST BYTE");fds_number(21,7,fds_result[3]);fds_number(25,7,fds_result[4]);m_wait();
m_text(2,9,"REQUIRE SAME BANK");fds_number(25,9,fds_result[2]);m_wait();
m_text(2,11,"RESTORE BANK 1");fds_number(25,11,fds_result[5]);m_wait();
m_text(2,13,"RESIDENT BANK");fds_number(25,13,fds_result[6]);m_wait();
m_text(2,16,"INVALID BANKS 0 / 255");m_wait();
fds_number(2,18,fds_result[8]);fds_number(8,18,fds_result[9]);m_wait();
while(1){m_wait();}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
独自のLoadFiles呼び出し規約テストでは、LOAD BANK 2 STATUS・REQUIRE SAME BANK・RESTORE BANK 1が000です。FIRST / LAST BYTEの011と044は実際に転送した4バイトの両端、RESIDENT BANKの001は復元後の番号です。無効なバンク0と255はそれぞれ255を返します。同一バンクのrequireは追加のロードを起こさず、検証したファイル読み込み要求は32→0の2回です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\fds_bank_load.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fds_bank_load.fds --mapper=fds --fds-layout=fds32 --fds-no-license-bypass --fds-overlay-trim --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
python .\kitaq-docs\tools\check_fds_load_examples.py実装とソース
kitaqfc/lib/nes_game.h:40
nes_fds_wave_load
__fds_wave_loadへ配列を渡すマクロです。64バイトの波形をロードします。
書式
#define nes_fds_wave_load(wave64) __fds_wave_load((wave64))引数
wave64FDS波形の64バイト配列。各値は0~63の6ビット振幅です。NULLは検査しません。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=fdsで.fdsを生成します。サンプルはディスク入出力を使わず音源を操作します。録音はKUROSAKIのRAM直接起動と音源モデルで確認しました。検証用の空のファームウェア領域はBIOS機能を提供せず、配布物にも含みません。通常のディスク起動や実機では別途適切な起動環境が必要です。
使用例
// Mapper 20 sound lesson: original 64-step triangle and two sustained pitches.
#include "fc_sound_example.h"
#include "fds_sound.h"
#include "nes_game.h"
__location(0x4087) u8 mod_control;
__location(0x4085) u8 mod_counter;
u8 triangle[64]; u8 modulation[32];
void main(void) {
u8 i;
sound_begin("FDS WAVE AND VOLUME");
for(i=0;i<32;i++) { triangle[i]=(u8)(i*2); triangle[63-i]=(u8)(i*2); modulation[i]=0; }
__fds_sound_enable();
// Halt/reset modulation before writing the 32 values (each writes two slots).
mod_control=0x80; mod_counter=0;
__fds_mod_load(modulation);
__fds_wave_load(triangle);
sound_result[0]=*((u8*)0x4040); sound_result[1]=*((u8*)0x405F);
__fds_freq_set(1031);
__fds_volume_set(24);
sound_wait(60);
__fds_volume_set(0); sound_wait(30);
// This macro forwards the same 64-byte load. Use raw envelope registers.
nes_fds_wave_load(triangle);
__fds_env_set(0x98,0x80,0);
__fds_freq_set(1545);
sound_wait(60);
__fds_volume_set(0); sound_wait(30);
sound_end();
}期待する結果
独自の64段階三角波で約440Hz、無音、約660Hz、無音の順です。2回目はnes_fds_wave_loadと生のエンベロープ設定を使います。これは音源モデルと直接起動の検証であり、BIOS経由のディスク起動の検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_fds.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-fds.fds --mapper=fds --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:39
#define nes_fds_wave_load(wave64) __fds_wave_load((wave64))nes_mapper_irq_ack
nes_game.hのハンドラー向けACKマクロです。__mapper_irq_ack()へ展開され、要求の解除と無効化を同時に行います。
書式
#define nes_mapper_irq_ack() __mapper_irq_ack()戻り値
戻り値はありません。
使用条件と制限
組み込み関数の宣言はintrinsics.hにあります。nes_mapper_irq_*のマクロにはnes_game.hを読み込みます。マッパーはビルド時の--mapperで選びます。
MMC3専用です。__nes_irqハンドラー、PPUのA12変化、マッパー側の有効化、CPU側の__irq_enable()が必要です。サンプルは背景を$0000、スプライトを$1000のパターンテーブルにして描画を有効にしています。実機での特定ラインへの同期精度は、このサンプルの検証範囲に含みません。
MMC3のACKと無効化はどちらも$E000への書き込みです。要求を解除すると同時に、次の要求も無効になります。繰り返し使う場合は必要なタイミングで__mapper_irq_enable()を呼んでください。CPUの割り込みマスクやNMIの有効状態は変更しません。
独自ハンドラーの末尾にはコンパイラがRTIを生成しますが、A/X/Yや共有一時領域はハンドラー側で必要な分を保存・復元します。サンプルのハンドラーはA/X/Yを保存し、バイトの加算と引数なしのACKだけを行います。別のC関数を追加する場合は、その関数が触る共有領域も確認してください。
使用例
void __nes_irq(void) {
__asm {
PHA
TXA
PHA
TYA
PHA
}
irq_count++;
if (irq_phase == 1) { nes_mapper_irq_ack(); }
else { __mapper_irq_ack(); }
__asm {
PLA
TAY
PLA
TAX
PLA
}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
MMC3のサンプルはCHRのマーカー値19/20、21/22、23/24を表示します。CIRAMは縦配置でA B A B、横配置でA A B Bになります。IRQ回数は01→02→03→03です。最初の3回は明示的な再有効化で1回ずつ増え、無効化後は増えません。各段階で6フレーム待って確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:36
nes_mapper_irq_disable
nes_game.hからMMC3のIRQを無効化し、保留中の要求も解除します。__mapper_irq_disable()へ展開されます。
書式
#define nes_mapper_irq_disable() __mapper_irq_disable()戻り値
戻り値はありません。
使用条件と制限
組み込み関数の宣言はintrinsics.hにあります。nes_mapper_irq_*のマクロにはnes_game.hを読み込みます。マッパーはビルド時の--mapperで選びます。
MMC3専用です。__nes_irqハンドラー、PPUのA12変化、マッパー側の有効化、CPU側の__irq_enable()が必要です。サンプルは背景を$0000、スプライトを$1000のパターンテーブルにして描画を有効にしています。実機での特定ラインへの同期精度は、このサンプルの検証範囲に含みません。
MMC3のACKと無効化はどちらも$E000への書き込みです。要求を解除すると同時に、次の要求も無効になります。繰り返し使う場合は必要なタイミングで__mapper_irq_enable()を呼んでください。CPUの割り込みマスクやNMIの有効状態は変更しません。
独自ハンドラーの末尾にはコンパイラがRTIを生成しますが、A/X/Yや共有一時領域はハンドラー側で必要な分を保存・復元します。サンプルのハンドラーはA/X/Yを保存し、バイトの加算と引数なしのACKだけを行います。別のC関数を追加する場合は、その関数が触る共有領域も確認してください。
使用例
nes_mapper_irq_disable();
for (i=0;i<6;i++) { m_wait(); }
mapper_result[18]=irq_count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
MMC3のサンプルはCHRのマーカー値19/20、21/22、23/24を表示します。CIRAMは縦配置でA B A B、横配置でA A B Bになります。IRQ回数は01→02→03→03です。最初の3回は明示的な再有効化で1回ずつ増え、無効化後は増えません。各段階で6フレーム待って確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:35
nes_mapper_irq_enable
nes_game.hからMMC3のIRQを有効にします。__mapper_irq_enable()へ展開され、CPUの割り込みマスクは変えません。
書式
#define nes_mapper_irq_enable() __mapper_irq_enable()戻り値
戻り値はありません。
使用条件と制限
組み込み関数の宣言はintrinsics.hにあります。nes_mapper_irq_*のマクロにはnes_game.hを読み込みます。マッパーはビルド時の--mapperで選びます。
MMC3専用です。__nes_irqハンドラー、PPUのA12変化、マッパー側の有効化、CPU側の__irq_enable()が必要です。サンプルは背景を$0000、スプライトを$1000のパターンテーブルにして描画を有効にしています。実機での特定ラインへの同期精度は、このサンプルの検証範囲に含みません。
MMC3のACKと無効化はどちらも$E000への書き込みです。要求を解除すると同時に、次の要求も無効になります。繰り返し使う場合は必要なタイミングで__mapper_irq_enable()を呼んでください。CPUの割り込みマスクやNMIの有効状態は変更しません。
独自ハンドラーの末尾にはコンパイラがRTIを生成しますが、A/X/Yや共有一時領域はハンドラー側で必要な分を保存・復元します。サンプルのハンドラーはA/X/Yを保存し、バイトの加算と引数なしのACKだけを行います。別のC関数を追加する場合は、その関数が触る共有領域も確認してください。
使用例
nes_mapper_irq_set(15); nes_mapper_irq_enable();
for (i=0;i<6;i++) { m_wait(); }
mapper_result[16]=irq_count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
MMC3のサンプルはCHRのマーカー値19/20、21/22、23/24を表示します。CIRAMは縦配置でA B A B、横配置でA A B Bになります。IRQ回数は01→02→03→03です。最初の3回は明示的な再有効化で1回ずつ増え、無効化後は増えません。各段階で6フレーム待って確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:34
nes_mapper_irq_set
nes_game.hから使うMMC3ラッチ設定マクロです。引数を__mapper_irq_setへ1回渡して再ロードを要求します。
書式
#define nes_mapper_irq_set(scanline) __mapper_irq_set((scanline))引数
scanline0~255の8ビットカウンターラッチ値です。画面上の絶対Y座標ではありません。再ロードのタイミングとPPUのA12変化によって発生位置が決まります。0も書き込めますが、割り込み停止の意味ではありません。
戻り値
戻り値はありません。
使用条件と制限
組み込み関数の宣言はintrinsics.hにあります。nes_mapper_irq_*のマクロにはnes_game.hを読み込みます。マッパーはビルド時の--mapperで選びます。
MMC3専用です。__nes_irqハンドラー、PPUのA12変化、マッパー側の有効化、CPU側の__irq_enable()が必要です。サンプルは背景を$0000、スプライトを$1000のパターンテーブルにして描画を有効にしています。実機での特定ラインへの同期精度は、このサンプルの検証範囲に含みません。
MMC3のACKと無効化はどちらも$E000への書き込みです。要求を解除すると同時に、次の要求も無効になります。繰り返し使う場合は必要なタイミングで__mapper_irq_enable()を呼んでください。CPUの割り込みマスクやNMIの有効状態は変更しません。
独自ハンドラーの末尾にはコンパイラがRTIを生成しますが、A/X/Yや共有一時領域はハンドラー側で必要な分を保存・復元します。サンプルのハンドラーはA/X/Yを保存し、バイトの加算と引数なしのACKだけを行います。別のC関数を追加する場合は、その関数が触る共有領域も確認してください。
使用例
nes_mapper_irq_set(15); nes_mapper_irq_enable();
for (i=0;i<6;i++) { m_wait(); }
mapper_result[16]=irq_count;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
MMC3のサンプルはCHRのマーカー値19/20、21/22、23/24を表示します。CIRAMは縦配置でA B A B、横配置でA A B Bになります。IRQ回数は01→02→03→03です。最初の3回は明示的な再有効化で1回ずつ増え、無効化後は増えません。各段階で6フレーム待って確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:33
nes_oam_clear
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
ページ02のシャドーバッファにある64個のスプライトすべてのYを0xF0にして非表示にします。タイル番号・属性・X座標は変更しません。
書式
#define nes_oam_clear() __oam_clear()戻り値
ありません(void)。
使用条件と制限
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
使用例
nes_sprite_set(63,144,111,128,0); // Seed the CLEAR row with a visible entry.
nes_oam_clear(); // Set every Y byte to 240; tile, attributes and X are retained.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:21
#define nes_oam_clear() __oam_clear()nes_oam_dma
同じ名前に2つの形式があります。引数なしのマクロnes_oam_dma()は__oam_dma()を通じてページ02を転送します。C関数nes_oam_dma(page)は指定した256バイトのCPUメモリページを転送します。どちらもOAMADDRを0にしてページ全体を転送し、転送元は変更しません。
書式
#define nes_oam_dma() __oam_dma()
// C function declared in runtime.h:
void nes_oam_dma(unsigned char page);引数
page (C)転送元CPUアドレスの上位バイトです。
0x0400–0x04FFなら0x0400ではなく4を渡します。256バイトすべてが読み出せるページを用意し、必要なバンクを選択しておいてください。読み出しに副作用があるI/Oレジスタを含むページは避けます。転送元の検査は行いません。
戻り値
ありません(void)。
使用条件と制限
使うヘッダーを明確に選んでください。nes_game.hと総合ヘッダーfc.hはマクロを定義します。C関数を使う場合はruntime.hやmetasprite.hと各実装ファイルを組み合わせ、同名マクロを含めないか、先に#undefで解除します。引数なしのDMAマクロにC関数のpage引数を渡すことはできません。
C版メタスプライト関数はnes_oam_shadow[256]を必要とします。runtime.cはこれを0x0200に定義し、VRAMキュー用に0x0300–0x03BFも確保して、__nes_nmiを提供します。各実装は一度だけ組み込み、割り込み処理をプログラム全体と整合させてください。
DMAはVBlank中、または描画を無効にした状態で呼びます。どちらの形式もVBlankを待ちません。転送元ページ全体を準備してから転送し、準備途中で割り込みがそのページを変更しないようにしてください。
使用例
m_wait();nes_oam_dma(); // Expand to the page-02 compiler intrinsic.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm使用例
m_wait();nes_oam_dma(4); // C function: source address high byte, 0x0400 -> 4.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
C-RUNTIMEはC関数を組み合わせて使う例です。PAIRには(144,32)と(152,32)に8×8矢印が並び、2本目は左右反転します。y=64の別の2本は隠すため、HIDE RANGEは空欄になります。描画でスロット4–5、6–7を順に使い、NEXT 08を表示します。ページ02をページ04へコピーし、スロット8だけ矢印のタイル128から四角の129へ変更します。nes_oam_dma(4)によりPAGE 04の横の(144,96)に緑の四角が表示され、ページ02にはタイル128が残ります。
矢印は赤い縁・緑の軸・先端の青い細部で構成し、四角のタイル129は緑です。検証では各画像の8,704ピクセルの色、転送したOAM全256バイト、表示ラベルを照合します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:23
kitaqfc/lib/runtime.c:245
void nes_oam_dma(unsigned char page)
{
OAMADDR = 0;
OAMDMA = page;
}nes_pad1
__pad_read1_safe() に展開される関数形式マクロです。コントローラ1を読み、NESの生のマスクを返します。保存済み状態を読む input_current() の別名ではありません。
書式
#define nes_pad1() __pad_read1_safe()戻り値
NESの生のビット順による8ビットの押下中マスクです。各ビット1が押されているボタンを表し、0はなしです。0/1に正規化された真偽値ではありません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
この呼び出しはハードウェアの状態を新たに読みます。input_* や nes_pad1_* の保存状態は更新せず、押下・解放イベントやリピートも生成しません。必要なら読み取った値を保存し、自分で保持した前回マスクと比較して変化を求めてください。
safe版は全ボタンを2回ずつ読み、二つのマスクが一致するまで繰り返します。各読込で $4016 をストローブし、両コントローラのシリアル読込位置を先頭へ戻します。再試行回数に上限はありません。この一致確認だけで、すべてのDMC・周辺機器との組み合わせを保証するわけではありません。
使用例
alias1=nes_pad1(); // Macro for __pad_read1_safe(), using NES raw bits.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
2つのコントローラーポート、通常読み取りと一致確認付き読み取り、マスク抽出、短縮マクロを比較するプログラムです。コントローラー1ではAと右、コントローラー2ではBと左を同時に押します。RAW P1、SAFE P1、ALIAS P1は129、P2の3項目は66になります。BUTTONS P1は1、DIRS P1は128です。方向の抽出では上位ビットの位置を保ちます。FAILED CHECKSは0が正常です。取得した値は画面に残ります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\input_raw.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_raw.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/nes_game.h:30
nes_pad2
__pad_read2_safe() に展開される関数形式マクロです。コントローラ2を読み、NESの生のマスクを返します。保存済み状態を読む input_current() の別名ではありません。
書式
#define nes_pad2() __pad_read2_safe()戻り値
NESの生のビット順による8ビットの押下中マスクです。各ビット1が押されているボタンを表し、0はなしです。0/1に正規化された真偽値ではありません。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
この呼び出しはハードウェアの状態を新たに読みます。input_* や nes_pad1_* の保存状態は更新せず、押下・解放イベントやリピートも生成しません。必要なら読み取った値を保存し、自分で保持した前回マスクと比較して変化を求めてください。
safe版は全ボタンを2回ずつ読み、二つのマスクが一致するまで繰り返します。各読込で $4016 をストローブし、両コントローラのシリアル読込位置を先頭へ戻します。再試行回数に上限はありません。この一致確認だけで、すべてのDMC・周辺機器との組み合わせを保証するわけではありません。
使用例
alias2=nes_pad2(); // Macro for __pad_read2_safe(), using NES raw bits.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
2つのコントローラーポート、通常読み取りと一致確認付き読み取り、マスク抽出、短縮マクロを比較するプログラムです。コントローラー1ではAと右、コントローラー2ではBと左を同時に押します。RAW P1、SAFE P1、ALIAS P1は129、P2の3項目は66になります。BUTTONS P1は1、DIRS P1は128です。方向の抽出では上位ビットの位置を保ちます。FAILED CHECKSは0が正常です。取得した値は画面に残ります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\input_raw.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_raw.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/nes_game.h:31
nes_split_scroll_sprite0
nes_game.hから使える画面分割マクロです。XとYを1回ずつ__split_scroll_sprite0へ渡し、同じ待機とスクロール書き込みを行います。
書式
#define nes_split_scroll_sprite0(x,y) __split_scroll_sprite0((x),(y))引数
x横スクロール値0~255。画面内の物体のX座標ではありません。サンプルでは32を指定し、同じ背景の下部を左へ32ピクセル移動して表示します。
y縦スクロールの一時設定値0~255。通常の表示範囲は0~239です。このヘルパーは$2005へ書くため、描画中の縦位置を直ちに変更する機能ではありません。横分割のサンプルでは0を渡します。
戻り値
戻り値はありません。
使用条件と制限
OAMの0番スプライトと背景の不透明なピクセルを重ねます。背景・スプライトの描画を両方有効にしてください。スプライトの「背景の後ろ」属性を付けてもヒットします。左端8ピクセルは該当する表示許可が必要で、X=255の重なりはヒットしません。
PPUSTATUS($2002)のビット6が0になるまで待ち、続いて1になるまで待ちます。呼び出した時点ですでにヒット済みなら、次のフレームの新しいヒットを待ちます。フラグは読み取りでは解除されず、PPUが次のフレームの準備中に解除します。
タイムアウトも失敗の戻り値もありません。0番スプライトが非表示、背景が透明、描画が無効など、重なりが発生しない設定では戻りません。呼ぶ前にヒット条件を保証してください。CPUのIRQ許可は不要で、この関数自身はNMIを有効化しません。サンプルのフレーム待機にはNMIを有効にしています。
$2002を読むので、PPUのアドレス/スクロール共有ラッチとVBlankフラグにも影響します。途中まで書いた$2005/$2006の2バイト操作を、この呼び出しを挟んで続けないでください。NMI/IRQハンドラーから呼ばず、前景ループで使います。
ヒット後に$2002を読み、$2005へX、Yの順に書きます。細かいX位置はすぐに変わり、タイル単位のX位置は次の水平再ロードで反映されます。Yは通常、次のプリレンダーで反映されますが、それまでに別のスクロール設定で上書きされることがあります。任意のX/Yへ即座に飛ばすラスタースクロール関数としては使わないでください。
書き込み位置にはポーリングと命令実行の遅れがあります。KUROSAKIは走査線単位で描画するため、実機のドット単位の境界位置をこの画像だけで保証しません。サンプルではY=96~103の白い帯の中に切り替えを収め、境界付近の誤差が青・赤の帯へ出ない構成にしています。
共有ヘッダーfc_sprite0_scene.hが独自の青・赤の帯と白い横帯を準備します。0番スプライトは画面(16,96)に置き、背景の後ろへ隠しています。OAMのYには表示位置−1の95を指定します。各フレームのVBlankで上部のスクロールを0へ戻してから、ヒットを待ちます。
NESdev: PPU programmer reference
使用例
void main(void) {
s0_prepare();
while (1) {
s0_frame();
nes_split_scroll_sprite0(32,0);
s0_completed++;
}
}呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
白い横帯より上では青がX=64~95、赤がX=160~191です。Y=104以降の下部では青がX=32~63、赤がX=128~159になり、下部だけ左へ32ピクセルずれます。横帯はY=96~103、縦帯の下端はY=207です。毎フレーム1回ずつ処理が完了することと、画像全体の色・位置を検証しています。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite0_split_alias.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite0_split_alias.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\sprite0_scene.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:37
nes_sprite_hide
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
1個のシャドースプライトのYを0xF0にして非表示にします。X・タイル・属性は残るので再利用できます。スロットの解放やスプライトライブラリの使用数の変更は行いません。
書式
#define nes_sprite_hide(i) __sprite_hide((i))引数
iスプライトの項目番号0〜63です。バイト単位のオフセットではありません。内部では8ビットのカーソルで4倍するため、範囲外の値は64を周期に折り返し、別の項目を上書きします。確保済みかどうかの検査は行いません。
戻り値
ありません(void)。
使用条件と制限
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
使用例
nes_sprite_hide(4); // The HIDE row remains empty after DMA.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:26
#define nes_sprite_hide(i) __sprite_hide((i))nes_sprite_move
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
シャドーバッファ内の1個のスプライトのX・Yだけを変更します。タイル番号と属性を保持するため、パレットや反転状態を変えずに同じ絵を移動できます。
書式
#define nes_sprite_move(i,x,y) __sprite_move((i),(x),(y))引数
iスプライトの項目番号0〜63です。バイト単位のオフセットではありません。内部では8ビットのカーソルで4倍するため、範囲外の値は64を周期に折り返し、別の項目を上書きします。確保済みかどうかの検査は行いません。
xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
yOAMに格納するY値です。画面の1行目以降に上端を置く場合は表示上端−1を渡します。239〜255は240行の表示範囲外です。
戻り値
ありません(void)。
使用条件と制限
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
使用例
nes_sprite_move(1,144,31); // Move to (144,32), retaining tile 128 and attributes 0.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:25
#define nes_sprite_move(i,x,y) __sprite_move((i),(x),(y))nes_sprite_set
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
1個のスプライトの4項目をページ02のシャドーバッファへ書き込みます。引数の順序は番号・X・Y・タイル・属性ですが、格納順はY・タイル・属性・Xです。
書式
#define nes_sprite_set(i,x,y,t,a) __sprite_set((i),(x),(y),(t),(a))引数
iスプライトの項目番号0〜63です。バイト単位のオフセットではありません。内部では8ビットのカーソルで4倍するため、範囲外の値は64を周期に折り返し、別の項目を上書きします。確保済みかどうかの検査は行いません。
xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
yOAMに格納するY値です。画面の1行目以降に上端を置く場合は表示上端−1を渡します。239〜255は240行の表示範囲外です。
t8ビットのタイル番号です。画像を先に読み込んでください。8×16スプライトのハードウェア規則はそのまま適用され、2枚組に合わせた番号補正は行いません。
a属性バイト全体です。ビット0〜1でスプライトパレット0〜3、ビット5で不透明な背景の後ろに表示する指定、ビット6で左右反転、ビット7で上下反転を設定します。ビット2〜4は0にしてください。フラグはビットORで組み合わせ、渡したバイトで属性全体を置き換えます。
戻り値
ありません(void)。
使用条件と制限
これらの操作はCPU RAMの0x0200–0x02FFを書き換えます。ハードウェアOAMへ即座に反映されるわけではありません。ページを完成させてから__oam_dmaで転送してください。スプライトライブラリの確保フラグ・座標配列・使用数は更新しません。準備途中のページを割り込み処理で変更・転送しないようにします。
使用例
nes_sprite_set(0,144,15,128,0); // An 8x8 arrow at visible position (144,16).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
操作ごとに見出し付きの行を分けたサンプルです。SETとMOVEは(144,16)・(144,32)の8×8右向き矢印、TILEは(144,48)の緑の四角、ATTRは(144,64)の上下左右を反転した矢印です。HIDEの(144,80)とCLEARの(144,112)には何も表示されません。METAは(136,96)と(144,96)の2個組で、2個目を左右反転します。NEXT 07は5・6番へ書き込んだ後の次の位置です。矢印の輪郭は赤、軸は緑、先端の一部は青です。検証では画像ごとに8,704ピクセルと転送後のOAMデータを照合します。実機では未検証です。
ALIASESのプログラムはnes_game.hのマクロで各行を準備し、nes_oam_dma()でページ02を送ります。TRANSFERには(144,128)の8×8矢印が表示されます。runtime.cやmetasprite.cを組み込まずに使うマクロ版の例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:24
#define nes_sprite_set(i,x,y,t,a) __sprite_set((i),(x),(y),(t),(a))nes_vram_clear_queue
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
組み込みキューの実行待ち記録をすべて破棄し、長さと準備完了フラグを0にします。VRAMへの書き込みや、オーバーフロー通知の解除は行いません。通知を解除するには__vramq_clear_overflowを別に呼びます。
書式
#define nes_vram_clear_queue() __vramq_clear()戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
これらのマクロは、lib/vram.cも使うコンパイラの組み込みコマンドキューを操作します。Cランタイムのnes_vram_queue_*用バッファを選ぶものではありません。保留中の符号化済みバイト数は__vramq_len()、容量不足は__vramq_overflow()で調べ、失敗フラグは__vramq_clear_overflow()で解除します。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
nes_vram_clear_queue();
if (__vramq_len()!=0 || __vramq_overflow()!=1) failures++;
// Clearing queued work deliberately leaves the overflow latch set.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成サンプルは、1バイト追加・コピー・塗りつぶし・コミット・キュー消去にnes_game.hのマクロを使います。効果の確認には__vramq_*の照会・実行用組み込み関数も使います。画面タイトルはVRAM QUEUE MACROSです。大きな転送は描画を無効にして実行します。コミットするだけで大きな転送がVBlank内に収まるようになるわけではありません。
完成プログラムでは、キュー容量、保持されるポインター、2種類のリセット、直接実行とNMI実行の違いを学べます。期待値はCAPACITY 128、ENCODED BYTES 030、AFTER EXEC 000、OVERFLOW SEEN 001、FAILED CHECKS 000です。6つの記録が30バイトを使い、512バイト分の書き込みを実行します。OVERFLOW SEENは意図的な登録拒否時に保存した値であり、終了時も通知が立っているという意味ではありません。
青いコピー領域はピクセル座標(0,16)、緑の埋め込み領域は(0,88)から始まります。それぞれ、幅256ピクセルのタイル行7行と、8行目の248ピクセルを描き、合計255タイルになります。最後の8×8セルは黒いままです。コピーは8×8の塗りつぶし・左半分・三角形を繰り返しますが、登録後に転送元を変えるため、先頭は三角形です。埋め込みは呼び出し元の変数を変えても塗りつぶしを保ちます。形と色の両方を検証します。
y=160では、x=16に赤い塗りつぶしの正方形、x=224に赤い左半分のタイル、x=240に赤い塗りつぶしの正方形が現れます。最初の正方形は長さ0のコピーと埋め込みで消えず、中央は標準NMIでの実行、最後は単発書き込みを確認するものです。取り消したセル(248,160)は黒いままです。長い転送は描画とNMIを止めて実行し、最後の小さな書き込みだけをNMIに任せます。これはNROMのエミュレータ検証であり、実機検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:19
#define nes_vram_clear_queue() __vramq_clear()nes_vram_commit
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
実行側がキューを処理できるよう、準備完了フラグを1にします。転送データのコピー、PPUへの書き込み、NMI待ちは行いません。コミット後は、実行が終わるまでキューと保持した転送元を変更しないでください。NMIが有効なら、標準ハンドラーが非同期に処理する場合があります。
書式
#define nes_vram_commit() __vramq_commit()戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
これらのマクロは、lib/vram.cも使うコンパイラの組み込みコマンドキューを操作します。Cランタイムのnes_vram_queue_*用バッファを選ぶものではありません。保留中の符号化済みバイト数は__vramq_len()、容量不足は__vramq_overflow()で調べ、失敗フラグは__vramq_clear_overflow()で解除します。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
nes_vram_commit(); // NMI is disabled here: this only marks the queue ready.
if (__vramq_len()!=30) failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成サンプルは、1バイト追加・コピー・塗りつぶし・コミット・キュー消去にnes_game.hのマクロを使います。効果の確認には__vramq_*の照会・実行用組み込み関数も使います。画面タイトルはVRAM QUEUE MACROSです。大きな転送は描画を無効にして実行します。コミットするだけで大きな転送がVBlank内に収まるようになるわけではありません。
完成プログラムでは、キュー容量、保持されるポインター、2種類のリセット、直接実行とNMI実行の違いを学べます。期待値はCAPACITY 128、ENCODED BYTES 030、AFTER EXEC 000、OVERFLOW SEEN 001、FAILED CHECKS 000です。6つの記録が30バイトを使い、512バイト分の書き込みを実行します。OVERFLOW SEENは意図的な登録拒否時に保存した値であり、終了時も通知が立っているという意味ではありません。
青いコピー領域はピクセル座標(0,16)、緑の埋め込み領域は(0,88)から始まります。それぞれ、幅256ピクセルのタイル行7行と、8行目の248ピクセルを描き、合計255タイルになります。最後の8×8セルは黒いままです。コピーは8×8の塗りつぶし・左半分・三角形を繰り返しますが、登録後に転送元を変えるため、先頭は三角形です。埋め込みは呼び出し元の変数を変えても塗りつぶしを保ちます。形と色の両方を検証します。
y=160では、x=16に赤い塗りつぶしの正方形、x=224に赤い左半分のタイル、x=240に赤い塗りつぶしの正方形が現れます。最初の正方形は長さ0のコピーと埋め込みで消えず、中央は標準NMIでの実行、最後は単発書き込みを確認するものです。取り消したセル(248,160)は黒いままです。長い転送は描画とNMIを止めて実行し、最後の小さな書き込みだけをNMIに任せます。これはNROMのエミュレータ検証であり、実機検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:18
#define nes_vram_commit() __vramq_commit()nes_vram_copy
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
実行時にsrcからppu_addrへlenバイトをコピーする、6バイトの記録を1つ追加します。キューに保存するのは転送データではなく転送元ポインターです。255バイトのコピーでもコマンドバッファーの使用量は6バイトです。転送元のアドレスが増える順に読み、転送先を連続させるにはPPUの増分を1にします。
書式
#define nes_vram_copy(addr, src, len) __vramq_copy((addr), (src), (len))引数
addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
lenバイト数で、範囲は0~255です。1つの記録で全体を扱い、127バイトずつには分割しません。0でもコピーや埋め込みの記録を使います。実行時にはPPUアドレスを設定しますが、PPUDATAへの書き込みは行いません。256はこの引数の範囲外です。
戻り値
ありません(void)。
使用条件と制限
追加処理は成否の値を返しません。記録全体が収まらない場合は__vramq_overflow()を1にし、既存の記録と長さは保ちます。空き容量と記録サイズが等しい場合は登録できます。以前の通知が残っていても、空きがあれば追加できるため、新しい一連の登録を判断する前に通知を処理・解除してください。複数回の追加をまとめて取り消す仕組みはありません。
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
これらのマクロは、lib/vram.cも使うコンパイラの組み込みコマンドキューを操作します。Cランタイムのnes_vram_queue_*用バッファを選ぶものではありません。保留中の符号化済みバイト数は__vramq_len()、容量不足は__vramq_overflow()で調べ、失敗フラグは__vramq_clear_overflow()で解除します。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
nes_vram_copy(0x2040,source_bytes,255);
source_bytes[0]=3; // The executor must see this change through its retained pointer.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成サンプルは、1バイト追加・コピー・塗りつぶし・コミット・キュー消去にnes_game.hのマクロを使います。効果の確認には__vramq_*の照会・実行用組み込み関数も使います。画面タイトルはVRAM QUEUE MACROSです。大きな転送は描画を無効にして実行します。コミットするだけで大きな転送がVBlank内に収まるようになるわけではありません。
完成プログラムでは、キュー容量、保持されるポインター、2種類のリセット、直接実行とNMI実行の違いを学べます。期待値はCAPACITY 128、ENCODED BYTES 030、AFTER EXEC 000、OVERFLOW SEEN 001、FAILED CHECKS 000です。6つの記録が30バイトを使い、512バイト分の書き込みを実行します。OVERFLOW SEENは意図的な登録拒否時に保存した値であり、終了時も通知が立っているという意味ではありません。
青いコピー領域はピクセル座標(0,16)、緑の埋め込み領域は(0,88)から始まります。それぞれ、幅256ピクセルのタイル行7行と、8行目の248ピクセルを描き、合計255タイルになります。最後の8×8セルは黒いままです。コピーは8×8の塗りつぶし・左半分・三角形を繰り返しますが、登録後に転送元を変えるため、先頭は三角形です。埋め込みは呼び出し元の変数を変えても塗りつぶしを保ちます。形と色の両方を検証します。
y=160では、x=16に赤い塗りつぶしの正方形、x=224に赤い左半分のタイル、x=240に赤い塗りつぶしの正方形が現れます。最初の正方形は長さ0のコピーと埋め込みで消えず、中央は標準NMIでの実行、最後は単発書き込みを確認するものです。取り消したセル(248,160)は黒いままです。長い転送は描画とNMIを止めて実行し、最後の小さな書き込みだけをNMIに任せます。これはNROMのエミュレータ検証であり、実機検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:16
#define nes_vram_copy(addr, src, len) __vramq_copy((addr), (src), (len))nes_vram_fill
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
1バイトのvalueをPPUへlen回書く、5バイトの記録を1つ追加します。値は記録内へ保存されるため、呼び出し元の変数を後で変えても登録済みの値は変わりません。繰り返すのは1バイトであり、複数バイトのタイルパターンではありません。
書式
#define nes_vram_fill(addr, value, len) __vramq_fill((addr), (value), (len))引数
addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
lenバイト数で、範囲は0~255です。1つの記録で全体を扱い、127バイトずつには分割しません。0でもコピーや埋め込みの記録を使います。実行時にはPPUアドレスを設定しますが、PPUDATAへの書き込みは行いません。256はこの引数の範囲外です。
戻り値
ありません(void)。
使用条件と制限
追加処理は成否の値を返しません。記録全体が収まらない場合は__vramq_overflow()を1にし、既存の記録と長さは保ちます。空き容量と記録サイズが等しい場合は登録できます。以前の通知が残っていても、空きがあれば追加できるため、新しい一連の登録を判断する前に通知を処理・解除してください。複数回の追加をまとめて取り消す仕組みはありません。
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
これらのマクロは、lib/vram.cも使うコンパイラの組み込みコマンドキューを操作します。Cランタイムのnes_vram_queue_*用バッファを選ぶものではありません。保留中の符号化済みバイト数は__vramq_len()、容量不足は__vramq_overflow()で調べ、失敗フラグは__vramq_clear_overflow()で解除します。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
nes_vram_fill(0x2160,fill_value,255); // Seven complete rows and 31 tiles in the eighth.
fill_value=3; // The queued value stays 1, so the green block remains solid.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成サンプルは、1バイト追加・コピー・塗りつぶし・コミット・キュー消去にnes_game.hのマクロを使います。効果の確認には__vramq_*の照会・実行用組み込み関数も使います。画面タイトルはVRAM QUEUE MACROSです。大きな転送は描画を無効にして実行します。コミットするだけで大きな転送がVBlank内に収まるようになるわけではありません。
完成プログラムでは、キュー容量、保持されるポインター、2種類のリセット、直接実行とNMI実行の違いを学べます。期待値はCAPACITY 128、ENCODED BYTES 030、AFTER EXEC 000、OVERFLOW SEEN 001、FAILED CHECKS 000です。6つの記録が30バイトを使い、512バイト分の書き込みを実行します。OVERFLOW SEENは意図的な登録拒否時に保存した値であり、終了時も通知が立っているという意味ではありません。
青いコピー領域はピクセル座標(0,16)、緑の埋め込み領域は(0,88)から始まります。それぞれ、幅256ピクセルのタイル行7行と、8行目の248ピクセルを描き、合計255タイルになります。最後の8×8セルは黒いままです。コピーは8×8の塗りつぶし・左半分・三角形を繰り返しますが、登録後に転送元を変えるため、先頭は三角形です。埋め込みは呼び出し元の変数を変えても塗りつぶしを保ちます。形と色の両方を検証します。
y=160では、x=16に赤い塗りつぶしの正方形、x=224に赤い左半分のタイル、x=240に赤い塗りつぶしの正方形が現れます。最初の正方形は長さ0のコピーと埋め込みで消えず、中央は標準NMIでの実行、最後は単発書き込みを確認するものです。取り消したセル(248,160)は黒いままです。長い転送は描画とNMIを止めて実行し、最後の小さな書き込みだけをNMIに任せます。これはNROMのエミュレータ検証であり、実機検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:17
#define nes_vram_fill(addr, value, len) __vramq_fill((addr), (value), (len))nes_vram_put
nes_game.hのマクロです。構文欄の組み込み関数へ展開されます。このヘッダーをインクルードすれば使え、この操作のためにC実装ファイルを追加する必要はありません。
PPUアドレスと1バイトの値を、4バイトの記録として追加します。値はその場でキューへコピーされ、実行時にPPUへ書かれます。ネームテーブルのタイル1個の更新に使えるほか、書き込み可能な他のPPUバイトも指定できます。
書式
#define nes_vram_put(addr, value) __vramq_put((addr), (value))引数
addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
戻り値
ありません(void)。
使用条件と制限
追加処理は成否の値を返しません。記録全体が収まらない場合は__vramq_overflow()を1にし、既存の記録と長さは保ちます。空き容量と記録サイズが等しい場合は登録できます。以前の通知が残っていても、空きがあれば追加できるため、新しい一連の登録を判断する前に通知を処理・解除してください。複数回の追加をまとめて取り消す仕組みはありません。
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
これらのマクロは、lib/vram.cも使うコンパイラの組み込みコマンドキューを操作します。Cランタイムのnes_vram_queue_*用バッファを選ぶものではありません。保留中の符号化済みバイト数は__vramq_len()、容量不足は__vramq_overflow()で調べ、失敗フラグは__vramq_clear_overflow()で解除します。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
使用例
nes_vram_put(0x2282,1); // Red 8x8 sentinel at pixel (16,160).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
完成サンプルは、1バイト追加・コピー・塗りつぶし・コミット・キュー消去にnes_game.hのマクロを使います。効果の確認には__vramq_*の照会・実行用組み込み関数も使います。画面タイトルはVRAM QUEUE MACROSです。大きな転送は描画を無効にして実行します。コミットするだけで大きな転送がVBlank内に収まるようになるわけではありません。
完成プログラムでは、キュー容量、保持されるポインター、2種類のリセット、直接実行とNMI実行の違いを学べます。期待値はCAPACITY 128、ENCODED BYTES 030、AFTER EXEC 000、OVERFLOW SEEN 001、FAILED CHECKS 000です。6つの記録が30バイトを使い、512バイト分の書き込みを実行します。OVERFLOW SEENは意図的な登録拒否時に保存した値であり、終了時も通知が立っているという意味ではありません。
青いコピー領域はピクセル座標(0,16)、緑の埋め込み領域は(0,88)から始まります。それぞれ、幅256ピクセルのタイル行7行と、8行目の248ピクセルを描き、合計255タイルになります。最後の8×8セルは黒いままです。コピーは8×8の塗りつぶし・左半分・三角形を繰り返しますが、登録後に転送元を変えるため、先頭は三角形です。埋め込みは呼び出し元の変数を変えても塗りつぶしを保ちます。形と色の両方を検証します。
y=160では、x=16に赤い塗りつぶしの正方形、x=224に赤い左半分のタイル、x=240に赤い塗りつぶしの正方形が現れます。最初の正方形は長さ0のコピーと埋め込みで消えず、中央は標準NMIでの実行、最後は単発書き込みを確認するものです。取り消したセル(248,160)は黒いままです。長い転送は描画とNMIを止めて実行し、最後の小さな書き込みだけをNMIに任せます。これはNROMのエミュレータ検証であり、実機検証ではありません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/nes_game.h:15
#define nes_vram_put(addr, value) __vramq_put((addr), (value))oam_fair.h — 優先順位付きスプライト交替表示
64項目のソフトウェアプールから有効な項目をOAMシャドウバッファへ追加します。呼び出すたびに、走査の開始位置を13進めて64で折り返します。OAMの優先順を巡回させ、繰り返し呼ぶことでスプライトのちらつきを分散できます。ハードウェアの表示上限が増えるわけではありません。
X・Y・active・shadowの各配列とusedカーソルは呼び出し側で定義し、固定PRGバンクにoam_fair_impl.hを一度だけ組み込みます。8×8スプライトモードでメインループから呼んでください。座標はクリッピングせず256で折り返します。この処理自体はOAMを転送しないため、shadowに対応したページをDMA転送します。ハードウェアの上限はOAM全体64個、走査線あたり8個です。
OAM_FairDraw
64項目のソフトウェアプールから有効な項目をOAMシャドウバッファへ追加します。呼び出すたびに、走査の開始位置を13進めて64で折り返します。OAMの優先順を巡回させ、繰り返し呼ぶことでスプライトのちらつきを分散できます。ハードウェアの表示上限が増えるわけではありません。
書式
void OAM_FairDraw(void);戻り値
Cの戻り値はありません。oam_fair_drawnに今回追加した項目数、oam_fair_usedに更新後のバイト位置、oam_fair_phaseに今回の走査開始番号が入ります。1項目も描画しない場合もphaseは進み、その場合のoam_fair_drawnは0になります。
使用条件と制限
関数の引数はなく、共有変数で設定します。oam_fair_x[64]とoam_fair_y[64]は8×8スプライトの中心座標をピクセルで保持します。生のOAM値はXが中心X−4、Yが中心Y−5です。oam_fair_active[64]の非0項目を描画対象とし、oam_fair_tileとoam_fair_attrを全項目共通のタイル・属性として使います。
oam_fair_usedにはoam_fair_shadow[256]内の次の空きバイト位置を4バイト境界で設定します。oam_fair_limitは今回追加してよい項目数の上限です。プールは最大64項目を調べ、書き込み位置が252になったら停止してスロット63を空けておきます。カーソルより前の項目は保持し、末尾の未使用項目は別途非表示にしてください。
X・Y・active・shadowの各配列とusedカーソルは呼び出し側で定義し、固定PRGバンクにoam_fair_impl.hを一度だけ組み込みます。8×8スプライトモードでメインループから呼んでください。座標はクリッピングせず256で折り返します。この処理自体はOAMを転送しないため、shadowに対応したページをDMA転送します。ハードウェアの上限はOAM全体64個、走査線あたり8個です。
使用例
oam_fair_used=4; // Byte cursor: reserve the already prepared slot 0.
OAM_FairDraw(); // Phase becomes 13; append pool entries 13, 14 and 15.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
FAIR-POOLはスロット0の(144,32)にある緑の8×8四角を保持し、中心が(148,68)、(164,68)、(180,68)の矢印3本を追加します。画面上の左上はそれぞれ(144,64)、(160,64)、(176,64)になります。phase=0、used=4から呼ぶとPHASE 13、DRAWN 03、BYTES 16を表示します。この静止画は配置と既存項目の保持を検証し、別の64回呼び出す状態テストで全項目が一度ずつ先頭になることを確認します。
矢印は赤い縁・緑の軸・先端の青い細部で構成し、四角のタイル129は緑です。検証では各画像の8,704ピクセルの色、転送したOAM全256バイト、表示ラベルを照合します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_fair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_fair.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/oam_fair.h:24
Append active pool entries using the configured common tile/attributes. The emitter stops at byte cursor 252, leaving slot 63 unused to avoid cursor wrap. Hide unused trailing OAM entries separately before DMA.
kitaqfc/lib/oam_fair_impl.h:12
void OAM_FairDraw(void) {
__asm {
LDA oam_fair_phase
CLC
ADC #13
AND #63
STA oam_fair_phase
TAX
LDY oam_fair_used
LDA #0
STA oam_fair_drawn
STA oam_fair_scanned
// Visit each pool entry at most once, preserving already-written OAM entries
// and sharing the configured tile/attribute values across emitted sprites.
kq_fair_loop:
LDA oam_fair_active,X
BEQ kq_fair_next
LDA oam_fair_drawn
CMP oam_fair_limit
BCS kq_fair_done
// Reserve the final four-byte slot so advancing this byte cursor cannot wrap to zero.
CPY #252
BCS kq_fair_done
// Convert the 8x8 center to raw OAM coordinates; Y also needs the hardware minus-one bias.
LDA oam_fair_y,X
SEC
SBC #5
STA oam_fair_shadow,Y
LDA oam_fair_tile
STA oam_fair_shadow+1,Y
LDA oam_fair_attr
STA oam_fair_shadow+2,Y
LDA oam_fair_x,X
SEC
SBC #4
STA oam_fair_shadow+3,Y
INY
INY
INY
INY
INC oam_fair_drawn
// Advance cyclically and stop after returning to this frame's starting entry.
kq_fair_next:
TXA
CLC
ADC #1
AND #63
TAX
CPX oam_fair_phase
BNE kq_fair_loop
// Publish the updated byte cursor for later OAM writers and the VBlank DMA path.
kq_fair_done:
STY oam_fair_used
}
}pad.h — NESコントローラー入力
pad.h と pad.c は、コントローラ1の状態をNESのビット順で保持します。読込時に前回・押下中・新規押下・解放の各マスクを求めますが、リピートは計算しません。ボタン別の繰り返しを設定する場合は input_repeat.c もリンクします。コントローラ2にはポート2用の組み込み関数を使い、前回値はゲーム側で管理してください。
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
nes_pad_poll
$4016 をストローブし、コントローラ1のD0から8ビットをシリアル読込して、nes_pad1_prev・nes_pad1_cur・nes_pad1_pressed・nes_pad1_released を更新します。safe組み込み関数の一致確認による再試行は行わず、一度だけ読みます。末尾で $4017 も読みますが、コントローラ2の状態は保存しません。
書式
void nes_pad_poll(void);戻り値
ありません(void)。
使用条件と制限
NESの生の形式は A=0x01、B=0x02、SELECT=0x04、START=0x08、UP=0x10、DOWN=0x20、LEFT=0x40、RIGHT=0x80 です。ビット1が押下中です。nes_pad_* と生の __pad_* の結果にはこの値を使い、input.h の共通 BTN_* マスクとは混同しないでください。
初回の読込前に4個の nes_pad1_* 状態変数を0にしてください。その後は入力処理1回につき一度読みます。pressed = current & ~previous、released = previous & ~current で、どちらも直近の読込で生じた変化です。リピート状態は別途 nes_pad_repeat_step() で更新します。
使用例
nes_pad_poll(); // Read controller 1 and update its held/pressed/released snapshots.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ポーリング→リピート更新→状態取得の順序と、NES用の取得関数が真偽値1ではなく対象ビットを返すことを学ぶプログラムです。nes_pad_repeat_config(3, 1)で設定し、Bを40回の更新にわたって押してから離します。FIRST HELD、FIRST TRIGGER、FIRST REPEATは2、HELD TICKSは40、PULSE COUNTは19、RELEASE BITSは2になります。押した瞬間のパルスから4回の更新後に最初のリピートが発生し、以後は2回の更新ごとに発生します。FAILED CHECKSは0が正常です。押す長さを変えるとパルスの回数も変わります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache実装とソース
kitaqfc/lib/pad.h:16
Latch controller input with a 1-to-0 strobe and shift eight controller-1 bits into held/pressed/released masks. This direct path does not perform repeated reads for DMC interference; the final JOY2 read does not populate player-2 state.
kitaqfc/lib/pad.c:21
void nes_pad_poll(void)
{
unsigned char state;
unsigned char mask;
unsigned char sample;
nes_pad1_prev = nes_pad1_cur;
JOY1 = 1;
JOY1 = 0;
state = 0;
mask = 1;
while (mask != 0)
{
sample = JOY1;
if ((sample & 1) != 0)
{
state = state | mask;
}
mask = mask + mask;
}
nes_pad1_cur = state;
nes_pad1_pressed = (unsigned char)(state & (unsigned char)(state ^ nes_pad1_prev));
nes_pad1_released = (unsigned char)(nes_pad1_prev & (unsigned char)(state ^ nes_pad1_prev));
sample = JOY2;
sample = sample;
}palette.h — NESのパレット操作
32バイトのCPUシャドウと、即時転送・キュー転送で背景とスプライトの色を管理します。PPUのパレットアドレスにはミラーがあります。
nes_palette_apply_now
32色をシャドウへコピーしてからPPUへ直接転送します。
書式
void nes_palette_apply_now(unsigned char* src32);引数
src32読み取り可能な32色のバイト配列。背景16色に続けてスプライト16色を並べ、各色コードは0~63にします。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。 増分1で、安全なVBlank時間内または描画停止中に呼びます。待機・割り込み禁止・スクロール復元は行いません。
使用例
nes_palette_apply_now(colors);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:11
Replace shadow palette data and upload all 32 bytes immediately. The caller provides a safe PPU access period; this helper does not wait for VBlank.
kitaqfc/lib/palette.c:28
void nes_palette_apply_now(unsigned char* src32)
{
nes_palette_copy(src32);
nes_ppu_stream_write(0x3F00, nes_palette_shadow, 32);
}nes_palette_apply_shadow_now
シャドウ配列の現在の32色をPPUへ直接転送します。
書式
void nes_palette_apply_shadow_now(void);戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。 増分1で、安全なVBlank時間内または描画停止中に呼びます。待機・割り込み禁止・スクロール復元は行いません。
使用例
nes_palette_apply_shadow_now();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:13
Upload the current palette shadow directly, with timing controlled by the caller.
kitaqfc/lib/palette.c:35
void nes_palette_apply_shadow_now(void)
{
nes_ppu_stream_write(0x3F00, nes_palette_shadow, 32);
}nes_palette_copy
32色をCPU上のシャドウ配列へコピーします。PPUへの転送は行いません。
書式
void nes_palette_copy(unsigned char* src32);引数
src32読み取り可能な32色のバイト配列。背景16色に続けてスプライト16色を並べ、各色コードは0~63にします。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。
使用例
nes_palette_copy(colors);result[0]=nes_palette_shadow[5];呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:8
Copy exactly 32 palette bytes to the fixed shadow buffer without writing the PPU.
kitaqfc/lib/palette.c:19
void nes_palette_copy(unsigned char* src32)
{
__memcpy(nes_palette_shadow, src32, 32);
}nes_palette_queue_all
シャドウを32色で更新し、その内容をランタイムのVRAMキューへコピーします。
書式
unsigned char nes_palette_queue_all(unsigned char* src32);引数
src32読み取り可能な32色のバイト配列。背景16色に続けてスプライト16色を並べ、各色コードは0~63にします。
戻り値
キューに追加できれば1、容量不足なら0。0でもシャドウには新しい値が残ります。成功は画面への反映完了を意味しません。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。 キューは値をコピーするので、追加成功後は元配列を変更できます。NMIの消費処理と追加処理が競合しないようにしてください。
使用例
nes_vram_queue_clear();result[1]=nes_palette_queue_all(colors);result[2]=nes_vram_queue_used;nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:18
Update the shadow and copy all colors into the runtime queue. A queue failure leaves the new shadow contents in place. Return one if the runtime queue accepted the copied payload, zero if it was full. Admission does not mean the NMI consumer has already updated the PPU.
kitaqfc/lib/palette.c:42
unsigned char nes_palette_queue_all(unsigned char* src32)
{
nes_palette_copy(src32);
return nes_vram_queue_try_write(0x3F00, nes_palette_shadow, 32);
}nes_palette_queue_bg4
指定した背景パレット4色をシャドウへ保存し、VRAMキューへコピーします。
書式
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4);引数
pal_indexパレット番号0~3。範囲検証はないので必ずこの範囲にします。
src4選んだパレット用の4バイト。色コードは0~63にします。
戻り値
キューに追加できれば1、容量不足なら0。0でもシャドウには新しい値が残ります。成功は画面への反映完了を意味しません。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。 キューは値をコピーするので、追加成功後は元配列を変更できます。NMIの消費処理と追加処理が競合しないようにしてください。
使用例
result[3]=nes_palette_queue_bg4(3,four);nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:21
Update and queue four background palette bytes. pal_index must be 0..3; there is no range check before writing the shadow.
kitaqfc/lib/palette.c:50
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4)
{
unsigned char dst;
dst = pal_index + pal_index;
dst = dst + dst;
__memcpy(nes_palette_shadow + dst, src4, 4);
return nes_vram_queue_try_write((unsigned short)(0x3F00 + dst), nes_palette_shadow + dst, 4);
}nes_palette_queue_sprite4
指定したスプライトパレット4色をシャドウへ保存し、VRAMキューへコピーします。
書式
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4);引数
pal_indexパレット番号0~3。範囲検証はないので必ずこの範囲にします。
src4選んだパレット用の4バイト。色コードは0~63にします。
戻り値
キューに追加できれば1、容量不足なら0。0でもシャドウには新しい値が残ります。成功は画面への反映完了を意味しません。
使用条件と制限
シャドウはCPUの0x03C0~0x03DFを予約します。PPUの0x3F10/14/18/1Cは0x3F00/04/08/0Cの別名なので、シャドウ32バイトとPPUの読み戻し値が独立した32色として一致するとは限りません。 キューは値をコピーするので、追加成功後は元配列を変更できます。NMIの消費処理と追加処理が競合しないようにしてください。
使用例
result[4]=nes_palette_queue_sprite4(2,four);nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
背景の32×32矩形を(16,32)・(80,32)・(144,32)へ赤・青・緑で表示します。スプライトの8×8正方形も(16,96)・(80,96)・(144,96)へ同じ順で表示します。背景とスプライトで別々のパレットを使い、直接転送とキュー経由の転送を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/palette.h:24
Update and queue four sprite palette bytes at the 0x3F10 region. The caller supplies pal_index 0..3; NES palette mirroring still applies.
kitaqfc/lib/palette.c:61
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4)
{
unsigned char dst;
dst = pal_index + pal_index;
dst = dst + dst;
__memcpy(nes_palette_shadow + (unsigned char)(0x10 + dst), src4, 4);
return nes_vram_queue_try_write((unsigned short)(0x3F10 + dst), nes_palette_shadow + (unsigned char)(0x10 + dst), 4);
}Q5.3の型を使う独自の移動処理
physics2d.hには、world/bodyの関数に加えて、独自の小数移動に使える型と定数があります。ここではQ5.3のデータ表現を使い、ゲーム側でgame_stepを実装します。この例はworld/bodyの積分処理とは別の方式です。
KQ2DPositionは整数ピクセル位置(0~255)、KQ2DFractionは端数(0~7)、KQ2DSpeedは1刻み当たりの移動量の大きさ(1/8ピクセル単位、0~255)、KQ2DDirectionは方向を保持します。各型の実体はu8です。速度の符号を同じ変数に格納しません。
KQ2D_SUBPIXEL_BITS=3、ONE=8、MASK=7を使い、位置×8+端数を16ビットへ広げて計算します。方向のNONE=0は停止、NEGATIVE=1は減算、POSITIVE=2は加算を表します。計算後の位置が0~255に収まり、減算が0未満にならないようにゲーム側で境界を処理してください。
KQ2D_DRAG_BRAKE=1、STUN=2、COAST=3、THRUST=4は、ゲーム側の制動・停止効果・慣性・推進などを区別する識別値です。これらを代入するだけで減速や加速は起こりません。効果と継続時間をゲームの更新処理で定義します。
使用例:半ピクセルずつ進む物体
上の赤い16×16ピクセルの箱はX=24、下の箱は16刻み後のX=32を示します。速度4は1刻み当たり4/8=0.5ピクセルなので、16刻みで8ピクセル進みます。Y座標の違いは比較のための配置です。その後、逆方向へ8刻み進める数値確認ではX=28、端数0になります。
void game_step() {
u16 total;
total=(u16)position*KQ2D_SUBPIXEL_ONE+fraction;
if(direction==KQ2D_DIR_POSITIVE) total=total+speed;
else if(direction==KQ2D_DIR_NEGATIVE) total=total-speed;
position=(KQ2DPosition)(total>>KQ2D_SUBPIXEL_BITS);
fraction=(KQ2DFraction)(total&KQ2D_SUBPIXEL_MASK);
}
// Initial state: position=24; fraction=0; speed=4;
// direction=KQ2D_DIR_POSITIVE; call game_step() 16 times.
ビルド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_subpixel.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\physics_subpixel.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasmphysics2d
矩形bodyの積分・AABB接触と、個別に使える面応答を提供します。中心と半サイズで矩形を表し、active=0は処理対象外、inv_mass_q8<=0は固定物体です。KQRectは中心形式ではなく左上+幅・高さです。bodyの反発係数は矩形ペアのソルバーでは使わず、面応答ではKQSurface2Dの係数を使います。
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
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の初期化だけでは配列要素は初期化されません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:36
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.
kitaqfc/lib/physics2d.c:351
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;
}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回行います。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:41
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 uses non-reentrant helper scratch. This is discrete integration; fast bodies can pass through thin obstacles.
kitaqfc/lib/physics2d.c:581
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[(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_value();
kq2d_asm_limit = world->max_speed;
kq2d__clamp_value();
b->vx = kq2d_asm_value;
kq2d_asm_value = b->vy;
kq2d_asm_operand = world->gravity_y;
kq2d__add_value();
kq2d_asm_limit = world->max_speed;
kq2d__clamp_value();
b->vy = kq2d_asm_value;
kq2d_asm_value = b->x;
kq2d_asm_operand = b->vx;
kq2d__add_value();
b->x = kq2d_asm_value;
kq2d_asm_value = b->y;
kq2d_asm_operand = b->vy;
kq2d__add_value();
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[(u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody2D* b = &bodies[(u8)j];
if (b->active == 0) continue;
kq2d__resolve_pair(a, b);
}
}
}
}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の場合も何もしません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:45
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.
kitaqfc/lib/physics2d.c:451
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_value();
kq2d_asm_limit = world->max_speed;
kq2d__clamp_value();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = world->gravity_y;
kq2d__add_value();
kq2d_asm_limit = world->max_speed;
kq2d__clamp_value();
body->vy = kq2d_asm_value;
kq2d_asm_value = body->x;
kq2d_asm_operand = body->vx;
kq2d__add_value();
body->x = kq2d_asm_value;
kq2d_asm_value = body->y;
kq2d_asm_operand = body->vy;
kq2d__add_value();
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ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:49
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.
kitaqfc/lib/physics2d.c:366
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_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なら何もしません。半サイズは正の値を指定してください。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:52
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.
kitaqfc/lib/physics2d.c:384
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_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以外なら変更します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:54
Replace the center without altering velocity, extents or activity. Null is ignored.
kitaqfc/lib/physics2d.c:399
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以外なら変更します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:56
Replace velocity without clamping or checking activity/mass. Null is ignored.
kitaqfc/lib/physics2d.c:407
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy)
{
if (body == 0) return;
body->vx = vx;
body->vy = vy;
}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以上を指定します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:60
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.
kitaqfc/lib/physics2d.c:417
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_value();
kq2d_asm_limit = max_speed;
kq2d__clamp_value();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = gravity_y;
kq2d__add_value();
kq2d_asm_limit = max_speed;
kq2d__clamp_value();
body->vy = kq2d_asm_value;
}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は無視します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:64
Scale both velocities by a signed eight-bit coefficient divided by 256. Despite the unsigned parameter, values 128..255 become negative coefficients; this is not a full-range unsigned Q8 damping helper. Null is ignored.
kitaqfc/lib/physics2d.c:441
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8)
{
if (body == 0) return;
body->vx = kq2d_scale_q8(body->vx, (s16)(s8)friction_q8);
body->vy = kq2d_scale_q8(body->vy, (s16)(s8)friction_q8);
}kq2d_rect_intersect
左上座標・幅・高さで表した2個の矩形について、内部の重なりを調べます。
書式
u8 kq2d_rect_intersect(KQRect a, KQRect b);引数
a左上座標と幅・高さを持つKQRectの値。幅と高さには正の値を指定します。
b左上座標と幅・高さを持つKQRectの値。幅と高さには正の値を指定します。
戻り値
内部が重なれば1、接触のみまたは離れていれば0です。
使用条件と制限
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
0以下の幅や高さを除外するチェックはありません。bodyの中心・半サイズをそのままKQRectへ入れないでください。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:67
Delegate strict rectangle overlap to the fixed-point utility; edge-only contact is false. Coordinate-plus-size sums must remain representable as s16.
kitaqfc/lib/physics2d.c:485
u8 kq2d_rect_intersect(KQRect a, KQRect b)
{
return kq_rect_intersect(a, b);
}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ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。初期値、設定、積分、矩形・点判定、符号付き倍率もRAMの期待値と照合します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:70
Delegate the half-open point test: include left/top and exclude right/bottom. Coordinate-plus-size sums must remain representable as s16.
kitaqfc/lib/physics2d.c:492
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r)
{
return kq_point_in_rect(x, y, r);
}kq2d_scale_q8
符号付き係数/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に収まりません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:86
Truncates toward zero, including negative values; coefficient is -256..256.
kitaqfc/lib/physics2d.c:8
s16 kq2d_scale_q8(s16 value,s16 coefficient) {
u16 magnitude;u16 product;u8 factor;u8 negative;
__asm {
LDA value
ORA value+1
BEQ kqfc_scale_zero
LDA coefficient
BNE kqfc_scale_fraction
LDA coefficient+1
BEQ kqfc_scale_zero
BMI kqfc_scale_negate_value
LDA value
LDX value+1
RTS
kqfc_scale_negate_value:
SEC
LDA #0
SBC value
STA product
LDA #0
SBC value+1
TAX
LDA product
RTS
kqfc_scale_zero:
LDA #0
LDX #0
RTS
kqfc_scale_fraction:
LDA value+1
EOR coefficient+1
AND #128
STA negative
LDA value
STA magnitude
LDA value+1
STA magnitude+1
BPL kqfc_scale_value_positive
SEC
LDA #0
SBC magnitude
STA magnitude
LDA #0
SBC magnitude+1
STA magnitude+1
kqfc_scale_value_positive:
LDA coefficient
STA factor
LDA coefficient+1
BPL kqfc_scale_factor_positive
SEC
LDA #0
SBC factor
STA factor
kqfc_scale_factor_positive:
LDA #0
STA product
STA product+1
// Process coefficient bit 0; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_0
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_0:
ROR product+1
ROR product
// Process coefficient bit 1; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_1
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_1:
ROR product+1
ROR product
// Process coefficient bit 2; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_2
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_2:
ROR product+1
ROR product
// Process coefficient bit 3; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_3
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_3:
ROR product+1
ROR product
// Process coefficient bit 4; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_4
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_4:
ROR product+1
ROR product
// Process coefficient bit 5; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_5
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_5:
ROR product+1
ROR product
// Process coefficient bit 6; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_6
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_6:
ROR product+1
ROR product
// Process coefficient bit 7; carry is the product's seventeenth bit.
LSR factor
BCC kqfc_scale_shift_7
CLC
LDA product
ADC magnitude
STA product
LDA product+1
ADC magnitude+1
STA product+1
kqfc_scale_shift_7:
ROR product+1
ROR product
LDA negative
BEQ kqfc_scale_positive
SEC
LDA #0
SBC product
STA product
LDA #0
SBC product+1
TAX
LDA product
RTS
kqfc_scale_positive:
LDA product
LDX product+1
RTS
}
}kq2d_body_limit_speed
速度ベクトルの長さを保守的に見積もり、両軸を同じ倍率で縮小して合成速度を制限します。
書式
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);引数
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
max_speed速度の上限。軸ごとの制限か合成速度の制限かは関数ごとに異なります。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
各速度の絶対値と正のmax_speedは16383以下にしてください。上限0以下は速度を0にします。NULLは無視しますが、activeや質量は無視して変更します。整数量子化により上限より小さくなり、ごく小さい倍率では0になります。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:90
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.
kitaqfc/lib/physics2d.c:214
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[(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
移動する面との相対速度を使い、法線方向の反発・最低反発速度と接線摩擦を速度へ適用します。
書式
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や質量の判定はしません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:93
Kinematic surface response; returns incoming normal speed, or zero if separating. Caller resolves penetration separately. Relative components must fit +/-8191.
kitaqfc/lib/physics2d.c:268
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_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、交差なしとちょうど終点での接触は同じ戻り値になります。位置は変更しません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics2d.h:96
Linear crossing time in Q8 (0..256); positive separation is outside. Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.
kitaqfc/lib/physics2d.c:245
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));
}physics3d
回転しない3Dの箱を中心と各軸の半サイズで表す物理ライブラリです。質量・逆質量は接触の重み、restitution_q8は反発係数、ax/ay/azは持続する加速度です。表示は別の処理で行います。サンプルは同じ物体を正面と上面に描き、Z方向の動きと壁の反発を分けて示します。
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
last_impact_speedには軸方向の接近速度の最大値が記録されます。動的な有効bodyは積分時に0へ戻り、固定・無効bodyは値を保持します。break_speedやKQ3D_BODY_BROKENを見て自動的に壊す処理はありません。破壊判定・flags・activeの更新はゲーム側で行います。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
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の初期化だけでは配列要素は初期化されません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:47
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.
kitaqfc/lib/physics3d.c:125
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;
}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へ変換したり、描画したりはしません。
反発係数は224、break_speedは0です。質量設定はkq3d_body_set_massと同じ規則です。NULLは無視し、半サイズの妥当性は呼び出し側が保証します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:51
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.
kitaqfc/lib/physics3d.c:161
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以下は固定物体です。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
0以下は逆質量0の固定物体です。正なら32767/max(mass,64)を整数除算し、最低1とします。保存するmassは元の値のままで、正確なQ8逆数ではありません。mass=256ではinv_mass=127です。速度やactiveは変更せず、NULLは無視します。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:56
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.
kitaqfc/lib/physics3d.c:142
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_step
ワールド内の有効な動的bodyを積分し、有効な全bodyの接触補正・反発・衝撃値の記録を行います。
書式
void kq3d_step(KQWorld3D* world);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
worldは配列を借りて使います。配列やbodyの領域確保・寿命管理は呼び出し側の責任です。worldの初期化だけでは配列要素は初期化されません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
侵入量が最小の軸で接触を解決し、同値なら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を超えない設定にしてください。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:62
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 uses non-reentrant helper scratch and can miss fast crossings.
kitaqfc/lib/physics3d.c:323
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[(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_value();
kq3d_asm_operand = b->ax;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
b->vx = kq3d_asm_value;
kq3d_asm_value = b->vy;
kq3d_asm_operand = world->gravity_y;
kq3d__add_value();
kq3d_asm_operand = b->ay;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
b->vy = kq3d_asm_value;
kq3d_asm_value = b->vz;
kq3d_asm_operand = world->gravity_z;
kq3d__add_value();
kq3d_asm_operand = b->az;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
b->vz = kq3d_asm_value;
kq3d_asm_value = b->x;
kq3d_asm_operand = b->vx;
kq3d__add_value();
b->x = kq3d_asm_value;
kq3d_asm_value = b->y;
kq3d_asm_operand = b->vy;
kq3d__add_value();
b->y = kq3d_asm_value;
kq3d_asm_value = b->z;
kq3d_asm_operand = b->vz;
kq3d__add_value();
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[(u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody3D* b = &bodies[(u8)j];
if (b->active == 0) continue;
kq3d__resolve_pair(a, b);
}
}
}
}kq3d_integrate_body
1個の有効な動的bodyの衝撃値を消去し、重力とbody固有の加速度を足して、軸速度制限後に位置を進めます。
書式
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);引数
world更新または参照するワールドへのポインタ。配列と設定値を保持します。
body変更するbodyへのポインタ。この関数が使わないフィールドは保持されます。
戻り値
戻り値はありません。更新されるフィールドと反映時期を、この項目で説明しています。
使用条件と制限
座標・半径・半サイズ・速度・重力は、呼び出し側で統一した整数単位を使います。1回のstepが1時間刻みです。サンプルは1単位=1ピクセルです。位置を自動的にQ8.8へ変換したり、描画したりはしません。
加算・差・半サイズの和・接触補正の積を、符号付き16ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
矩形の積分・衝突処理は共有作業領域を使います。同じ物理処理を割り込みから重ねて呼び出さないでください。
last_impact_speedには軸方向の接近速度の最大値が記録されます。動的な有効bodyは積分時に0へ戻り、固定・無効bodyは値を保持します。break_speedやKQ3D_BODY_BROKENを見て自動的に壊す処理はありません。破壊判定・flags・activeの更新はゲーム側で行います。
ax・ay・azは呼び出し後も保持され、次の積分でも加算します。この関数単独では衝突を解決しません。NULL、無効、固定bodyは変更しません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:67
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.
kitaqfc/lib/physics3d.c:267
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_value();
kq3d_asm_operand = body->ax;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
body->vx = kq3d_asm_value;
kq3d_asm_value = body->vy;
kq3d_asm_operand = world->gravity_y;
kq3d__add_value();
kq3d_asm_operand = body->ay;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
body->vy = kq3d_asm_value;
kq3d_asm_value = body->vz;
kq3d_asm_operand = world->gravity_z;
kq3d__add_value();
kq3d_asm_operand = body->az;
kq3d__add_value();
kq3d_asm_limit = world->max_speed;
kq3d__clamp_value();
body->vz = kq3d_asm_value;
kq3d_asm_value = body->x;
kq3d_asm_operand = body->vx;
kq3d__add_value();
body->x = kq3d_asm_value;
kq3d_asm_value = body->y;
kq3d_asm_operand = body->vy;
kq3d__add_value();
body->y = kq3d_asm_value;
kq3d_asm_value = body->z;
kq3d_asm_operand = body->vz;
kq3d__add_value();
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ビットの範囲に収めてください。速度制限は加算後に適用されるため、加算時のオーバーフローは防げません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:71
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.
kitaqfc/lib/physics3d.c:188
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_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で返します。
使用条件と制限
各項を先に0方向へ切り捨てるため、積の総和を最後に1回だけシフトする内積とは端数が異なります。係数に1.0=256は格納できません。最終和は飽和しません。
使用するヘッダーに加え、実装の.cもコンパイルしてください。2Dはfixed.cとphysics2d.c、3Dはこれらにphysics3d.cを加えます。サンプルはMMC3、CHR RAM、描画用のPRG RAM $6800~$6FFFを使います。この描画領域は物理ライブラリ自体の要件ではありません。
使用例
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)です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm実装とソース
kitaqfc/lib/physics3d.h:75
Sum three signed products, dividing each by 256 (toward zero) before addition. Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.
kitaqfc/lib/physics3d.c:407
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az)
{
return (s16)(kq2d_scale_q8(x,(s16)ax) + kq2d_scale_q8(y,(s16)ay) + kq2d_scale_q8(z,(s16)az));
}ppu.h — PPU画面操作の宣言
ppu.hを読み込み、ppu.cを1回コンパイルします。2バイト指定のヘルパーは自身で共有アドレスラッチをリセットします。runtime.cも併用できます。16ビット指定はnes_ppu_seek、2バイト指定はnes_ppu_seek_bytesです。
nes_ppu_clear_nt
論理ネームテーブル1面のタイル960バイトと、属性64バイトを埋めます。アドレス増分を一時的に1へ設定し、終了後にPPUCTRLを戻します。不正な先頭アドレスでは何もしません。
書式
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr);引数
nt_base / tile / attrnt_baseは0x2000・0x2400・0x2800・0x2C00のいずれかです。tileは960マス共通のタイル番号、attrは属性64バイト共通の4区画のパレット選択値です。論理テーブル同士のメモリ共有はカートリッジのミラーリング設定で決まります。
戻り値
戻り値はありません。書き込みは呼び出し内で完了します。
使用条件と制限
ppu.cをコンパイルし、ppu.hを読み込みます。一括転送は描画を止めて実行してください。VBlank待機やキュー登録はしません。同時のPPUアクセスを避け、表示前にスクロールを設定します。制御・マスクはライブラリまたは組み込み関数で設定してください。レジスターへの直接書き込みはソフトウェア側の保持値へ反映されません。
使用例
nes_ppu_clear_nt(0x2000,0,0); // All 960 tiles and 64 packed attribute bytes.
// Nametable reads are buffered: discard one read, then inspect all 1024 bytes.
__ppu_addr(0x2000);discarded_read=sample_ppudata;
for(i=0;i<1024;i++)if(sample_ppudata!=0)failures=1;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのライブラリ関数で描画停止、両パレットの転送、ネームテーブル全体の初期化、描画開始を行います。描く前にパレットと初期化した面を読み戻して確認します。y=16に青い正方形・棒・三角形のコピー、y=96に緑の埋め込み、y=208に赤い正方形・棒・三角形を表示します。大きい領域は各255タイルで、最後の1マスは黒です。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.h:14
Fill 960 tiles and 64 packed attributes; preserve PPUCTRL. Accept exactly $2000/$2400/$2800/$2C00; invalid bases are ignored.
kitaqfc/lib/ppu.c:60
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr)
{
unsigned char table;
unsigned char saved_ctrl;
if (nt_base != 0x2000 && nt_base != 0x2400 && nt_base != 0x2800 && nt_base != 0x2C00) return;
table = (unsigned char)((nt_base - 0x2000) >> 10);
saved_ctrl=__ppu_ctrl_get();
__ppu_ctrl_set((u8)(saved_ctrl & 0xFB));
__nametable_rect_nt(table,0,0,32,30,tile);
__vram_fill((u16)(nt_base+0x3C0),attr,64);
__ppu_ctrl_set(saved_ctrl);
}nes_ppu_fill
アドレスラッチをリセットしてhi/loを設定し、PPUDATAへvalueをcount回書きます。8ビットの個数による即時の埋め込みで、行や矩形の境界では切りません。
書式
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count);引数
hi / lohiとloはPPUアドレスの上位・下位バイトです。たとえば0x23, 0x42で0x2342を表します。タイル座標ではなくアドレスのバイトです。PPUの通常のアドレスマスクとミラーリングが適用されます。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
countバイト数0~255です。0でもアドレスラッチをリセットしてアドレスを設定しますが、PPUDATAには書きません。
戻り値
ありません(void)。
使用条件と制限
ppu.hを読み込み、ppu.cを1回コンパイルします。2バイト指定のヘルパーは自身で共有アドレスラッチをリセットします。runtime.cも併用できます。16ビット指定はnes_ppu_seek、2バイト指定はnes_ppu_seek_bytesです。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
__ppu_read_status();nes_ppu_fill(0x21,0x80,1,255);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上位・下位バイト指定の例では、ラッチのリセットと8ビットの転送個数を確認します。青いコピーと緑の埋め込みは各255バイトで、最後のマス(248,72)と(248,152)は黒です。y=208の赤い印はアドレス設定と個数0の転送を示します。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.c:27
Set the address and issue count identical PPUDATA writes without waiting for VBlank.
kitaqfc/lib/ppu.c:27
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count)
{
unsigned char i;
nes_ppu_seek_bytes(hi, lo);
for (i = 0; i != count; ++i)
kq_ppu_data_register = value;
}nes_ppu_load_palette
1つの32バイト配列から、背景パレット16バイト、スプライトパレット16バイトの順に転送します。アドレス増分を一時的に1へ設定し、終了後にPPUCTRLを戻します。NULLでは何もしません。
書式
void nes_ppu_load_palette(unsigned char* pal32);引数
pal32読み出せる色コード32バイトへのポインタです。先に背景、次にスプライトを並べます。戻るまで素材のバンクを保持してください。共通背景色の別名アドレスなど、PPUのパレットミラーリングが適用されます。
戻り値
戻り値はありません。書き込みは呼び出し内で完了します。
使用条件と制限
ppu.cをコンパイルし、ppu.hを読み込みます。一括転送は描画を止めて実行してください。VBlank待機やキュー登録はしません。同時のPPUアクセスを避け、表示前にスクロールを設定します。制御・マスクはライブラリまたは組み込み関数で設定してください。レジスターへの直接書き込みはソフトウェア側の保持値へ反映されません。
使用例
nes_ppu_load_palette(stream_palette); // Sixteen BG bytes followed by sixteen sprite bytes.
// Palette reads are immediate; both halves use the same original sample palette.
__ppu_addr(0x3F00);
for(i=0;i<32;i++)if(sample_ppudata!=stream_palette[(u8)(i&15)])failures=1;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのライブラリ関数で描画停止、両パレットの転送、ネームテーブル全体の初期化、描画開始を行います。描く前にパレットと初期化した面を読み戻して確認します。y=16に青い正方形・棒・三角形のコピー、y=96に緑の埋め込み、y=208に赤い正方形・棒・三角形を表示します。大きい領域は各255タイルで、最後の1マスは黒です。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.h:11
Copy 32 bytes into $3F00..$3F1F with increment one; preserve PPUCTRL. Null is ignored. Hardware palette mirrors apply; source bank must stay mapped.
kitaqfc/lib/ppu.c:48
void nes_ppu_load_palette(unsigned char* pal32)
{
u8 saved_ctrl;
if (pal32 == 0) return;
saved_ctrl=__ppu_ctrl_get();
__ppu_ctrl_set((u8)(saved_ctrl & 0xFB));
__palette_bg_load(pal32);
__palette_sp_load(pal32 + 16);
__ppu_ctrl_set(saved_ctrl);
}nes_ppu_screen_off
PPUMASKのソフトウェア側の保持値から背景・スプライトの描画ビットを消し、書き戻します。それ以外のマスクビットとPPUCTRLは保持します。
書式
void nes_ppu_screen_off(void);戻り値
戻り値はありません。書き込みは呼び出し内で完了します。
使用条件と制限
ppu.cをコンパイルし、ppu.hを読み込みます。一括転送は描画を止めて実行してください。VBlank待機やキュー登録はしません。同時のPPUアクセスを避け、表示前にスクロールを設定します。制御・マスクはライブラリまたは組み込み関数で設定してください。レジスターへの直接書き込みはソフトウェア側の保持値へ反映されません。
使用例
nes_ppu_screen_off();__ppu_ctrl_set(0); // Disable NMI during this bulk preparation.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのライブラリ関数で描画停止、両パレットの転送、ネームテーブル全体の初期化、描画開始を行います。描く前にパレットと初期化した面を読み戻して確認します。y=16に青い正方形・棒・三角形のコピー、y=96に緑の埋め込み、y=208に赤い正方形・棒・三角形を表示します。大きい領域は各255タイルで、最後の1マスは黒です。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.h:6
Link ppu.c. Bulk palette/nametable transfers require rendering to be disabled. Disable background and sprite rendering, preserving other PPUMASK bits.
kitaqfc/lib/ppu.c:36
void nes_ppu_screen_off(void) { __ppu_mask_set((u8)(__ppu_mask_get() & 0xE7)); }nes_ppu_screen_on
共有PPUラッチをリセットし、スクロールを(0,0)にしてから指定のPPUCTRLとPPUMASKを書きます。描画とNMIの有効・無効は指定値で決まります。
書式
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask);引数
ctrl / maskPPUCTRLとPPUMASKへ書くバイト値です。例のctrl=0x80はNMI有効・背景パターンテーブル0、mask=0x0Aは左端8ピクセルを含む背景描画を表します。
戻り値
戻り値はありません。書き込みは呼び出し内で完了します。
使用条件と制限
ppu.cをコンパイルし、ppu.hを読み込みます。一括転送は描画を止めて実行してください。VBlank待機やキュー登録はしません。同時のPPUアクセスを避け、表示前にスクロールを設定します。制御・マスクはライブラリまたは組み込み関数で設定してください。レジスターへの直接書き込みはソフトウェア側の保持値へ反映されません。
使用例
nes_ppu_screen_on(0x80,0x0A);
// NMI and background rendering are now enabled, with left-edge background visible.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
4つのライブラリ関数で描画停止、両パレットの転送、ネームテーブル全体の初期化、描画開始を行います。描く前にパレットと初期化した面を読み戻して確認します。y=16に青い正方形・棒・三角形のコピー、y=96に緑の埋め込み、y=208に赤い正方形・棒・三角形を表示します。大きい領域は各255タイルで、最後の1マスは黒です。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.h:8
Set scroll to (0,0), then apply the requested PPUCTRL and PPUMASK values.
kitaqfc/lib/ppu.c:39
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask)
{
__ppu_read_status();
__scroll_set(0,0);
__ppu_ctrl_set(ctrl);
__ppu_mask_set(mask);
}nes_ppu_seek_bytes
PPUSTATUSを読んで共有PPUアドレスラッチをリセットし、上位hi・下位loの2バイトからアドレスを設定します。ゲーム側でアドレスを2バイトに分けて保持している場合に使います。
書式
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo);引数
hi / lohiとloはPPUアドレスの上位・下位バイトです。たとえば0x23, 0x42で0x2342を表します。タイル座標ではなくアドレスのバイトです。PPUの通常のアドレスマスクとミラーリングが適用されます。
戻り値
ありません(void)。
使用条件と制限
ppu.hを読み込み、ppu.cを1回コンパイルします。2バイト指定のヘルパーは自身で共有アドレスラッチをリセットします。runtime.cも併用できます。16ビット指定はnes_ppu_seek、2バイト指定はnes_ppu_seek_bytesです。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
nes_ppu_seek_bytes(0x23,0x42);
PPUDATA=1; // Red 8x8 marker at pixel (16,208).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上位・下位バイト指定の例では、ラッチのリセットと8ビットの転送個数を確認します。青いコピーと緑の埋め込みは各255バイトで、最後のマス(248,72)と(248,152)は黒です。y=208の赤い印はアドレス設定と個数0の転送を示します。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.c:8
Reset the shared scroll/address latch before writing the address byte pair.
kitaqfc/lib/ppu.c:8
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo)
{
__ppu_read_status();
kq_ppu_addr_register = hi;
kq_ppu_addr_register = lo;
}nes_ppu_write_bytes
アドレスラッチをリセットしてhi/loを設定し、素材のcountバイトを直接PPUDATAへ書きます。個数は8ビットなので1回で255バイトまでです。キューを使わず、戻る前に転送を完了します。
書式
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count);引数
hi / lohiとloはPPUアドレスの上位・下位バイトです。たとえば0x23, 0x42で0x2342を表します。タイル座標ではなくアドレスのバイトです。PPUの通常のアドレスマスクとミラーリングが適用されます。src指定したバイト数を読み出せるCPU側のポインターです。同期転送が戻るまで、元データが見えるバンクと内容を保ってください。バンク切り替え・キューへのポインター保持・転送元の長さ確認は行いません。長さ0ならポインターを参照しません。
countバイト数0~255です。0でもアドレスラッチをリセットしてアドレスを設定しますが、PPUDATAには書きません。
戻り値
ありません(void)。
使用条件と制限
ppu.hを読み込み、ppu.cを1回コンパイルします。2バイト指定のヘルパーは自身で共有アドレスラッチをリセットします。runtime.cも併用できます。16ビット指定はnes_ppu_seek、2バイト指定はnes_ppu_seek_bytesです。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
__ppu_read_status();nes_ppu_write_bytes(0x20,0x40,source_bytes,255);
source_bytes[0]=3; // The completed transfer retains tile 1.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
上位・下位バイト指定の例では、ラッチのリセットと8ビットの転送個数を確認します。青いコピーと緑の埋め込みは各255バイトで、最後のマス(248,72)と(248,152)は黒です。y=208の赤い印はアドレス設定と個数0の転送を示します。FAILED CHECKSは0です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/ppu.c:18
Set the address and write count bytes directly through PPUDATA. The current PPUCTRL increment mode determines how the destination advances. count is 0..255; zero still changes PPU address/latch state. No bank switch or payload copy is performed.
kitaqfc/lib/ppu.c:18
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count)
{
unsigned char i;
nes_ppu_seek_bytes(hi, lo);
for (i = 0; i != count; ++i)
kq_ppu_data_register = src[i];
}runtime.h — NMI、OAM、PPU転送のCヘルパー
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
これはruntime.cがCPUアドレス0x0300–0x03BFに持つ、独立した192バイトのキューです。実データ付きレコードは3+lenバイト、塗りつぶしは4バイトを使います。nes_vram_queue_usedは符号化したレコードのバイト数で、PPUへの書き込み回数やVRAMの空き容量ではありません。__vramq_*の組み込みキューは形式が異なり、これらの関数では消去・照会・実行しません。
レコードを作る段階と実行する段階を分けてください。ロックやダブルバッファはありません。確実に準備するには追加中はNMIを無効にし、描画停止中に実行するか、VBlankに収まる量を準備してからNMIを有効にします。runtime.cはコミット呼び出しなしで、空でないキューを__nes_nmiから自動実行します。フラッシュには時間制限・分割・スクロール復元がありません。キューに入る量でもVBlank内に転送できるとは限りません。
nes_ppu_seek
後続のPPUDATAアクセス用にPPUアドレスを設定します。PPUSTATUSを読んで共有ラッチをリセットし、16ビットのppu_addrの上位・下位バイトを書きます。runtime.cのこの関数は、ppu.cの2バイト指定のnes_ppu_seek_bytesと併用できます。
書式
void nes_ppu_seek(unsigned short ppu_addr);引数
ppu_addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。
戻り値
ありません(void)。
使用条件と制限
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
PPUADDR=0x21; // Deliberately leave the shared address latch half-written.
nes_ppu_seek(0x2342); // This runtime function resets the latch itself.
PPUDATA=1; // Red 8x8 marker at pixel (16,208).呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ランタイムの例では直接転送と二つの待機方法を学びます。(0,16)に青い塗りつぶしタイル・左半分の棒・三角形の繰り返し、(0,96)に緑の塗りつぶしが表示されます。各領域は256タイル分のバイトからなる256×64ピクセルです。転送元を変更しても青の先頭タイルは塗りつぶしのままです。y=208のx=16・224・240には、アドレス設定・長さ0の書き込み・長さ0の埋め込みを示す赤い8×8の正方形、左半分4×8の棒、8行の三角形が表示されます。待機確認は両方1、FAILED CHECKSは0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:28
Reset the PPU address latch with a status read, then write the high/low address bytes.
kitaqfc/lib/runtime.c:207
void nes_ppu_seek(unsigned short ppu_addr)
{
unsigned char latch;
latch = PPUSTATUS;
PPUADDR = (unsigned char)(ppu_addr >> 8);
PPUADDR = (unsigned char)ppu_addr;
latch = latch;
}nes_ppu_stream_fill
ppu_addrを設定し、同じ1バイト値valueをPPUDATAへlen回書きます。関数が戻るまでに完了する直接の埋め込み処理です。タイルの寸法・属性・矩形の境界は解釈しません。
書式
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len);引数
ppu_addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
lenバイト数。符号なし16ビットの
0..65535です。0でもPPUアドレスの設定とラッチのリセットは行いますが、PPUDATAへは書きません。転送先の容量確認や分割処理はなく、大きな値ではPPUアドレス空間を周回し得ます。
戻り値
ありません(void)。
使用条件と制限
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
nes_ppu_stream_fill(0x2180,1,256); // Eight complete rows of solid green tiles.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ランタイムの例では直接転送と二つの待機方法を学びます。(0,16)に青い塗りつぶしタイル・左半分の棒・三角形の繰り返し、(0,96)に緑の塗りつぶしが表示されます。各領域は256タイル分のバイトからなる256×64ピクセルです。転送元を変更しても青の先頭タイルは塗りつぶしのままです。y=208のx=16・224・240には、アドレス設定・長さ0の書き込み・長さ0の埋め込みを示す赤い8×8の正方形、左半分4×8の棒、8行の三角形が表示されます。待機確認は両方1、FAILED CHECKSは0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:34
Write value repeatedly through PPUDATA, using the current PPU address increment mode. This routine does not wait for VBlank or disable rendering.
kitaqfc/lib/runtime.c:232
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len)
{
nes_ppu_seek(ppu_addr);
while (len != 0)
{
PPUDATA = value;
len = len - 1;
}
}nes_ppu_stream_write
ppu_addrを開始アドレスとして、転送元の連続したlenバイトをPPUDATAへ直接書きます。最初にアドレスを設定し、書くたびに関数内の転送元ポインターを進めます。関数が戻る時点で転送は完了しており、その後に転送元を変更してもVRAMは変わりません。呼び出し元のポインター変数も変わりません。
書式
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len);引数
ppu_addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。src指定したバイト数を読み出せるCPU側のポインターです。同期転送が戻るまで、元データが見えるバンクと内容を保ってください。バンク切り替え・キューへのポインター保持・転送元の長さ確認は行いません。長さ0ならポインターを参照しません。
lenバイト数。符号なし16ビットの
0..65535です。0でもPPUアドレスの設定とラッチのリセットは行いますが、PPUDATAへは書きません。転送先の容量確認や分割処理はなく、大きな値ではPPUアドレス空間を周回し得ます。
戻り値
ありません(void)。
使用条件と制限
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
PPU直接アクセスは、描画停止中か、処理全体が安全なVBlank時間内に収まる条件で行います。これらの関数は待機・キュー登録・NMI抑止・スクロール復元を行いません。呼び出し中に割り込みで共有ラッチやアドレスを変更しないでください。PPUCTRLのビット2により、書き込み先は1バイトごとに1または32進みます。ミラーリングとアドレスの周回はPPUの動作に従い、CHR-ROMへは書き込めません。アクセス後、描画を始める前に意図したスクロール状態を設定してください。
使用例
// Rendering/NMI are off; PPUCTRL increment is 1. Transfer 256 bytes now.
nes_ppu_stream_write(0x2040,source_bytes,256);
source_bytes[0]=3; // VRAM already holds tile 1 at the first blue cell.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ランタイムの例では直接転送と二つの待機方法を学びます。(0,16)に青い塗りつぶしタイル・左半分の棒・三角形の繰り返し、(0,96)に緑の塗りつぶしが表示されます。各領域は256タイル分のバイトからなる256×64ピクセルです。転送元を変更しても青の先頭タイルは塗りつぶしのままです。y=208のx=16・224・240には、アドレス設定・長さ0の書き込み・長さ0の埋め込みを示す赤い8×8の正方形、左半分4×8の棒、8行の三角形が表示されます。待機確認は両方1、FAILED CHECKSは0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:31
Write len source bytes directly through PPUDATA. Call only during an appropriate PPU access period; the address increment mode comes from the current PPUCTRL.
kitaqfc/lib/runtime.c:219
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len)
{
nes_ppu_seek(ppu_addr);
while (len != 0)
{
PPUDATA = *src;
src = src + 1;
len = len - 1;
}
}nes_vblank_wait
PPUSTATUSのビット7を読み、既に立っている通知を消費したうえで、新しいVBlankの通知を待ちます。NMIが無効でも使えますが、キューの実行やランタイムのNMIカウンター更新は行いません。ステータスを読むたびにVBlankフラグが消え、スクロール・アドレス共用の書き込みラッチもリセットされます。読み取りはNMIのタイミングに影響し得るため、NMI駆動の処理とは分けて使ってください。タイムアウト・転送・スクロール復元はありません。
書式
void nes_vblank_wait(void);戻り値
ありません(void)。
使用条件と制限
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
使用例
// With NMI disabled, poll a new PPU VBlank flag. No queued work is flushed.
nes_vblank_wait();blank_seen=1;
if((PPUSTATUS&0x80)!=0)failures++; // The wait's status read cleared the flag.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ランタイムの例では直接転送と二つの待機方法を学びます。(0,16)に青い塗りつぶしタイル・左半分の棒・三角形の繰り返し、(0,96)に緑の塗りつぶしが表示されます。各領域は256タイル分のバイトからなる256×64ピクセルです。転送元を変更しても青の先頭タイルは塗りつぶしのままです。y=208のx=16・224・240には、アドレス設定・長さ0の書き込み・長さ0の埋め込みを示す赤い8×8の正方形、左半分4×8の棒、8行の三角形が表示されます。待機確認は両方1、FAILED CHECKSは0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:26
Wait for the current VBlank to end, then for the next one to begin. Each PPUSTATUS read has hardware side effects, including clearing the VBlank flag.
kitaqfc/lib/runtime.c:196
void nes_vblank_wait(void)
{
while ((PPUSTATUS & 0x80) != 0)
{
}
while ((PPUSTATUS & 0x80) == 0)
{
}
}nes_vram_queue_clear
Cランタイムの保留中のキューレコードをすべて破棄し、nes_vram_queue_usedとnes_vram_queue_overflowを両方0にします。PPUへは書き込まず、バッファ内のデータ自体は消去しません。コンパイラの組み込みキューには影響しません。
書式
void nes_vram_queue_clear(void);戻り値
ありません(void)。
使用条件と制限
これはruntime.cがCPUアドレス0x0300–0x03BFに持つ、独立した192バイトのキューです。実データ付きレコードは3+lenバイト、塗りつぶしは4バイトを使います。nes_vram_queue_usedは符号化したレコードのバイト数で、PPUへの書き込み回数やVRAMの空き容量ではありません。__vramq_*の組み込みキューは形式が異なり、これらの関数では消去・照会・実行しません。
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
レコードを作る段階と実行する段階を分けてください。ロックやダブルバッファはありません。確実に準備するには追加中はNMIを無効にし、描画停止中に実行するか、VBlankに収まる量を準備してからNMIを有効にします。runtime.cはコミット呼び出しなしで、空でないキューを__nes_nmiから自動実行します。フラッシュには時間制限・分割・スクロール復元がありません。キューに入る量でもVBlank内に転送できるとは限りません。
使用例
nes_vram_queue_clear(); // Discard all records AND clear the overflow latch.
if(nes_vram_queue_used!=0 || nes_vram_queue_overflow!=0)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルはまずキューの192バイトを使い切り、追加を意図的に失敗させます。そのキューを消去し、長さ128の拒否を確認してから、145バイトを使う5レコードで255回のPPU書き込みを予約します。表示はCAPACITY 192、ENCODED BYTES 145、AFTER FLUSH 000、REJECTED LENGTH 001、OVERFLOW SEEN 001、FAILED CHECKS 000になります。拒否に関する表示はプログラム中に確認した結果を保存したもので、最終時点のフラグ値ではありません。
コピーした青いタイルは(0,16)、塗りつぶした緑のタイルは(0,64)から始まります。各領域は127タイルで、3行全部と4行目の31タイルを使い、最後の8×8セルは黒のままです。青は全面・半幅・三角の繰り返しで、転送元を変更しても先頭は全面タイルのままです。y=112では、x=16の赤い四角が長さ0の処理でも残り、x=224の赤い半幅タイルがNMIの自動実行を示し、取り消したx=248は黒のままです。検証は18,432ピクセルを比較し、画面描画中のVRAM書き込みが0件であることも確認します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:40
Discard pending VRAM commands and clear the overflow latch; no PPU writes occur.
kitaqfc/lib/runtime.c:31
void nes_vram_queue_clear(void)
{
nes_vram_queue_used = 0;
nes_vram_queue_overflow = 0;
}nes_vram_queue_nmi_flush
Cランタイムの保留レコードを順番にすべて実行します。コミットフラグは不要です。レコードごとにPPUアドレスを設定し、保存済みデータを転送するか、塗りつぶし値を繰り返し書き込みます。完了時にnes_vram_queue_usedを0にし、オーバーフローフラグは保持します。空のキューではPPUへアクセスしません。
書式
void nes_vram_queue_nmi_flush(void);戻り値
ありません(void)。
使用条件と制限
これはruntime.cがCPUアドレス0x0300–0x03BFに持つ、独立した192バイトのキューです。実データ付きレコードは3+lenバイト、塗りつぶしは4バイトを使います。nes_vram_queue_usedは符号化したレコードのバイト数で、PPUへの書き込み回数やVRAMの空き容量ではありません。__vramq_*の組み込みキューは形式が異なり、これらの関数では消去・照会・実行しません。
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
レコードを作る段階と実行する段階を分けてください。ロックやダブルバッファはありません。確実に準備するには追加中はNMIを無効にし、描画停止中に実行するか、VBlankに収まる量を準備してからNMIを有効にします。runtime.cはコミット呼び出しなしで、空でないキューを__nes_nmiから自動実行します。フラッシュには時間制限・分割・スクロール復元がありません。キューに入る量でもVBlank内に転送できるとは限りません。
CPUポインターではなく、PPUのバイトアドレス(0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。
使用例
// Rendering and NMI are disabled; PPUCTRL selects increment 1.
nes_vram_queue_nmi_flush(); // No commit flag: consume all queued records now.
after_flush=nes_vram_queue_used;
if(after_flush!=0)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルはまずキューの192バイトを使い切り、追加を意図的に失敗させます。そのキューを消去し、長さ128の拒否を確認してから、145バイトを使う5レコードで255回のPPU書き込みを予約します。表示はCAPACITY 192、ENCODED BYTES 145、AFTER FLUSH 000、REJECTED LENGTH 001、OVERFLOW SEEN 001、FAILED CHECKS 000になります。拒否に関する表示はプログラム中に確認した結果を保存したもので、最終時点のフラグ値ではありません。
コピーした青いタイルは(0,16)、塗りつぶした緑のタイルは(0,64)から始まります。各領域は127タイルで、3行全部と4行目の31タイルを使い、最後の8×8セルは黒のままです。青は全面・半幅・三角の繰り返しで、転送元を変更しても先頭は全面タイルのままです。y=112では、x=16の赤い四角が長さ0の処理でも残り、x=224の赤い半幅タイルがNMIの自動実行を示し、取り消したx=248は黒のままです。検証は18,432ピクセルを比較し、画面描画中のVRAM書き込みが0件であることも確認します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:51
Consume complete records in order during a safe PPU access period. Reading PPUSTATUS resets the shared address latch before each address pair. The caller is responsible for ensuring the transfer fits its NMI/VBlank time budget.
kitaqfc/lib/runtime.c:127
void nes_vram_queue_nmi_flush(void)
{
unsigned char i;
unsigned char cmd;
unsigned char len;
unsigned char data_index;
unsigned char latch;
i = 0;
while (i != nes_vram_queue_used)
{
latch = PPUSTATUS;
PPUADDR = nes_vram_queue_data[i];
PPUADDR = nes_vram_queue_data[(unsigned char)(i + 1)];
cmd = nes_vram_queue_data[(unsigned char)(i + 2)];
if ((cmd & 0x80) != 0)
{
len = (unsigned char)(cmd & 0x7F);
data_index = nes_vram_queue_data[(unsigned char)(i + 3)];
while (len != 0)
{
PPUDATA = data_index;
len = len - 1;
}
i = (unsigned char)(i + 4);
}
else
{
len = cmd;
data_index = (unsigned char)(i + 3);
while (len != 0)
{
PPUDATA = nes_vram_queue_data[data_index];
data_index = data_index + 1;
len = len - 1;
}
i = data_index;
}
latch = latch;
}
nes_vram_queue_used = 0;
}nes_vram_queue_try_fill
Cランタイムのキューへ4バイトの塗りつぶしレコードを追加します。実行時にvalueをPPUDATAへlen回書き込みます。値はレコードへその場で保存するため、呼び出し元の変数を後から変えても塗りつぶし内容は変わりません。繰り返すのは1バイトで、タイルパターンではありません。
書式
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len);引数
ppu_addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
lenPPUへ書くバイト数で、範囲は0–127です。レコードの長さバイトのビット7を塗りつぶしの識別に使うため、128–255は拒否します。長さ0でもwriteはキューの3バイト、fillは4バイトを使います。どちらもフラッシュ時にPPUアドレスを設定しますが、PPUDATAへは書き込みません。
戻り値
レコード全体を追加できたら1、長さが不正または空き不足なら0を返します。拒否時はnes_vram_queue_overflowを1にし、既存レコードと使用量は変更しません。エラーフラグは解除するまで残りますが、空きに収まる後続の追加を妨げません。呼び出すたびに戻り値を確認してください。
使用条件と制限
これはruntime.cがCPUアドレス0x0300–0x03BFに持つ、独立した192バイトのキューです。実データ付きレコードは3+lenバイト、塗りつぶしは4バイトを使います。nes_vram_queue_usedは符号化したレコードのバイト数で、PPUへの書き込み回数やVRAMの空き容量ではありません。__vramq_*の組み込みキューは形式が異なり、これらの関数では消去・照会・実行しません。
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
レコードを作る段階と実行する段階を分けてください。ロックやダブルバッファはありません。確実に準備するには追加中はNMIを無効にし、描画停止中に実行するか、VBlankに収まる量を準備してからNMIを有効にします。runtime.cはコミット呼び出しなしで、空でないキューを__nes_nmiから自動実行します。フラッシュには時間制限・分割・スクロール復元がありません。キューに入る量でもVBlank内に転送できるとは限りません。
使用例
if(nes_vram_queue_try_fill(0x2100,fill_value,127)!=1)failures++;
fill_value=3; // The queued fill remains the solid tile 1.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルはまずキューの192バイトを使い切り、追加を意図的に失敗させます。そのキューを消去し、長さ128の拒否を確認してから、145バイトを使う5レコードで255回のPPU書き込みを予約します。表示はCAPACITY 192、ENCODED BYTES 145、AFTER FLUSH 000、REJECTED LENGTH 001、OVERFLOW SEEN 001、FAILED CHECKS 000になります。拒否に関する表示はプログラム中に確認した結果を保存したもので、最終時点のフラグ値ではありません。
コピーした青いタイルは(0,16)、塗りつぶした緑のタイルは(0,64)から始まります。各領域は127タイルで、3行全部と4行目の31タイルを使い、最後の8×8セルは黒のままです。青は全面・半幅・三角の繰り返しで、転送元を変更しても先頭は全面タイルのままです。y=112では、x=16の赤い四角が長さ0の処理でも残り、x=224の赤い半幅タイルがNMIの自動実行を示し、取り消したx=248は黒のままです。検証は18,432ピクセルを比較し、画面描画中のVRAM書き込みが0件であることも確認します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:47
Queue a four-byte fill record: address high/low, length with bit 7 set, value. Reject lengths above 127 and latch overflow on any capacity/format failure.
kitaqfc/lib/runtime.c:90
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len)
{
unsigned char used;
unsigned char need;
used = nes_vram_queue_used;
need = 4;
if ((unsigned char)(used + need) < used)
{
nes_vram_queue_overflow = 1;
return 0;
}
if ((unsigned char)(used + need) > 192)
{
nes_vram_queue_overflow = 1;
return 0;
}
if ((len & 0x80) != 0)
{
nes_vram_queue_overflow = 1;
return 0;
}
nes_vram_queue_data[used] = (unsigned char)(ppu_addr >> 8);
nes_vram_queue_data[(unsigned char)(used + 1)] = (unsigned char)ppu_addr;
nes_vram_queue_data[(unsigned char)(used + 2)] = (unsigned char)(len | 0x80);
nes_vram_queue_data[(unsigned char)(used + 3)] = value;
nes_vram_queue_used = (unsigned char)(used + need);
return 1;
}nes_vram_queue_try_write
Cランタイムのキューへ実データ付きレコードを追加します。この呼び出し中に転送元のlenバイトをキューへコピーし、最後に使用量を更新します。PPUへの転送はフラッシュ時に行います。成功後に転送元を変更・再利用しても、キュー内のデータは変わりません。
書式
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len);引数
ppu_addrCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。src現在マップされているメモリ内の、少なくとも
lenバイトを読み取れる領域へのポインターです。転送元を有効に保つ必要があるのはこの呼び出し中だけで、データをコピーし、転送元へは書き込みません。長さ0なら転送元を読みません。lenPPUへ書くバイト数で、範囲は0–127です。レコードの長さバイトのビット7を塗りつぶしの識別に使うため、128–255は拒否します。長さ0でもwriteはキューの3バイト、fillは4バイトを使います。どちらもフラッシュ時にPPUアドレスを設定しますが、PPUDATAへは書き込みません。
戻り値
レコード全体を追加できたら1、長さが不正または空き不足なら0を返します。拒否時はnes_vram_queue_overflowを1にし、既存レコードと使用量は変更しません。エラーフラグは解除するまで残りますが、空きに収まる後続の追加を妨げません。呼び出すたびに戻り値を確認してください。
使用条件と制限
これはruntime.cがCPUアドレス0x0300–0x03BFに持つ、独立した192バイトのキューです。実データ付きレコードは3+lenバイト、塗りつぶしは4バイトを使います。nes_vram_queue_usedは符号化したレコードのバイト数で、PPUへの書き込み回数やVRAMの空き容量ではありません。__vramq_*の組み込みキューは形式が異なり、これらの関数では消去・照会・実行しません。
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
レコードを作る段階と実行する段階を分けてください。ロックやダブルバッファはありません。確実に準備するには追加中はNMIを無効にし、描画停止中に実行するか、VBlankに収まる量を準備してからNMIを有効にします。runtime.cはコミット呼び出しなしで、空でないキューを__nes_nmiから自動実行します。フラッシュには時間制限・分割・スクロール復元がありません。キューに入る量でもVBlank内に転送できるとは限りません。
使用例
if(nes_vram_queue_try_write(0x2040,source_bytes,127)!=1)failures++;
source_bytes[0]=3; // The queued first tile stays 1: payload was copied immediately.呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルはまずキューの192バイトを使い切り、追加を意図的に失敗させます。そのキューを消去し、長さ128の拒否を確認してから、145バイトを使う5レコードで255回のPPU書き込みを予約します。表示はCAPACITY 192、ENCODED BYTES 145、AFTER FLUSH 000、REJECTED LENGTH 001、OVERFLOW SEEN 001、FAILED CHECKS 000になります。拒否に関する表示はプログラム中に確認した結果を保存したもので、最終時点のフラグ値ではありません。
コピーした青いタイルは(0,16)、塗りつぶした緑のタイルは(0,64)から始まります。各領域は127タイルで、3行全部と4行目の31タイルを使い、最後の8×8セルは黒のままです。青は全面・半幅・三角の繰り返しで、転送元を変更しても先頭は全面タイルのままです。y=112では、x=16の赤い四角が長さ0の処理でも残り、x=224の赤い半幅タイルがNMIの自動実行を示し、取り消したx=248は黒のままです。検証は18,432ピクセルを比較し、画面描画中のVRAM書き込みが0件であることも確認します。実機では未検証です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:44
Copy a literal payload into the queue and publish its new used length last. Accept lengths 0..127; reject larger lengths and latch overflow without changing the queue. The caller must ensure queue production cannot race its NMI consumer.
kitaqfc/lib/runtime.c:40
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len)
{
unsigned char used;
unsigned char need;
unsigned char i;
unsigned char dst_index;
// Bit 7 selects a fill record; accepting it here would misdecode a literal
// and lengths 253..255 would also wrap the byte-sized allocation cost.
if ((len & 0x80) != 0)
{
nes_vram_queue_overflow = 1;
return 0;
}
used = nes_vram_queue_used;
// Each literal record costs three header bytes plus len payload bytes.
need = (unsigned char)(len + 3);
if ((unsigned char)(used + need) < used)
{
nes_vram_queue_overflow = 1;
return 0;
}
if ((unsigned char)(used + need) > 192)
{
nes_vram_queue_overflow = 1;
return 0;
}
nes_vram_queue_data[used] = (unsigned char)(ppu_addr >> 8);
nes_vram_queue_data[(unsigned char)(used + 1)] = (unsigned char)ppu_addr;
nes_vram_queue_data[(unsigned char)(used + 2)] = len;
i = 0;
dst_index = (unsigned char)(used + 3);
while (i != len)
{
nes_vram_queue_data[dst_index] = src[i];
dst_index = dst_index + 1;
i = i + 1;
}
nes_vram_queue_used = (unsigned char)(used + need);
return 1;
}nes_wait_nmi
呼び出し時に記録した値からnes_nmi_counterが変わるまで待ちます。この8ビットカウンターはruntime.cの__nes_nmiがキュー処理後に進めます。不一致で判定するため255から0への折り返しにも対応します。NMIを有効にし、このハンドラーか同等のカウンター更新処理を使ってください。タイムアウトはなく、NMIの有効化やスクロールの復元は行いません。経過時間を厳密に1フレームへ固定する関数でもありません。
書式
void nes_wait_nmi(void);戻り値
ありません(void)。
使用条件と制限
宣言にはruntime.hを使い、runtime.cを一度だけインクルードまたはコンパイルします。このファイルはキューに加え、__nes_nmi、ランタイムのフレームカウンター、PPU直接操作、ページ02のOAM領域も提供します。プログラムのNMI処理と整合させてください。関数を宣言しただけでは実装は組み込まれません。
使用例
__scroll_set(0,0);start_counter=nes_nmi_counter;
__ppu_ctrl_set(0x80); // Enable runtime.c's NMI handler before waiting.
nes_wait_nmi(); // Returns after the runtime completion counter changes.
nmi_seen=(u8)(nes_nmi_counter!=start_counter);
if(nmi_seen!=1)failures++;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
ランタイムの例では直接転送と二つの待機方法を学びます。(0,16)に青い塗りつぶしタイル・左半分の棒・三角形の繰り返し、(0,96)に緑の塗りつぶしが表示されます。各領域は256タイル分のバイトからなる256×64ピクセルです。転送元を変更しても青の先頭タイルは塗りつぶしのままです。y=208のx=16・224・240には、アドレス設定・長さ0の書き込み・長さ0の埋め込みを示す赤い8×8の正方形、左半分4×8の棒、8行の三角形が表示されます。待機確認は両方1、FAILED CHECKSは0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/runtime.h:23
Wait until the NMI handler advances its counter. NMI must already be enabled and able to execute; this polling loop has no timeout.
kitaqfc/lib/runtime.c:185
void nes_wait_nmi(void)
{
unsigned char start;
start = nes_nmi_counter;
while (nes_nmi_counter == start)
{
}
}scene.h — シーンの登録、変更と更新
シーンの画像データは、共有の分割転送状態を使ってVRAMキューへ順次コピーできます。stepの完了と、キュー消費によるPPUへの反映完了を区別してください。
シーン表のenter・update・draw・exitを使い、タイトル画面やプレイ画面の処理を切り替えます。
nes_scene_stream_begin
転送先・転送元・長さ・チャンクを保存し、共有の分割転送を開始状態にします。
書式
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk);引数
ppu_addr転送先のPPUアドレス。CPUアドレスではありません。
src転送元の
lenバイト。全量がキューへ入るまで読み取り可能に保ちます。len転送するバイト数、0~65535。転送先領域の範囲も呼び出し側で確認します。
chunk1キューレコードの最大バイト数。0は32、128以上は127に制限されます。1回のstep全体の上限ではありません。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
同時に管理できる転送は1件です。beginは進行中の転送を置き換えますが、すでにキューへコピーしたデータは取り消しません。転送元の寿命・ROMバンクを保ち、stepとキュー消費を呼び出し側で進めてください。
使用例
nes_scene_stream_begin(0x2100,small,4,2);result[0]=nes_scene_stream_state.remaining;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
キューへ分割してコピーし、消費処理でPPUへ反映する順序を学びます。960バイトのタイル・64バイトの属性・32バイトのパレットを転送し、(16,32)に赤い64×32矩形を表示します。beginだけでは表示は変わりません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/scene.h:47
Replace the single active transfer and retain its source pointer. Keep the source storage and any required ROM bank readable until all bytes are queued.
kitaqfc/lib/scene.c:86
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk)
{
nes_scene_stream_state.ppu_addr = ppu_addr;
nes_scene_stream_state.src = src;
nes_scene_stream_state.remaining = len;
nes_scene_stream_state.chunk = chunk;
nes_scene_stream_state.active = 1;
}nes_scene_stream_begin_attr
ネームテーブル先頭+0x03C0へ属性64バイトを、1レコード16バイトで転送する準備をします。
書式
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64);引数
nt_baseネームテーブルの先頭PPUアドレス。例:0x2000。
src64属性テーブルの64バイト。全量がキューへ入るまで保持します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
同時に管理できる転送は1件です。beginは進行中の転送を置き換えますが、すでにキューへコピーしたデータは取り消しません。転送元の寿命・ROMバンクを保ち、stepとキュー消費を呼び出し側で進めてください。
使用例
nes_scene_stream_begin_attr(0x2000,attrs);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
キューへ分割してコピーし、消費処理でPPUへ反映する順序を学びます。960バイトのタイル・64バイトの属性・32バイトのパレットを転送し、(16,32)に赤い64×32矩形を表示します。beginだけでは表示は変わりません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/scene.h:51
Prepare the 64-byte attribute table at nametable base + 0x03C0.
kitaqfc/lib/scene.c:102
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64)
{
nes_scene_stream_begin((unsigned short)(nt_base + 0x03C0), src64, 64, 16);
}nes_scene_stream_begin_nametable
ネームテーブルのタイル部分960バイトを、1レコード32バイトで転送する準備をします。
書式
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960);引数
nt_baseネームテーブルの先頭PPUアドレス。例:0x2000。
src960ネームテーブルのタイル部分960バイト。属性64バイトは含めません。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
同時に管理できる転送は1件です。beginは進行中の転送を置き換えますが、すでにキューへコピーしたデータは取り消しません。転送元の寿命・ROMバンクを保ち、stepとキュー消費を呼び出し側で進めてください。
使用例
nes_scene_stream_begin_nametable(0x2000,tiles960);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();result[4]=nes_scene_stream_state.remaining;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
キューへ分割してコピーし、消費処理でPPUへ反映する順序を学びます。960バイトのタイル・64バイトの属性・32バイトのパレットを転送し、(16,32)に赤い64×32矩形を表示します。beginだけでは表示は変わりません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/scene.h:49
Prepare the 960-byte tile portion of a nametable using 32-byte queue records.
kitaqfc/lib/scene.c:96
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960)
{
nes_scene_stream_begin(nt_base, src960, 960, 32);
}nes_scene_stream_begin_palette
PPUの0x3F00へパレット32バイトを、1レコード16バイトで転送する準備をします。
書式
void nes_scene_stream_begin_palette(unsigned char* src32);引数
src32背景16色・スプライト16色の32バイト。色コードとPPUパレットのミラーに注意します。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
同時に管理できる転送は1件です。beginは進行中の転送を置き換えますが、すでにキューへコピーしたデータは取り消しません。転送元の寿命・ROMバンクを保ち、stepとキュー消費を呼び出し側で進めてください。
使用例
nes_scene_stream_begin_palette(colors);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
キューへ分割してコピーし、消費処理でPPUへ反映する順序を学びます。960バイトのタイル・64バイトの属性・32バイトのパレットを転送し、(16,32)に赤い64×32矩形を表示します。beginだけでは表示は変わりません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/scene.h:53
Prepare a 32-byte palette transfer to PPU address 0x3F00.
kitaqfc/lib/scene.c:108
void nes_scene_stream_begin_palette(unsigned char* src32)
{
nes_scene_stream_begin(0x3F00, src32, 32, 16);
}nes_scene_stream_step
残りデータを、VRAMキューに入る限り複数チャンクに分けてコピーします。
書式
unsigned char nes_scene_stream_step(void);戻り値
キューが満杯で続きが残れば1、全量がキューに入ったか非アクティブなら0。失敗したチャンクの位置は進まず、次回再試行できます。0でもPPUへの転送待ちが残る場合があります。
使用条件と制限
同時に管理できる転送は1件です。beginは進行中の転送を置き換えますが、すでにキューへコピーしたデータは取り消しません。転送元の寿命・ROMバンクを保ち、stepとキュー消費を呼び出し側で進めてください。
使用例
result[1]=nes_scene_stream_step();result[2]=nes_scene_stream_state.active;result[3]=nes_vram_queue_used;nes_vram_queue_nmi_flush();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
キューへ分割してコピーし、消費処理でPPUへ反映する順序を学びます。960バイトのタイル・64バイトの属性・32バイトのパレットを転送し、(16,32)に赤い64×32矩形を表示します。beginだけでは表示は変わりません。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr実装とソース
kitaqfc/lib/scene.h:57
Queue as many chunks as available capacity allows, capped at 127 bytes each. Return one when queue exhaustion requires a later retry, zero when all data has been queued. Zero does not mean the NMI consumer has already written the PPU.
kitaqfc/lib/scene.c:116
unsigned char nes_scene_stream_step(void)
{
unsigned char chunk;
while (nes_scene_stream_state.active != 0)
{
if (nes_scene_stream_state.remaining == 0)
{
nes_scene_stream_state.active = 0;
return 0;
}
// A step may enqueue several chunks; chunk limits record size, not the total bytes or time per frame.
chunk = nes_scene_stream_state.chunk;
if (chunk == 0)
{
chunk = 32;
}
if ((unsigned short)chunk > nes_scene_stream_state.remaining)
{
chunk = (unsigned char)nes_scene_stream_state.remaining;
}
if (chunk > 127)
{
chunk = 127;
}
// A failed enqueue leaves this chunk pending so a later step can retry after the queue drains.
if (nes_vram_queue_try_write(nes_scene_stream_state.ppu_addr, nes_scene_stream_state.src, chunk) == 0)
{
return 1;
}
nes_scene_stream_state.ppu_addr = (unsigned short)(nes_scene_stream_state.ppu_addr + chunk);
nes_scene_stream_state.src = nes_scene_stream_state.src + chunk;
nes_scene_stream_state.remaining = (unsigned short)(nes_scene_stream_state.remaining - chunk);
}
return 0;
}scene_change
現在のシーンを終了させ、指定したシーンを開始します。
書式
void scene_change(u8 scene_id);引数
scene_id開始するシーンの添字。0~count−1です。
戻り値
戻り値はありません。
使用条件と制限
範囲外IDは無視します。有効な変更ではexit→ID更新・変更フラグ設定→enterの順です。同じIDの指定でもexitとenterを実行します。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:33
Ignore invalid IDs; otherwise exit the current scene and enter the requested one. Selecting the current ID still performs exit/enter. Callbacks run synchronously.
kitaqfc/lib/scene.c:28
void scene_change(u8 scene_id)
{
SceneFunc fn;
if (scene_id >= kq_scene_count) return;
// Exit/enter handlers must not recursively change scenes or replace this shared table.
if (kq_scene_has_current != 0) {
fn = kq_scene_table[(__safe_index u8)kq_scene_current].exit;
if (fn != 0) fn();
}
kq_scene_current = scene_id;
kq_scene_has_current = 1;
kq_scene_changed = 1;
fn = kq_scene_table[(__safe_index u8)kq_scene_current].enter;
if (fn != 0) fn();
}scene_draw
アクティブなシーンのdraw処理を一回呼びます。
書式
void scene_draw();戻り値
戻り値はありません。
使用条件と制限
アクティブなシーンがない場合とdrawが0の場合は何もしません。変更フラグは消去しません。画面の転送やフレーム待機はコールバック側で設計します。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
使用例
scene_draw();result[15]=events;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0のdrawで12、シーン1のdrawで22がイベント列に入ることを確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:38
Call the active scene's draw handler if one exists; no current scene is a no-op.
kitaqfc/lib/scene.c:61
void scene_draw()
{
SceneFunc fn;
if (kq_scene_has_current == 0) return;
fn = kq_scene_table[(__safe_index u8)kq_scene_current].draw;
if (fn != 0) fn();
}scene_get_current
保存されている現在シーンのIDを読み出します。
書式
u8 scene_get_current();戻り値
保存されている8ビットIDです。初期化直後も0を返します。
使用条件と制限
0という戻り値だけではシーン0が開始済みか判別できません。初期化後に最初の scene_change() を行ったかを呼び出し側で管理してください。
使用例
result[16]=scene_get_current();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0の開始後は0、シーン1への変更後は1を取得します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:40
Return the stored scene ID; zero is also returned before the first transition.
kitaqfc/lib/scene.c:71
u8 scene_get_current()
{
return kq_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の中から再帰的にシーン変更や表の置換をしないでください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:28
Attach the caller-owned scene table and clear current-scene state without invoking callbacks.
kitaqfc/lib/scene.c:11
void scene_init(const SceneDef* scenes, u8 count)
{
kq_scene_table = scenes;
kq_scene_count = count;
kq_scene_current = 0;
kq_scene_changed = 0;
kq_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の中から再帰的にシーン変更や表の置換をしないでください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:30
Replace the table by resetting scene state; this does not call the old scene's exit handler.
kitaqfc/lib/scene.c:21
void scene_set_table(const SceneDef* scenes, u8 count)
{
scene_init(scenes, count);
}scene_update
アクティブなシーンのupdate処理を一回呼びます。
書式
void scene_update();戻り値
戻り値はありません。
使用条件と制限
アクティブなシーンがなければ何もしません。ある場合は呼び出し前に変更フラグを消すため、update内で起きたシーン変更は呼び出し後にも残ります。シーン表は呼び出し側の所有です。使用中は表と通常の関数ポインターの参照先を有効に保ってください。各コールバックは0なら呼びません。enter/exitの中から再帰的にシーン変更や表の置換をしないでください。
使用例
scene_update();result[13]=events;result[14]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
シーン0のupdateでイベント11を記録します。直前に1だった変更フラグは通常のupdate後に0になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:36
Clear the change flag before calling the active scene's update handler, so a transition made during that handler is visible afterward.
kitaqfc/lib/scene.c:50
void scene_update()
{
SceneFunc fn;
if (kq_scene_has_current == 0) return;
kq_scene_changed = 0;
fn = kq_scene_table[(__safe_index u8)kq_scene_current].update;
if (fn != 0) fn();
}scene_was_changed
シーン変更が記録されているかを調べます。
書式
u8 scene_was_changed();戻り値
変更フラグが立っていれば1、消えていれば0です。
使用条件と制限
読み出しでは消しません。初期化・表の置換と、アクティブなシーンのupdate開始時に消えます。無効IDの指定は変更しません。
使用例
result[17]=scene_was_changed();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
変更直後の1、通常のupdate後の0、表を置き換えた後の0を確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/scene.h:42
Read the transition flag, which is cleared at the start of an active scene update.
kitaqfc/lib/scene.c:77
u8 scene_was_changed()
{
return kq_scene_changed;
}scroll.h — 背景・ウィンドウ・分割スクロール
16ビットのワールド座標からPPUCTRLのネームテーブル選択とPPUSCROLLを作ります。Yも256単位で分ける方式なので、240ピクセルの画面高さとの差を考慮してください。
nes_camera_follow_center
対象ワールド座標から画面アンカーを引き、カメラ座標として保存します。
書式
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y);引数
target_x対象のワールドX座標、0~65535ピクセル。
target_y対象のワールドY座標、0~65535ピクセル。
center_x対象を表示したい画面X座標、0~255。
center_y対象を表示したい画面Y座標、0~255。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
状態は1組を共有します。実際の表示更新には nes_scroll_apply が必要です。引き算の負の結果は u16 で周回するため、ワールド端での制限はゲーム側で行います。
使用例
nes_camera_follow_center(300,260,128,120);result[4]=nes_scroll_camera_x;result[5]=nes_scroll_camera_y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
保存した16ビット座標からPPUCTRLとスクロールを作る例です。基本値144、座標(264,272)の適用値は147です。対象(300,260)をアンカー(128,120)に合わせるとカメラは(172,140)になります。別の状態検証ではPPUに実際に書かれた値も確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr使用例
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦ミラーのNROMでネームテーブル1を表示します。ワールド(288,32)の赤い32×16矩形が、カメラ(264,16)により画面(24,16)へ表示されます。座標bit8によるテーブル選択と、下位バイトのスクロールを画像で確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical実装とソース
kitaqfc/lib/scroll.h:21
Subtract the screen anchor from the world point into unsigned camera coordinates. Underflow wraps; world-edge clamping belongs to the game.
kitaqfc/lib/scroll.c:56
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y)
{
nes_scroll_camera_x = target_x - center_x;
nes_scroll_camera_y = target_y - center_y;
}nes_scroll_apply
保存した座標と基本PPUCTRL値を組み合わせ、ラッチをリセットしてPPUCTRL・X/Yスクロールを書きます。
書式
void nes_scroll_apply(void);戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
X/Yともbit8をネームテーブル選択、下位8ビットをスクロールに使います。Yの240~255は通常の縦スクロール座標として扱えません。PPUCTRLは全体を書き換えるため、基本値でNMIなども適切に指定してください。VBlank待ちはなく、PPUSTATUSの読み取りはVBlankフラグと共有ラッチにも影響します。
使用例
nes_scroll_apply();result[3]=nes_scroll_ctrl_base|((nes_scroll_camera_x>>8)&1)|((nes_scroll_camera_y>>7)&2);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
保存した16ビット座標からPPUCTRLとスクロールを作る例です。基本値144、座標(264,272)の適用値は147です。対象(300,260)をアンカー(128,120)に合わせるとカメラは(172,140)になります。別の状態検証ではPPUに実際に書かれた値も確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr使用例
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦ミラーのNROMでネームテーブル1を表示します。ワールド(288,32)の赤い32×16矩形が、カメラ(264,16)により画面(24,16)へ表示されます。座標bit8によるテーブル選択と、下位バイトのスクロールを画像で確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical実装とソース
kitaqfc/lib/scroll.h:18
Combine bit 8 of each camera coordinate with base PPUCTRL, reset the latch by reading PPUSTATUS, and write low-byte X/Y scroll. This uses 256-unit wrapping on both axes rather than normalizing Y to a 240-pixel nametable height. Initialize base control to the intended PPUCTRL value, including NMI enable, before applying. Only coordinate bits 0..8 are used; arrange safe PPU timing in the caller.
kitaqfc/lib/scroll.c:34
void nes_scroll_apply(void)
{
unsigned char ctrl;
unsigned char x_hi;
unsigned char y_hi;
unsigned char latch;
x_hi = (unsigned char)((nes_scroll_camera_x >> 8) & 1);
y_hi = (unsigned char)((nes_scroll_camera_y >> 8) & 1);
ctrl = (unsigned char)(nes_scroll_ctrl_base | x_hi | (unsigned char)(y_hi << 1));
// Replace the whole control register, so the saved base also determines NMI and increment mode.
PPUCTRL = ctrl;
latch = PPUSTATUS;
PPUSCROLL = (unsigned char)nes_scroll_camera_x;
PPUSCROLL = (unsigned char)nes_scroll_camera_y;
latch = latch;
}nes_scroll_set
16ビットのカメラ座標を保存します。PPUへはまだ書きません。
書式
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y);引数
camera_x保存する横座標、整数ピクセル。適用時は下位9ビットを使います。
camera_y保存する縦座標。適用時は下位9ビットを使い、240ピクセルの高さへの正規化は行いません。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
状態は1組を共有します。実際の表示更新には nes_scroll_apply が必要です。引き算の負の結果は u16 で周回するため、ワールド端での制限はゲーム側で行います。
使用例
nes_scroll_set(264,272);result[1]=nes_scroll_camera_x;result[2]=nes_scroll_camera_y;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
保存した16ビット座標からPPUCTRLとスクロールを作る例です。基本値144、座標(264,272)の適用値は147です。対象(300,260)をアンカー(128,120)に合わせるとカメラは(172,140)になります。別の状態検証ではPPUに実際に書かれた値も確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr使用例
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦ミラーのNROMでネームテーブル1を表示します。ワールド(288,32)の赤い32×16矩形が、カメラ(264,16)により画面(24,16)へ表示されます。座標bit8によるテーブル選択と、下位バイトのスクロールを画像で確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical実装とソース
kitaqfc/lib/scroll.h:12
Store camera coordinates without touching hardware registers.
kitaqfc/lib/scroll.c:25
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y)
{
nes_scroll_camera_x = camera_x;
nes_scroll_camera_y = camera_y;
}nes_scroll_set_base_ctrl
PPUCTRLの基本値を保存し、ネームテーブル選択用の下位2ビットを消します。
書式
void nes_scroll_set_base_ctrl(unsigned char ctrl);引数
ctrlNMI許可・パターンテーブル・増分などを含むPPUCTRL値。下位2ビットは適用時に座標から求めます。
戻り値
戻り値はありません。変更される状態と反映時期は、この項目の説明に従います。
使用条件と制限
状態は1組を共有します。実際の表示更新には nes_scroll_apply が必要です。引き算の負の結果は u16 で周回するため、ワールド端での制限はゲーム側で行います。
使用例
nes_scroll_set_base_ctrl(0x93);result[0]=nes_scroll_ctrl_base;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
保存した16ビット座標からPPUCTRLとスクロールを作る例です。基本値144、座標(264,272)の適用値は147です。対象(300,260)をアンカー(128,120)に合わせるとカメラは(172,140)になります。別の状態検証ではPPUに実際に書かれた値も確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr使用例
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
縦ミラーのNROMでネームテーブル1を表示します。ワールド(288,32)の赤い32×16矩形が、カメラ(264,16)により画面(24,16)へ表示されます。座標bit8によるテーブル選択と、下位バイトのスクロールを画像で確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical実装とソース
kitaqfc/lib/scroll.h:10
Store PPUCTRL bits other than the two nametable-selection bits for later application.
kitaqfc/lib/scroll.c:19
void nes_scroll_set_base_ctrl(unsigned char ctrl)
{
nes_scroll_ctrl_base = (unsigned char)(ctrl & 0xFC);
}sprite.h — OBJの確保、配置、メタスプライト、アニメーション
スプライトは通常8×8または8×16ピクセルの移動可能な表示物体で、OAM(Object Attribute Memory)の項目で指定します。スロットを確保し、作業用OAMへ位置・タイル・属性を設定してから転送します。複数スロットを1物体として扱うのがメタスプライトです。GBは40スロット・1走査線10個、FCは64スロット・1走査線8個が上限です。
FC属性はビット0・1が4種類のスプライトパレット選択、5が背景の色番号0以外より奥への配置、6・7がX・Y反転です。未使用の2〜4は0にします。GBとは値が異なるため、各機種の定数を使ってください。
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枚おきには進みません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:74
Advance animation ticks and wrap the frame index, returning the selected tile. Zero ticks-per-frame freezes advancement; null state returns zero.
kitaqfc/lib/sprite.c:174
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 = (u8)(anim->ticks + 1);
if (anim->ticks >= anim->ticks_per_frame) {
anim->ticks = 0;
anim->frame = (u8)(anim->frame + 1);
if (anim->frame >= anim->frame_count) anim->frame = 0;
}
return (u8)(anim->first_tile + anim->frame);
}metasprite_draw
個数を指定したスプライト部品の配列を連続スロットへ配置します。各部品は符号付きの位置差・タイル・属性を持ちます。空きスロットは有効にし、使用中のスロットは上書きして、末尾の上限で停止します。
書式
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ビット値で渡します。タイル座標ではありません。
yOAMに格納するY値です。画面の1行目以降に上端を置く場合は表示上端−1を渡します。239〜255は240行の表示範囲外です。
partscount個以上のMetaSpritePartを読み出せる配列です。各4バイトの要素は符号付きdx・dy、タイル、属性の順で、終端記号は使いません。
count部品数を符号なし8ビット値で指定します。0なら書き込みません。連続スロットがSPRITE_MAXに達した場合は、そこまでの部品だけを書き込みます。
戻り値
書き込んだ部品数を0〜countで返します。スロット上限で打ち切られたかどうかは、この値で確認します。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
符号付き8ビットの位置差を基準座標に加え、結果を8ビットに折り返します。呼び出し中は部品配列全体を参照可能にしてください。使用中のスロットも上書きするため、別の物体のスロットは保護されません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:71
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.
kitaqfc/lib/sprite.c:152
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count)
{
u8 i;
i = 0;
while (i < count) {
u8 id;
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(id, (u8)(x + parts[i].dx), (u8)(y + parts[i].dy), parts[i].tile, parts[i].flags);
kq_sprite_x[id] = (u8)(x + parts[i].dx);
kq_sprite_y[id] = (u8)(y + parts[i].dy);
i = (u8)(i + 1);
}
return count;
}sprite_alloc
空いている最初のスプライトスロットを確保し、位置を設定するまで非表示にします。非表示でも確保は維持されます。表示前にタイルと属性を設定してください。
書式
u8 sprite_alloc(void);戻り値
確保したスロットIDを返します。全スロットが使用中なら0xFFです。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:45
Claim and hide the first free slot, or return 0xFF if all slots are active.
kitaqfc/lib/sprite.c:35
u8 sprite_alloc(void)
{
u8 i;
i = 0;
while (i < SPRITE_MAX) {
if (kq_sprite_active[i] == 0) {
kq_sprite_active[i] = 1;
kq_sprite_used = (u8)(kq_sprite_used + 1);
sprite_hide(i);
return i;
}
i = (u8)(i + 1);
}
return 0xFF;
}sprite_count_used
非表示のスプライトやmetasprite_drawで有効にしたスロットも含め、確保済みの数を返します。画面に見えている数ではなく、割り当て済みの数です。
書式
u8 sprite_count_used(void);戻り値
0〜SPRITE_MAXの符号なし8ビット値です。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:62
Return the number of active slots, including slots activated by metasprite_draw. Hidden allocated slots remain active until sprite_free releases them.
kitaqfc/lib/sprite.c:108
u8 sprite_count_used(void)
{
return kq_sprite_used;
}sprite_flush_oam
NMIのフレームカウンター更新を待ち、256バイトの作業用OAMページを転送します。NMIを有効にし、ハンドラーでそのカウンターが更新される必要があります。
書式
void sprite_flush_oam(void);戻り値
ありません(void)。
使用条件と制限
FC版はOAMADDRを0にし、CPUページ$02($0200〜$02FF)からDMAを開始します。VBlank中または描画停止中に使用し、割り込み側と同じ作業用ページを同時更新しないでください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:59
Wait for NMI before requesting OAM DMA.
kitaqfc/lib/sprite.c:100
void sprite_flush_oam(void)
{
__nmi_wait();
__oam_dma();
}sprite_flush_oam_now
作業用OAMを直ちにハードウェアへ転送します。安全な表示期間になるまで待つ処理は含まれないため、呼び出し側で実行時期を決めます。
書式
void sprite_flush_oam_now(void);戻り値
ありません(void)。
使用条件と制限
FC版はOAMADDRを0にし、CPUページ$02($0200〜$02FF)からDMAを開始します。VBlank中または描画停止中に使用し、割り込み側と同じ作業用ページを同時更新しないでください。
使用例
// 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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:57
Transfer intrinsic shadow OAM immediately; this wrapper does not wait for VBlank.
kitaqfc/lib/sprite.c:94
void sprite_flush_oam_now(void)
{
__oam_dma();
}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で転送します。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:47
Release an active valid slot and hide it; invalid/repeated frees are ignored.
kitaqfc/lib/sprite.c:52
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 = (u8)(kq_sprite_used - 1);
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で転送します。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:55
Mark the software position off screen and hide its OAM entry without freeing it.
kitaqfc/lib/sprite.c:85
void sprite_hide(u8 id)
{
if (sprite_valid(id) == 0) return;
kq_sprite_x[id] = 0;
kq_sprite_y[id] = 0xF0;
__sprite_hide(id);
}sprite_init
スプライトの割り当て状態を初期化し、OAMの作業用データで全スロットを非表示にします。ソフトウェア側の高さは8ピクセルになります。実機のスプライトサイズ設定とOAM転送は別に行います。
書式
void sprite_init(void);戻り値
ありません(void)。
使用条件と制限
FC版の初期化は確保状態と管理用座標を初期化しますが、OAMの非表示処理ではタイルと属性を保持します。SPRITE_MAXは1〜64にしてください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:43
Reset allocation/position state and hide each OAM slot through the intrinsic. The software height defaults to eight; hardware sprite size is configured elsewhere.
kitaqfc/lib/sprite.c:19
void sprite_init(void)
{
u8 i;
kq_sprite_used = 0;
kq_sprite_height = 8;
i = 0;
while (i < SPRITE_MAX) {
kq_sprite_active[i] = 0;
kq_sprite_x[i] = 0;
kq_sprite_y[i] = 0;
__sprite_hide(i);
i = (u8)(i + 1);
}
}sprite_max_scanline_count
管理している縦位置とkq_sprite_heightから、表示範囲の1走査線に重なる有効スプライトの最大数を求めます。
書式
u8 sprite_max_scanline_count(void);戻り値
0〜SPRITE_MAXの符号なし8ビット値です。
使用条件と制限
kq_sprite_heightを実機の高さ8または16に合わせてください。この推定は透明ピクセル、横方向の表示範囲、ハードウェア優先順位を考慮しません。FC版は管理用座標を読むため、OAM組み込み関数で直接変更した位置は反映されません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:67
Estimate peak overlap across 240 lines from software Y/height values. This does not model pixel transparency, OAM priority or every hardware overflow quirk.
kitaqfc/lib/sprite.c:117
u8 sprite_max_scanline_count(void)
{
u8 line;
u8 best;
line = 0;
best = 0;
while (line < 240) {
u8 i;
u8 count;
i = 0;
count = 0;
while (i < SPRITE_MAX) {
if (kq_sprite_active[i] != 0) {
if (line > kq_sprite_y[i] && (u8)(line - kq_sprite_y[i]) <= kq_sprite_height) {
count = (u8)(count + 1);
}
}
i = (u8)(i + 1);
}
if (count > best) best = count;
line = (u8)(line + 1);
}
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で転送します。
FC属性はビット0・1が4種類のスプライトパレット選択、5が背景の色番号0以外より奥への配置、6・7がX・Y反転です。未使用の2〜4は0にします。GBとは値が異なるため、各機種の定数を使ってください。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:53
Replace the hardware attribute byte of a valid OAM slot.
kitaqfc/lib/sprite.c:78
void sprite_set_flags(u8 id, u8 flags)
{
if (sprite_valid(id) == 0) return;
__sprite_attr(id, flags);
}sprite_set_pos
FCスプライトの管理用座標と作業用OAM座標を更新します。Xは画面上の位置ですが、YはOAMへ格納する値です。画面の32ピクセル目を上端にする場合はY=31を渡します。
書式
void sprite_set_pos(u8 id, u8 x, u8 y);引数
id0〜SPRITE_MAX-1のスロットIDです。標準ではGBが0〜39、FCが0〜63です。範囲外は無視します。
xピクセル単位の横位置を符号なし8ビット値で渡します。タイル座標ではありません。
yOAMに格納するY値です。画面の1行目以降に上端を置く場合は表示上端−1を渡します。239〜255は240行の表示範囲外です。
戻り値
ありません(void)。
使用条件と制限
位置・タイル・属性・非表示の操作は作業用データを変更します。スロットの確保やハードウェアOAMの即時更新は行いません。1フレーム分の更新後にsprite_flush_oamで転送します。
座標の加算は8ビットで行い、256で折り返します。画面範囲へのクリッピングは行いません。範囲内のスロットなら、未確保でも内容を変更できます。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:49
Update software coordinates and the intrinsic OAM position for a valid slot.
kitaqfc/lib/sprite.c:62
void sprite_set_pos(u8 id, u8 x, u8 y)
{
if (sprite_valid(id) == 0) return;
kq_sprite_x[id] = x;
kq_sprite_y[id] = y;
__sprite_move(id, x, y);
}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で転送します。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:51
Replace the tile byte of a valid OAM slot without changing allocation state.
kitaqfc/lib/sprite.c:71
void sprite_set_tile(u8 id, u8 tile)
{
if (sprite_valid(id) == 0) return;
__sprite_tile(id, tile);
}sprite_warn_scanline_overflow
sprite_max_scanline_countを、GBでは10個、FCでは8個という1走査線の上限と比較します。ソフトウェアの推定結果を返す処理であり、ハードウェアのオーバーフローフラグは読みません。
書式
u8 sprite_warn_scanline_overflow(void);戻り値
推定値が1走査線の上限を超えると1、それ以外は0です。
使用条件と制限
kq_sprite_heightを実機の高さ8または16に合わせてください。この推定は透明ピクセル、横方向の表示範囲、ハードウェア優先順位を考慮しません。FC版は管理用座標を読むため、OAM組み込み関数で直接変更した位置は反映されません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/sprite.h:64
Flag software overlap estimates above eight sprites on one scanline.
kitaqfc/lib/sprite.c:143
u8 sprite_warn_scanline_overflow(void)
{
return (u8)(sprite_max_scanline_count() > 8);
}system.h — 起動、フレーム数、待機と割り込み
フレーム待機回数の管理、待機後のコールバック、CPU割り込みの許可・禁止を扱います。
fc_frame_count
fc_frame_countはsystem_get_frameを呼ぶ別名です。待機関数を通過した回数を16ビットのソフトウェアカウンターから読みます。
フレーム待機ラッパーの16ビットカウンターを読み出します。
書式
u16 fc_frame_count(void);戻り値
現在の0~65535のカウンター値です。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
result[3]=fc_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した後に3を読みます。サンプルでは折り返し確認のためカウンターを65535へ設定し、次の待機後に0になることも確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:30
#define fc_frame_count system_get_framekitaqfc/lib/system.c:30
u16 system_get_frame(void)
{
return kq_system_frame;
}fc_init
fc_initはsystem_initを呼ぶ別名です。ソフトウェアのフレーム数とコールバック登録を初期化します。
フレーム管理を初期化し、カウンターと登録コールバックを消去します。
書式
void fc_init(void);戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
fc_init();result[0]=fc_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後のフレーム番号が0であることを確認してからコールバックを登録します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:27
#define fc_init system_initkitaqfc/lib/system.c:7
void system_init(void)
{
kq_system_frame = 0;
system_vblank_callback = 0;
// NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
__nmi_enable();
}fc_set_vblank_callback
fc_set_vblank_callbackはsystem_set_vblank_callbackを呼ぶ別名です。フレーム待機後に同期的に呼ぶコールバックを登録・解除します。
フレーム待機後に呼ぶ処理を登録、置換、または解除します。
書式
void fc_set_vblank_callback(SystemCallback callback);引数
callback呼び出す関数。0なら登録を解除します。
戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
引数なし・戻り値なしの通常の関数ポインターです。バンク番号は保持しません。呼び出される間は対象コードが読み出せる状態にしてください。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
fc_set_vblank_callback(on_frame);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
登録した関数が3回呼ばれた後、0を設定して解除します。次の待機でフレームは4へ進みますが、呼び出し回数は3のままです。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:29
#define fc_set_vblank_callback system_set_vblank_callbackkitaqfc/lib/system.c:24
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}fc_wait_vblank
fc_wait_vblankはsystem_wait_vblankを呼ぶ別名です。フレーム待機を行い、ソフトウェアのフレーム数を進めて登録済みコールバックを呼びます。
次のフレーム待機を行い、カウンターを加算してから登録コールバックを呼びます。
書式
void fc_wait_vblank(void);戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
コールバックはこの関数内から同期的に呼ばれます。割り込みハンドラーとして登録されるわけではありません。カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
fc_wait_vblank();fc_wait_vblank();fc_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回呼び出すとコールバックも3回実行され、コールバックが読むカウンターは3になります。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:28
#define fc_wait_vblank system_wait_vblankkitaqfc/lib/system.c:16
void system_wait_vblank(void)
{
__nmi_wait();
kq_system_frame = (u16)(kq_system_frame + 1);
if (system_vblank_callback != 0) system_vblank_callback();
}gb_frame_count
gb_frame_countはsystem_get_frameを呼ぶ別名です。待機関数を通過した回数を16ビットのソフトウェアカウンターから読みます。
フレーム待機ラッパーの16ビットカウンターを読み出します。
書式
u16 gb_frame_count(void);戻り値
現在の0~65535のカウンター値です。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
FCライブラリでのgb_という綴りです。この項目はFC側のsystem_*を呼び、Game Boyの機器を操作するものではありません。fc_で始まる対応する名前も同じFC実装を呼びます。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
result[3]=gb_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した後に3を読みます。サンプルでは折り返し確認のためカウンターを65535へ設定し、次の待機後に0になることも確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:34
#define gb_frame_count system_get_framekitaqfc/lib/system.c:30
u16 system_get_frame(void)
{
return kq_system_frame;
}gb_init
gb_initはsystem_initを呼ぶ別名です。ソフトウェアのフレーム数とコールバック登録を初期化します。
フレーム管理を初期化し、カウンターと登録コールバックを消去します。
書式
void gb_init(void);戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
FCライブラリでのgb_という綴りです。この項目はFC側のsystem_*を呼び、Game Boyの機器を操作するものではありません。fc_で始まる対応する名前も同じFC実装を呼びます。
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
gb_init();result[0]=gb_frame_count();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後のフレーム番号が0であることを確認してからコールバックを登録します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:31
#define gb_init system_initkitaqfc/lib/system.c:7
void system_init(void)
{
kq_system_frame = 0;
system_vblank_callback = 0;
// NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
__nmi_enable();
}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を、完成サンプルのビルド例に従って使います。
FCライブラリでのgb_という綴りです。この項目はFC側のsystem_*を呼び、Game Boyの機器を操作するものではありません。fc_で始まる対応する名前も同じFC実装を呼びます。
引数なし・戻り値なしの通常の関数ポインターです。バンク番号は保持しません。呼び出される間は対象コードが読み出せる状態にしてください。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
gb_set_vblank_callback(on_frame);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
登録した関数が3回呼ばれた後、0を設定して解除します。次の待機でフレームは4へ進みますが、呼び出し回数は3のままです。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:33
#define gb_set_vblank_callback system_set_vblank_callbackkitaqfc/lib/system.c:24
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}gb_wait_vblank
gb_wait_vblankはsystem_wait_vblankを呼ぶ別名です。フレーム待機を行い、ソフトウェアのフレーム数を進めて登録済みコールバックを呼びます。
次のフレーム待機を行い、カウンターを加算してから登録コールバックを呼びます。
書式
void gb_wait_vblank(void);戻り値
戻り値はありません。
使用条件と制限
ヘッダーの#defineで同じ実装名へ置き換わります。引数の評価、戻り値、状態の読み書きは参照先と同じで、別の状態や処理は追加しません。入力はinput.hとinput.c、システム制御はsystem.hとsystem.cを、完成サンプルのビルド例に従って使います。
FCライブラリでのgb_という綴りです。この項目はFC側のsystem_*を呼び、Game Boyの機器を操作するものではありません。fc_で始まる対応する名前も同じFC実装を呼びます。
コールバックはこの関数内から同期的に呼ばれます。割り込みハンドラーとして登録されるわけではありません。カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:32
#define gb_wait_vblank system_wait_vblankkitaqfc/lib/system.c:16
void system_wait_vblank(void)
{
__nmi_wait();
kq_system_frame = (u16)(kq_system_frame + 1);
if (system_vblank_callback != 0) system_vblank_callback();
}system_disable_interrupts
CPUのマスク可能な割り込みを禁止します。
書式
void system_disable_interrupts(void);戻り値
戻り値はありません。
使用条件と制限
必要な共有データの更新を短く保ち、更新後は適切に割り込みを再開してください。フレームの待機条件とは区別して使用します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
system_disable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
サンプルでは禁止と再開を対にして呼びます。CPUの割り込み状態は専用の検証プログラムでも確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:25
Disable maskable CPU IRQs; this does not disable the PPU's NMI source.
kitaqfc/lib/system.c:48
void system_disable_interrupts(void)
{
__irq_disable();
}system_enable_interrupts
CPUのマスク可能な割り込みを許可します。
書式
void system_enable_interrupts(void);戻り値
戻り値はありません。
使用条件と制限
許可する前に割り込み元とハンドラーを適切に準備してください。FCではNMIの許可・禁止とは独立しています。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
system_enable_interrupts();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
禁止した割り込みを再開します。FCのサンプルはAPUのフレームIRQとDMC IRQを止めてから許可し、未処理IRQの連続発生を避けます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:23
Enable maskable CPU IRQs through the intrinsic; NMI is controlled separately.
kitaqfc/lib/system.c:42
void system_enable_interrupts(void)
{
__irq_enable();
}system_get_frame
フレーム待機ラッパーの16ビットカウンターを読み出します。
書式
u16 system_get_frame(void);戻り値
現在の0~65535のカウンター値です。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
result[3]=system_get_frame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した後に3を読みます。サンプルでは折り返し確認のためカウンターを65535へ設定し、次の待機後に0になることも確認します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:19
Return the wrapping 16-bit count of completed waits through this wrapper.
kitaqfc/lib/system.c:30
u16 system_get_frame(void)
{
return kq_system_frame;
}system_get_frame8
フレームカウンターの下位8ビットを取り出します。短い周期の処理に利用できます。
書式
u8 system_get_frame8(void);戻り値
0~255の下位バイトです。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
result[4]=system_get_frame8();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
3回待機した時点では3、16ビットカウンターが折り返した時点では0を得ます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:21
Return the low byte of the software frame counter.
kitaqfc/lib/system.c:36
u8 system_get_frame8(void)
{
return (u8)kq_system_frame;
}system_init
フレーム管理を初期化し、カウンターと登録コールバックを消去します。
書式
void system_init(void);戻り値
戻り値はありません。
使用条件と制限
カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
system_init();result[0]=system_get_frame();呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
初期化直後のフレーム番号が0であることを確認してからコールバックを登録します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:13
Reset the software frame counter and callback storage, then enable NMI.
kitaqfc/lib/system.c:7
void system_init(void)
{
kq_system_frame = 0;
system_vblank_callback = 0;
// NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
__nmi_enable();
}system_set_vblank_callback
フレーム待機後に呼ぶ処理を登録、置換、または解除します。
書式
void system_set_vblank_callback(SystemCallback callback);引数
callback呼び出す関数。0なら登録を解除します。
戻り値
戻り値はありません。
使用条件と制限
引数なし・戻り値なしの通常の関数ポインターです。バンク番号は保持しません。呼び出される間は対象コードが読み出せる状態にしてください。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
system_set_vblank_callback(on_frame);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
登録した関数が3回呼ばれた後、0を設定して解除します。次の待機でフレームは4へ進みますが、呼び出し回数は3のままです。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:17
Replace the per-wait callback; null disables callback dispatch.
kitaqfc/lib/system.c:24
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}system_wait_vblank
次のフレーム待機を行い、カウンターを加算してから登録コールバックを呼びます。
書式
void system_wait_vblank(void);戻り値
戻り値はありません。
使用条件と制限
コールバックはこの関数内から同期的に呼ばれます。割り込みハンドラーとして登録されるわけではありません。カウンターはこのラッパーを通った待機回数であり、経過したハードウェアフレームを自動計測するものではありません。16ビットで折り返します。
FC版の初期化はNMIを許可します。待機にはNMIが実際に動作する必要があります。CPUのIRQ禁止だけではNMIは止まりません。
使用例
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
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/system.h:15
Wait for NMI, count the completed wait, then invoke the registered callback.
kitaqfc/lib/system.c:16
void system_wait_vblank(void)
{
__nmi_wait();
kq_system_frame = (u16)(kq_system_frame + 1);
if (system_vblank_callback != 0) system_vblank_callback();
}tilemap.h — タイルマップの矩形操作
行優先のタイル配列を読み、タイルIDのしきい値で衝突を判定します。矩形判定は四隅のみで、内側すべての走査ではありません。
nes_tilemap_get
指定タイル番号を読み、幅・高さの範囲外なら255を返します。
書式
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty);引数
map行優先タイル配列・幅・高さ・衝突しきい値を持つ有効なマップ。配列を呼び出し中に読める状態に保ちます。
txタイルX座標。1単位は8ピクセルです。
tyタイルY座標。1単位は8ピクセルです。
戻り値
タイルの1バイト値または255。保存値255と範囲外は区別できません。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[1]=nes_tilemap_get(&map,2,1);result[2]=nes_tilemap_get(&map,5,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:19
Return a tile byte, or 0xFF when coordinates exceed map dimensions. The map/storage pointers must be valid; 0xFF may also be an actual stored tile.
kitaqfc/lib/tilemap.c:32
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
if (tx >= map->width)
{
return 0xFF;
}
if (ty >= map->height)
{
return 0xFF;
}
return map->tiles[nes_tilemap_index(map, tx, ty)];
}nes_tilemap_index
タイル座標を行優先配列のバイト位置 ty*width+tx に変換します。
書式
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty);引数
map行優先タイル配列・幅・高さ・衝突しきい値を持つ有効なマップ。配列を呼び出し中に読める状態に保ちます。
txタイルX座標。1単位は8ピクセルです。
tyタイルY座標。1単位は8ピクセルです。
戻り値
計算した16ビット位置。座標の範囲検証は行いません。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[0]=nes_tilemap_index(&map,2,1);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:16
Compute a row-major byte offset by adding the row stride ty times. This internal arithmetic helper does not validate coordinates.
kitaqfc/lib/tilemap.c:14
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
unsigned short index;
unsigned char y;
index = tx;
y = 0;
while (y != ty)
{
index = (unsigned short)(index + map->width);
y = y + 1;
}
return index;
}nes_tilemap_point_solid
指定タイルを solid_from 以上かどうかで衝突判定します。255と範囲外は常に衝突扱いです。
書式
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty);引数
map行優先タイル配列・幅・高さ・衝突しきい値を持つ有効なマップ。配列を呼び出し中に読める状態に保ちます。
txタイルX座標。1単位は8ピクセルです。
tyタイルY座標。1単位は8ピクセルです。
戻り値
衝突扱いなら1、それ以外は0。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[3]=nes_tilemap_point_solid(&map,2,1);result[4]=nes_tilemap_point_solid(&map,0,0);result[5]=nes_tilemap_point_solid(&map,5,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:22
Treat out-of-bounds/0xFF tiles as solid, then compare other tile IDs with the map's solid_from threshold.
kitaqfc/lib/tilemap.c:47
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
unsigned char tile;
tile = nes_tilemap_get(map, tx, ty);
if (tile == 0xFF)
{
return 1;
}
if (tile >= map->solid_from)
{
return 1;
}
return 0;
}nes_tilemap_world_box_solid
ワールド矩形の左上・右上・左下・右下の4点を調べます。
書式
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height);引数
map行優先タイル配列・幅・高さ・衝突しきい値を持つ有効なマップ。配列を呼び出し中に読める状態に保ちます。
world_xワールドX、整数ピクセル。周回を避けるなら2048未満にします。
world_yワールドY、整数ピクセル。周回を避けるなら2048未満にします。
width矩形幅、1~255ピクセル。右端
world_x+width-1も2048未満にします。height矩形高さ、1~255ピクセル。下端
world_y+height-1も2048未満にします。
戻り値
四隅のどれかが衝突扱いなら1、すべて通過可能なら0。
使用条件と制限
幅・高さは0にできません。辺の途中や内部は調べないため、大きな矩形内の障害物を見逃す場合があります。本サンプルでは中央に障害物があっても四隅が空なら0になります。
使用例
result[12]=nes_tilemap_world_box_solid(&map,8,8,16,8);result[13]=nes_tilemap_world_box_solid(&map,0,0,40,24);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:33
Test only the four corners of a nonempty world box. Interior/edge tiles between the corners are not scanned, so large boxes can miss obstacles. Width/height must be nonzero and endpoint arithmetic must not overflow.
kitaqfc/lib/tilemap.c:86
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height)
{
unsigned short x2;
unsigned short y2;
x2 = (unsigned short)(world_x + width - 1);
y2 = (unsigned short)(world_y + height - 1);
if (nes_tilemap_world_point_solid(map, world_x, world_y) != 0)
{
return 1;
}
if (nes_tilemap_world_point_solid(map, x2, world_y) != 0)
{
return 1;
}
if (nes_tilemap_world_point_solid(map, world_x, y2) != 0)
{
return 1;
}
if (nes_tilemap_world_point_solid(map, x2, y2) != 0)
{
return 1;
}
return 0;
}nes_tilemap_world_point_solid
ワールドの点を8ピクセル単位のタイルへ変換して衝突判定します。
書式
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y);引数
map行優先タイル配列・幅・高さ・衝突しきい値を持つ有効なマップ。配列を呼び出し中に読める状態に保ちます。
world_xワールドX、整数ピクセル。周回を避けるなら2048未満にします。
world_yワールドY、整数ピクセル。周回を避けるなら2048未満にします。
戻り値
対応するタイルが衝突扱いなら1、それ以外は0。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[10]=nes_tilemap_world_point_solid(&map,16,8);result[11]=nes_tilemap_world_point_solid(&map,0,0);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:29
Convert a world point to tile coordinates and apply the tile solidity rule.
kitaqfc/lib/tilemap.c:77
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y)
{
return nes_tilemap_point_solid(map, nes_world_to_tile8_x(world_x), nes_world_to_tile8_y(world_y));
}nes_world_to_tile8_x
ワールドXを8で割り、1バイトのタイルXに変換します。
書式
unsigned char nes_world_to_tile8_x(unsigned short world_x);引数
world_xワールドX、整数ピクセル。周回を避けるなら2048未満にします。
戻り値
(world_x >> 3) の下位8ビット。2048ピクセルごとに周回します。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[6]=nes_world_to_tile8_x(23);result[7]=nes_world_to_tile8_x(24);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:25
Convert pixels to eight-pixel tile X and narrow to a byte. Keep world X below 2048 when wraparound is not intended.
kitaqfc/lib/tilemap.c:65
unsigned char nes_world_to_tile8_x(unsigned short world_x)
{
return (unsigned char)(world_x >> 3);
}nes_world_to_tile8_y
ワールドYを8で割り、1バイトのタイルYに変換します。
書式
unsigned char nes_world_to_tile8_y(unsigned short world_y);引数
world_yワールドY、整数ピクセル。周回を避けるなら2048未満にします。
戻り値
(world_y >> 3) の下位8ビット。2048ピクセルごとに周回します。
使用条件と制限
マップと非空の配列には有効なポインターが必要です。衝突情報はタイルIDのしきい値で表し、別の当たり判定マスクは読みません。
使用例
result[8]=nes_world_to_tile8_y(15);result[9]=nes_world_to_tile8_y(16);呼び出し部分のコード例です。完全なプログラムに組み込む際は、記載した初期化とバッファの条件を満たしてください。
期待する結果
5×3マップの(2,1)に障害タイル5を置きます。配列位置は7、範囲外は255。障害に触れる四隅判定は1ですが、40×24の矩形の中央だけに障害がある場合は0です。四隅判定の限界を確認する例です。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr実装とソース
kitaqfc/lib/tilemap.h:27
Convert pixels to eight-pixel tile Y with the same byte-coordinate limit.
kitaqfc/lib/tilemap.c:71
unsigned char nes_world_to_tile8_y(unsigned short world_y)
{
return (unsigned char)(world_y >> 3);
}vram.h — VRAM更新の予約と転送
後で実行するVRAM書き込みコマンドのキューを空にし、オーバーフローの記録を消します。画面の消去、タイル画像の転送、ハードウェアVRAMの確保は行いません。
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
__vramq_commitで組み込み関数のキューをコミットし、__nmi_waitでNMIの処理を待ちます。標準ハンドラーが準備済みキューを処理します。NMIとそのハンドラーを有効にして使ってください。この呼び出しは大きな転送を複数回のVBlankへ分割しません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
vram_clear_queue
保留中のVRAMコマンドを実行せずに破棄し、あふれフラグも解除します。
書式
void vram_clear_queue(void);戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:15
Discard queued commands and explicitly reset the overflow latch.
kitaqfc/lib/vram.c:17
void vram_clear_queue(void)
{
__vramq_clear();
__vramq_clear_overflow();
}vram_flush
__vramq_commitで組み込み関数のキューをコミットし、__nmi_waitでNMIの処理を待ちます。標準ハンドラーが準備済みキューを処理します。NMIとそのハンドラーを有効にして使ってください。この呼び出しは大きな転送を複数回のVBlankへ分割しません。
書式
void vram_flush(void);戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
CPUポインターではなく、PPUのバイトアドレス(0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。
使用例
vram_flush(); // One small write through the VBlank/NMI path.
__scroll_set(0,0);
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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:41
Commit queued records for consumption and wait for the NMI path.
kitaqfc/lib/vram.c:115
void vram_flush(void)
{
__vramq_commit();
__nmi_wait();
}vram_flush_now
__vramq_execを直ちに呼びます。__vramq_commitで準備完了フラグが立っている場合だけ実行し、未コミットのキューはそのまま残ります。実行が完了すると長さと準備完了フラグを消しますが、オーバーフローの記録は残します。この関数はコミットも、安全なPPU書き込み時期の待機も行いません。
書式
void vram_flush_now(void);戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらのAPIはロックやダブルバッファーを提供しません。容量はコマンドバッファーの容量であり、ハードウェアVRAMの空きバイト数ではありません。
CPUポインターではなく、PPUのバイトアドレス(0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。
使用例
// Rendering is stopped here. On FC, commit readiness before direct execution.
__vramq_commit();
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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:39
Invoke the queue executor directly. Its ready flag must already be set by commit, and the caller supplies appropriate PPU timing.
kitaqfc/lib/vram.c:109
void vram_flush_now(void)
{
__vramq_exec();
}vram_get_overflowed
キューあふれフラグを消さずに読み取ります。過去の登録失敗が記録されたままの場合があります。
書式
u8 vram_get_overflowed(void);戻り値
オーバーフローの記録がなければ0です。登録を拒否すると1になり、明示的にリセットするまで残ります。キューを実行してもこの記録は消えません。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:52
Read the overflow latch without clearing it.
kitaqfc/lib/vram.c:144
u8 vram_get_overflowed(void)
{
return __vramq_overflow();
}vram_get_queue_capacity
現在の使用量にかかわらず、コマンドバッファーの総容量を返します。
書式
u8 vram_get_queue_capacity(void);戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:50
Return the queue's total capacity in bytes, independent of its current usage. This describes the command buffer, not free space in PPU VRAM.
kitaqfc/lib/vram.c:138
u8 vram_get_queue_capacity(void)
{
return __vramq_capacity();
}vram_get_queue_free
コマンドバッファーの総容量から現在の使用量を引いた空き容量を返します。次の記録や一連の処理が全部収まるか、登録前に確認してください。この取得関数は領域を予約しません。
書式
u8 vram_get_queue_free(void);戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:47
Return the remaining command-buffer capacity in bytes. A command requires space for its complete encoded record, including its address and metadata. Read before committing: NMI may consume a committed queue asynchronously.
kitaqfc/lib/vram.c:131
u8 vram_get_queue_free(void)
{
return (u8)(__vramq_capacity() - __vramq_len());
}vram_get_queue_used
実行待ちのコマンドが現在占めている、コマンドバッファーの使用量を返します。ビデオメモリーへ書き込むデータのバイト数ではありません。
書式
u8 vram_get_queue_used(void);戻り値
ここで説明した各機種の単位による符号なし8ビット値です。読み取るだけではキューを変更・実行しません。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:43
Return the encoded queue length in bytes, not the number of commands.
kitaqfc/lib/vram.c:122
u8 vram_get_queue_used(void)
{
return __vramq_len();
}vram_init
後で実行するVRAM書き込みコマンドのキューを空にし、オーバーフローの記録を消します。画面の消去、タイル画像の転送、ハードウェアVRAMの確保は行いません。
書式
void vram_init(void);戻り値
ありません(void)。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:13
Clear the intrinsic queue and its overflow latch.
kitaqfc/lib/vram.c:10
void vram_init(void)
{
__vramq_clear();
__vramq_clear_overflow();
}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タイルと矩形のコマンドは、コンパイル時の
VRAM_NT_BASEを使います。既定値は0x2000です。ブロック転送は指定したネームテーブル基底アドレス(0x2000、0x2400、0x2800、0x2C00)を使い、ミラーリングの影響も受けます。アドレスは登録時に1行32バイトとして計算されます。属性バイトを上書きしないよう、32×30タイルの範囲に収めてください。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です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに6 * hバイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
タイルと矩形のコマンドは、コンパイル時のVRAM_NT_BASEを使います。既定値は0x2000です。ブロック転送は指定したネームテーブル基底アドレス(0x2000、0x2400、0x2800、0x2C00)を使い、ミラーリングの影響も受けます。アドレスは登録時に1行32バイトとして計算されます。属性バイトを上書きしないよう、32×30タイルの範囲に収めてください。
1行が1記録になります。高さ0なら追加しませんが、幅0でも各行に1記録を使い、書き込みは行いません。行座標はu8に変換されるので、y + rowは256で折り返します。クリッピングや部分登録の取り消しは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:27
Queue each tightly packed source row as a pointer-based copy record. Keep source storage and its ROM bank accessible until queue execution finishes; partial queuing remains possible on overflow.
kitaqfc/lib/vram.c:54
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src)
{
u8 row;
row = 0;
while (row < h) {
__vramq_copy(vram_xy_addr(base, x, (u8)(y + row)), src, w);
src = src + w;
row = (u8)(row + 1);
}
return (u8)(__vramq_overflow() == 0);
}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です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに5 * hバイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
タイルと矩形のコマンドは、コンパイル時のVRAM_NT_BASEを使います。既定値は0x2000です。ブロック転送は指定したネームテーブル基底アドレス(0x2000、0x2400、0x2800、0x2C00)を使い、ミラーリングの影響も受けます。アドレスは登録時に1行32バイトとして計算されます。属性バイトを上書きしないよう、32×30タイルの範囲に収めてください。
1行が1記録になります。高さ0なら追加しませんが、幅0でも各行に1記録を使い、書き込みは行いません。行座標はu8に変換されるので、y + rowは256で折り返します。クリッピングや部分登録の取り消しは行いません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:23
Queue one fill per row. This is not transactional: capacity exhaustion can leave earlier rows queued, and the loop continues attempting later rows.
kitaqfc/lib/vram.c:40
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile)
{
u8 row;
row = 0;
while (row < h) {
__vramq_fill(vram_xy_addr(VRAM_NT_BASE, x, (u8)(y + row)), tile, w);
row = (u8)(row + 1);
}
return (u8)(__vramq_overflow() == 0);
}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です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに4バイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
タイルと矩形のコマンドは、コンパイル時のVRAM_NT_BASEを使います。既定値は0x2000です。ブロック転送は指定したネームテーブル基底アドレス(0x2000、0x2400、0x2800、0x2C00)を使い、ミラーリングの影響も受けます。アドレスは登録時に1行32バイトとして計算されます。属性バイトを上書きしないよう、32×30タイルの範囲に収めてください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:18
Queue one tile at the default nametable base. Return the current overflow status, which may include a failure from an earlier operation.
kitaqfc/lib/vram.c:25
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile)
{
__vramq_put(vram_xy_addr(VRAM_NT_BASE, x, y), tile);
return (u8)(__vramq_overflow() == 0);
}vram_queue_memcpy
srcからdstへ、連続したlenバイトをコピーする処理を登録します。転送元・転送先は1バイトずつ進み、タイルの行境界では飛びません。マップのバイトや書き込み可能なタイルパターン領域を転送できますが、形式変換や1タイル当たりのバイト数の計算は行いません。
書式
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);引数
dstCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
登録処理後にオーバーフローの記録がなければ1、記録があれば0です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに6 * ceil(len / 127)バイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
lenを最大127バイトずつに分割します。ceilは切り上げです。len=0なら記録を追加しません。127バイトへの分割はこのライブラリ関数の処理であり、下位の組み込み関数は符号なし8ビットの個数を受け付けます。オーバーフロー後も登録を試み、先に登録した記録は取り消しません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:31
Split a byte transfer into copy records of at most 127 bytes. The runtime retains source pointers rather than copying payloads into the queue. Return the overflow latch state after all records have been attempted.
kitaqfc/lib/vram.c:70
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len)
{
const u8* p;
u8 chunk;
p = (const u8*)src;
while (len != 0) {
if (len > 127) chunk = 127;
else chunk = (u8)len;
__vramq_copy(dst, p, chunk);
dst = (u16)(dst + chunk);
p = p + chunk;
len = (u16)(len - chunk);
}
return (u8)(__vramq_overflow() == 0);
}vram_queue_memset
dstから連続するlenバイトを、1バイトのvalueで埋める処理を登録します。繰り返すのは1バイトで、複数バイトからなるタイルパターンではありません。マップの32バイトの行間隔も自動では挿入しません。
書式
u8 vram_queue_memset(u16 dst, u8 value, u16 len);引数
dstCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。value0〜255の1バイトを繰り返し書きます。画面での意味は、転送先がタイル番号、画素のビットプレーン、その他のバイト列のどれかによって変わります。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
登録処理後にオーバーフローの記録がなければ1、記録があれば0です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに5 * ceil(len / 127)バイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
lenを最大127バイトずつに分割します。ceilは切り上げです。len=0なら記録を追加しません。127バイトへの分割はこのライブラリ関数の処理であり、下位の組み込み関数は符号なし8ビットの個数を受け付けます。オーバーフロー後も登録を試み、先に登録した記録は取り消しません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:36
Queue fills of at most 127 bytes each. Overflow can leave a partially queued transfer; no rollback is performed.
kitaqfc/lib/vram.c:94
u8 vram_queue_memset(u16 dst, u8 value, u16 len)
{
u8 chunk;
while (len != 0) {
if (len > 127) chunk = 127;
else chunk = (u8)len;
__vramq_fill(dst, value, chunk);
dst = (u16)(dst + chunk);
len = (u16)(len - chunk);
}
return (u8)(__vramq_overflow() == 0);
}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です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに4バイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
タイルと矩形のコマンドは、コンパイル時のVRAM_NT_BASEを使います。既定値は0x2000です。ブロック転送は指定したネームテーブル基底アドレス(0x2000、0x2400、0x2800、0x2C00)を使い、ミラーリングの影響も受けます。アドレスは登録時に1行32バイトとして計算されます。属性バイトを上書きしないよう、32×30タイルの範囲に収めてください。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:20
Alias the queued background-tile operation.
kitaqfc/lib/vram.c:32
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);引数
dstCPUポインターではなく、PPUのバイトアドレス(
0x0000~0x3FFF)です。転送先は書き込み可能でなければなりません。パターンの変更にはCHR ROMではなくCHR RAMが必要です。PPUのマッピングとミラーリングが適用されます。連続したコピーや埋め込みではPPUCTRLの増分を1にしてください。安全にPPUへ書ける時期に実行し、その後で必要なスクロール状態を設定してください。実行処理はPPUADDRとPPUDATAを使い、スクロール設定を復元しません。src実行時まで保存する、読み取り可能なバイト列への近距離ポインターです。実行が終わるまで必要な領域を有効に保ち、そのメモリー・ROMバンクを読み取り可能にしてください。バンク番号やデータのコピーは保存しません。長さがある転送には有効な元データが必要です。有効期間が終わったローカル配列を使わないでください。
len連続したバイト数を0〜65535で指定します。タイル数ではありません。全範囲が有効な転送元と書き込み可能な転送先に収まるようにしてください。範囲チェックや16ビットアドレスの折り返し防止は行いません。
戻り値
登録処理後にオーバーフローの記録がなければ1、記録があれば0です。今回の記録が収まっても、以前のオーバーフローにより0を返すことがあります。複数の記録を使う処理は一部だけ登録される場合があり、取り消しは行いません。長さ0の処理でも、記録済みのオーバーフローにより0を返す場合があります。
使用条件と制限
FCの単位は固定128バイトの組み込みバッファーに格納する、コマンド記録のバイト数です。タイル1個の書き込みは4バイト、埋める記録は5バイト、ポインターを使うコピー記録は6バイトで、管理情報も含みます。準備済みキューはNMIで消費されるため、コミット前に使用量の確認と登録を行い、登録側と実行側を調整してください。
この呼び出しを全て登録するには、コマンドバッファーに6 * ceil(len / 127)バイト必要です。式は管理情報を含む記録のサイズであり、転送するデータの量ではありません。複数記録の処理では、先に十分な空き容量があるか確認してください。
lenを最大127バイトずつに分割します。ceilは切り上げです。len=0なら記録を追加しません。127バイトへの分割はこのライブラリ関数の処理であり、下位の組み込み関数は符号なし8ビットの個数を受け付けます。オーバーフロー後も登録を試み、先に登録した記録は取り消しません。
キューの登録と実行は、互いに重ならないよう管理してください。割り込みで実行している途中の追加、管理変数の直接変更、保存した転送元データの意図しない上書きは避けてください。これらの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ピクセルです。
FCでは、上部の2つの正方形、3パターンのコピー、最後のVBlank経由の正方形が赤です。32×24の矩形と2パターンのコピーは青、3×2タイルのブロックと40×8の横棒は緑です。背景は黒で、描画した各画素の位置と色を検証します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vram.h:33
Alias the byte-copy queue helper; len remains a byte count, not a tile count.
kitaqfc/lib/vram.c:87
u8 vram_queue_tiles(u16 dst, const void* src, u16 len)
{
return vram_queue_memcpy(dst, src, len);
}vrc6_sound.h — VRC6拡張音源
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
VRC6_PULSE_DUTY_12/25/50/75は値0x00/0x10/0x20/0x30の名前です。接尾辞を実測デューティ比と解釈しないでください。KUROSAKIではコードdの比率は(d+1)/16です。
nes_vrc6_pulse1_off
VRC6パルス1の上位タイマー・有効化レジスターを0にして停止します。
書式
void nes_vrc6_pulse1_off(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:22
Disable pulse 1 by clearing its high timer/enable register.
kitaqfc/lib/vrc6_sound.c:43
void nes_vrc6_pulse1_off(void)
{
VRC6_P1_HI = 0x00;
}nes_vrc6_pulse1_set
VRC6パルス1の制御値と12ビットタイマーを書き、発音を有効にします。
書式
void nes_vrc6_pulse1_set(u8 control, u16 period);引数
controlVRC6パルス制御。bit7=ゲート、bit6~4=デューティコード、bit3~0=音量0~15。
period12ビットタイマー値0~4095。上位4ビットは捨てます。サンプルではパルス253、ノコギリ波289を使います。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:18
Write pulse-1 control and the low 12 timer bits, then enable the channel.
kitaqfc/lib/vrc6_sound.c:27
void nes_vrc6_pulse1_set(u8 control, u16 period)
{
VRC6_P1_CTRL = control;
VRC6_P1_LO = (u8)period;
VRC6_P1_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}nes_vrc6_pulse2_off
VRC6パルス2の上位タイマー・有効化レジスターを0にして停止します。
書式
void nes_vrc6_pulse2_off(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:24
Disable pulse 2 by clearing its high timer/enable register.
kitaqfc/lib/vrc6_sound.c:49
void nes_vrc6_pulse2_off(void)
{
VRC6_P2_HI = 0x00;
}nes_vrc6_pulse2_set
VRC6パルス2の制御値と12ビットタイマーを書き、発音を有効にします。
書式
void nes_vrc6_pulse2_set(u8 control, u16 period);引数
controlVRC6パルス制御。bit7=ゲート、bit6~4=デューティコード、bit3~0=音量0~15。
period12ビットタイマー値0~4095。上位4ビットは捨てます。サンプルではパルス253、ノコギリ波289を使います。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:20
Write pulse-2 control and the low 12 timer bits, then enable the channel.
kitaqfc/lib/vrc6_sound.c:35
void nes_vrc6_pulse2_set(u8 control, u16 period)
{
VRC6_P2_CTRL = control;
VRC6_P2_LO = (u8)period;
VRC6_P2_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}nes_vrc6_saw_off
VRC6ノコギリ波の上位タイマー・有効化レジスターを0にして停止します。
書式
void nes_vrc6_saw_off(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:28
Disable the saw channel by clearing its high timer/enable register.
kitaqfc/lib/vrc6_sound.c:63
void nes_vrc6_saw_off(void)
{
VRC6_SAW_HI = 0x00;
}nes_vrc6_saw_set
VRC6ノコギリ波の6ビット加算値と12ビットタイマーを書き、発音を有効にします。
書式
void nes_vrc6_saw_set(u8 rate, u16 period);引数
rateノコギリ波の加算値0~63。下位6ビットを使用します。
period12ビットタイマー値0~4095。上位4ビットは捨てます。サンプルではパルス253、ノコギリ波289を使います。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:26
Mask the saw accumulation rate to six bits, load a 12-bit timer and enable output.
kitaqfc/lib/vrc6_sound.c:55
void nes_vrc6_saw_set(u8 rate, u16 period)
{
VRC6_SAW_RATE = (u8)(rate & 0x3F);
VRC6_SAW_LO = (u8)period;
VRC6_SAW_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}nes_vrc6_silence_all
VRC6の3音源を無効化し、パルス制御とノコギリ波の加算値を0にします。
書式
void nes_vrc6_silence_all(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
このレジスター配線には--mapper=vrc6(mapper 24)を使います。VRC6を搭載しないカートリッジでは発音しません。periodの下位12ビットだけを使い、周波数制御レジスター$9003は設定しません。
使用例
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
// The background font occupies the first four 1 KiB CHR windows.
*((u8*)0xD000)=0; *((u8*)0xD001)=1;
*((u8*)0xD002)=2; *((u8*)0xD003)=3;
sound_begin("VRC6 PULSE PULSE SAW");
nes_vrc6_silence_all();
nes_vrc6_pulse1_set(0x7C,253);
sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
nes_vrc6_pulse2_set(0x3C,253);
sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
nes_vrc6_saw_set(32,289);
sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
nes_vrc6_silence_all();
sound_end();
}期待する結果
VRC6のパルス1、デューティの異なるパルス2、ノコギリ波を各45フレーム鳴らし、15フレームずつ休みます。3音の音程はおよそ440Hzで、最後は3音源とも停止します。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc6_sound.h:16
Clear channel-enable/high-timer registers and output controls for all VRC6 voices. Requires the VRC6 register layout used by this module. No mapper setup or global frequency-control write is included.
kitaqfc/lib/vrc6_sound.c:16
void nes_vrc6_silence_all(void)
{
VRC6_P1_HI = 0x00;
VRC6_P2_HI = 0x00;
VRC6_SAW_HI = 0x00;
VRC6_P1_CTRL = 0x00;
VRC6_P2_CTRL = 0x00;
VRC6_SAW_RATE = 0x00;
}vrc7_sound.h — VRC7 FM音源
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
nes_vrc7_channel_set
1つのFM音源の周波数・ブロック・キー・楽器・減衰を設定します。
書式
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags);引数
channelVRC7チャンネル0~5。6以上は何もしません。
instrument楽器番号0~15。0は独自パッチ、1~15はプリセットです。下位4ビットを使います。
volumeVRC7の減衰量0~15。0が最大音量で、15が最大減衰です。下位4ビットを使います。
fnum周波数番号0~511。ブロックと組み合わせて音程を決めます。下位9ビットを使います。
blockオクターブのブロック番号0~7。下位3ビットを使います。
key_flags0x10でキーオン、0x20でサステイン指定。ほかのビットは捨てます。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
楽器0は全チャンネルで共有します。channel_setはキー・周波数上位を楽器・減衰より先に書きます。key_off後の次の音は周波数上位とブロックも設定し直します。
使用例
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
// Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
*((u8*)0xA000)=0; *((u8*)0xA010)=1;
*((u8*)0xB000)=2; *((u8*)0xB010)=3;
*((u8*)0xC000)=4; *((u8*)0xC010)=5;
*((u8*)0xD000)=6; *((u8*)0xD010)=7;
sound_begin("VRC7 PRESET AND PATCH");
nes_vrc7_silence_all();
nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
nes_vrc7_set_user_patch(patch);
nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
sound_wait(60);
// Raw register 30 selects instrument zero and maximum attenuation.
nes_vrc7_write(0x30,0x0F);
nes_vrc7_key_off(0);
nes_vrc7_silence_all(); sound_wait(30);
sound_end();
}期待する結果
プリセット楽器1と、サステインを有効にした独自の楽器0を各60フレーム鳴らします。音程を揃えて音色を比較し、最後にキーオフと最大減衰で止めます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc7_sound.h:18
Validate channel 0..5, then pack a 9-bit frequency, 3-bit block, key flags, instrument and volume nibbles into its registers. Volume is the raw hardware attenuation field, not a linear loudness value.
kitaqfc/lib/vrc7_sound.c:32
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags)
{
u8 reg;
u8 hi;
if (channel >= 6) return;
reg = channel;
hi = (u8)(((fnum >> 8) & 0x01) | ((block & 0x07) << 1) | (key_flags & 0x30));
nes_vrc7_write((u8)(0x10 + reg), (u8)fnum);
// Key/frequency-high is written before instrument/attenuation; this sequence is not an atomic note update.
nes_vrc7_write((u8)(0x20 + reg), hi);
nes_vrc7_write((u8)(0x30 + reg), (u8)(((instrument & 0x0F) << 4) | (volume & 0x0F)));
}nes_vrc7_key_off
指定チャンネルの周波数上位・ブロック・キーのレジスター全体を0にします。
書式
void nes_vrc7_key_off(u8 channel);引数
channelVRC7チャンネル0~5。6以上は何もしません。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
楽器0は全チャンネルで共有します。channel_setはキー・周波数上位を楽器・減衰より先に書きます。key_off後の次の音は周波数上位とブロックも設定し直します。
使用例
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
// Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
*((u8*)0xA000)=0; *((u8*)0xA010)=1;
*((u8*)0xB000)=2; *((u8*)0xB010)=3;
*((u8*)0xC000)=4; *((u8*)0xC010)=5;
*((u8*)0xD000)=6; *((u8*)0xD010)=7;
sound_begin("VRC7 PRESET AND PATCH");
nes_vrc7_silence_all();
nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
nes_vrc7_set_user_patch(patch);
nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
sound_wait(60);
// Raw register 30 selects instrument zero and maximum attenuation.
nes_vrc7_write(0x30,0x0F);
nes_vrc7_key_off(0);
nes_vrc7_silence_all(); sound_wait(30);
sound_end();
}期待する結果
プリセット楽器1と、サステインを有効にした独自の楽器0を各60フレーム鳴らします。音程を揃えて音色を比較し、最後にキーオフと最大減衰で止めます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc7_sound.h:21
Clear the channel's entire frequency-high/block/key register, not just the key-on bit. A later note must restore frequency and block settings.
kitaqfc/lib/vrc7_sound.c:47
void nes_vrc7_key_off(u8 channel)
{
if (channel >= 6) return;
nes_vrc7_write((u8)(0x20 + channel), 0x00);
}nes_vrc7_set_user_patch
8バイトをVRC7の楽器0レジスターへ順番に書き込みます。
書式
void nes_vrc7_set_user_patch(const u8* patch8);引数
patch8楽器0の設定となる8バイト配列。NULLは検査しません。読み込み中にバンクを切り替えないでください。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
楽器0は全チャンネルで共有します。channel_setはキー・周波数上位を楽器・減衰より先に書きます。key_off後の次の音は周波数上位とブロックも設定し直します。
使用例
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
// Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
*((u8*)0xA000)=0; *((u8*)0xA010)=1;
*((u8*)0xB000)=2; *((u8*)0xB010)=3;
*((u8*)0xC000)=4; *((u8*)0xC010)=5;
*((u8*)0xD000)=6; *((u8*)0xD010)=7;
sound_begin("VRC7 PRESET AND PATCH");
nes_vrc7_silence_all();
nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
nes_vrc7_set_user_patch(patch);
nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
sound_wait(60);
// Raw register 30 selects instrument zero and maximum attenuation.
nes_vrc7_write(0x30,0x0F);
nes_vrc7_key_off(0);
nes_vrc7_silence_all(); sound_wait(30);
sound_end();
}期待する結果
プリセット楽器1と、サステインを有効にした独自の楽器0を各60フレーム鳴らします。音程を揃えて音色を比較し、最後にキーオフと最大減衰で止めます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc7_sound.h:14
Copy eight caller-supplied patch bytes into user-instrument registers 0..7.
kitaqfc/lib/vrc7_sound.c:16
void nes_vrc7_set_user_patch(const u8* patch8)
{
const u8* p;
u8 i;
p = patch8;
i = 0;
while (i < 8) {
nes_vrc7_write(i, *p);
p = p + 1;
i = (u8)(i + 1);
}
}nes_vrc7_silence_all
6音源すべてをキーオフし、楽器0・最大減衰に設定します。
書式
void nes_vrc7_silence_all(void);戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
楽器0は全チャンネルで共有します。channel_setはキー・周波数上位を楽器・減衰より先に書きます。key_off後の次の音は周波数上位とブロックも設定し直します。
使用例
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
// Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
*((u8*)0xA000)=0; *((u8*)0xA010)=1;
*((u8*)0xB000)=2; *((u8*)0xB010)=3;
*((u8*)0xC000)=4; *((u8*)0xC010)=5;
*((u8*)0xD000)=6; *((u8*)0xD010)=7;
sound_begin("VRC7 PRESET AND PATCH");
nes_vrc7_silence_all();
nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
nes_vrc7_set_user_patch(patch);
nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
sound_wait(60);
// Raw register 30 selects instrument zero and maximum attenuation.
nes_vrc7_write(0x30,0x0F);
nes_vrc7_key_off(0);
nes_vrc7_silence_all(); sound_wait(30);
sound_end();
}期待する結果
プリセット楽器1と、サステインを有効にした独自の楽器0を各60フレーム鳴らします。音程を揃えて音色を比較し、最後にキーオフと最大減衰で止めます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc7_sound.h:23
Key off all six voices and set instrument zero with maximum attenuation.
kitaqfc/lib/vrc7_sound.c:54
void nes_vrc7_silence_all(void)
{
u8 i;
i = 0;
while (i < 6) {
nes_vrc7_key_off(i);
nes_vrc7_write((u8)(0x30 + i), 0x0F);
i = (u8)(i + 1);
}
}nes_vrc7_write
VRC7のレジスター選択ポートへ番号を書き、続いてデータポートへ値を書きます。
書式
void nes_vrc7_write(u8 reg, u8 value);引数
regVRC7レジスター番号。チャンネル0では0x10=周波数下位、0x20=上位・ブロック・キー、0x30=楽器・減衰です。
value選択したVRC7レジスターへ書く1バイト。
戻り値
戻り値はありません。音源レジスターまたはドライバーの状態を変更します。
使用条件と制限
--mapper=vrc7(mapper 85)用です。レジスター選択とデータ書き込みの組を割り込みと競合させないでください。書き込み待ち時間はヘルパーに含まれていません。録音はKUROSAKIのFMモデルの出力で、実機の音色・待ち時間の検証ではありません。
楽器0は全チャンネルで共有します。channel_setはキー・周波数上位を楽器・減衰より先に書きます。key_off後の次の音は周波数上位とブロックも設定し直します。
使用例
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
// Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
*((u8*)0xA000)=0; *((u8*)0xA010)=1;
*((u8*)0xB000)=2; *((u8*)0xB010)=3;
*((u8*)0xC000)=4; *((u8*)0xC010)=5;
*((u8*)0xD000)=6; *((u8*)0xD010)=7;
sound_begin("VRC7 PRESET AND PATCH");
nes_vrc7_silence_all();
nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
nes_vrc7_set_user_patch(patch);
nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
sound_wait(60);
// Raw register 30 selects instrument zero and maximum attenuation.
nes_vrc7_write(0x30,0x0F);
nes_vrc7_key_off(0);
nes_vrc7_silence_all(); sound_wait(30);
sound_end();
}期待する結果
プリセット楽器1と、サステインを有効にした独自の楽器0を各60フレーム鳴らします。音程を揃えて音色を比較し、最後にキーオフと最大減衰で止めます。

ビルドコマンド
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm実装とソース
kitaqfc/lib/vrc7_sound.h:12
Write register selection followed immediately by data. No explicit device settling delay or cartridge detection is inserted by this wrapper. The caller must arrange device timing and serialize register/data pairs against interrupt writers.
kitaqfc/lib/vrc7_sound.c:8
void nes_vrc7_write(u8 reg, u8 value)
{
VRC7_ADDR = reg;
VRC7_DATA = value;
}構造体・定数・ヘッダー全文
関数形式でないマクロ、構造体、配列上限、enum、別名はここで確認できます。元のコメントを含む採取時点のヘッダー全文です。
actor.h — ゲームアクターの配列
/*
* Optional declarations for the phase 11 actor helper.
*/
// Callers initialize size and metadata and pass non-null actors.
// attr/flags are retained fields; this helper does not interpret or apply them.
struct NesActor {
unsigned short world_x;
unsigned short world_y;
unsigned char width;
unsigned char height;
unsigned char screen_x;
unsigned char screen_y;
unsigned char attr;
unsigned char flags;
};
// Set the actor's 16-bit world position without updating cached screen coordinates.
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y);
// Subtract the camera and retain only the low coordinate bytes. This performs
// wrapping projection, not clipping; callers must hide actors outside the viewport.
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y);
// Draw the byte-stream metasprite at cached screen coordinates and return its
// next OAM index. Actor attr/flags are not applied by this wrapper.
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite);
// Test overlap in world coordinates with exclusive right/bottom edges. Keep
// position-plus-size sums within u16; touching edges are not collisions.
// Require positive widths and heights; zero extents are not rejected before the edge tests.
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b);kitaqfc/lib/actor.h
asset.h — 素材IDとデータ記述表
#ifndef ASSET_H
#define ASSET_H
#include "core.h"
#include "bank.h"
#define ASSET_TYPE_RAW ((u8)0)
#define ASSET_TYPE_TILES ((u8)1)
#define ASSET_TYPE_MAP ((u8)2)
#define ASSET_TYPE_SONG ((u8)3)
// The descriptor table bank and each payload bank are independent.
// ptr is a CPU-visible address and len counts bytes; type is a tag, not a decoder selection.
typedef __packed struct AssetDesc {
u8 type;
u8 bank;
const u8* ptr;
u16 len;
} AssetDesc;
// Retain a bank-qualified descriptor table; the caller keeps its storage valid.
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);
// Copy a descriptor from its ROM bank into caller storage. Null output or an
// out-of-range ID returns zero without reading the table.
u8 asset_get(u8 asset_id, AssetDesc* out_desc);
// Copy the smaller of the asset length and max_len using a bank-aware transfer.
// Return one for a valid ID even when the payload is truncated.
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);
// Switch to the asset bank, upload tile/raw data in chunks of at most 128 bytes,
// then restore the bank recorded by this library. The caller supplies safe PPU timing.
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);
// Return the asset bank or zero on lookup failure; bank zero may also be valid.
u8 asset_get_bank(u8 asset_id);
// Return the stored address without switching banks. Pair it with the descriptor
// bank before reading banked ROM; a failed lookup returns null.
const u8* asset_get_ptr(u8 asset_id);
// Return the descriptor byte length, or zero for an invalid ID.
u16 asset_get_len(u8 asset_id);
#endifkitaqfc/lib/asset.h
attribute.h — ネームテーブル属性
/*
* KITAQFC phase15 attribute patch helpers.
*/
// Copy 64 already-packed attribute bytes into shadow storage. This does not
// convert one palette ID per tile into NES attribute bitfields.
// The source must expose all 64 packed bytes and stay readable for the duration of this copy.
void nes_attr_shadow_build_from_palette_map(unsigned char* src64);
// Apply a palette to every two-by-two tile quadrant touched by the rectangle.
// Empty or out-of-range rectangles leave the shadow unchanged.
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index);
// Queue the attribute bytes covering a tile rectangle within 32x30 tiles.
// Empty rectangles succeed; invalid bounds fail without queuing.
// Capacity failure may leave earlier rows queued.
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height);kitaqfc/lib/attribute.h
audio.h — 音楽と効果音・音源制御
#ifndef AUDIO_H
#define AUDIO_H
#include "core.h"
// Channel bits form a complete replacement mask for nes_apu_channel_enable.
#define NES_APU_ENABLE_PULSE1 0x01
#define NES_APU_ENABLE_PULSE2 0x02
#define NES_APU_ENABLE_TRIANGLE 0x04
#define NES_APU_ENABLE_NOISE 0x08
#define NES_APU_ENABLE_DMC 0x10
// Disable frame IRQs, enable the four tonal/noise channels and install quiet
// control defaults. DMC remains disabled.
void nes_apu_init(void);
// Replace all five channel-enable bits in APU_STATUS; this is not an additive enable.
void nes_apu_channel_enable(u8 mask);
// Disable every channel and clear volume/DMC output settings used by this helper.
void nes_apu_silence_all(void);
// Enable only pulse 1, disable its sweep and load control, 11-bit timer and
// length-table index. Starting this effect disables the other APU channels.
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index);
// Enable only pulse 2 and load its control/timer/length fields, disabling other channels.
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index);
// Enable only triangle and load linear-counter, timer and length fields.
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index);
// Enable only noise and load envelope/control, period/mode and length fields.
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index);
// Trigger the predefined pulse-1 tick effect; it inherits the exclusive-channel behavior.
void nes_sfx_tick_blip(void);
// Trigger the predefined pulse-1 movement effect.
void nes_sfx_move_blip(void);
// Convert a CPU sample address and byte length to DMC register units. Supply
// an address in 0xC000-0xFFC0 aligned to 64 bytes and a representable length
// 16*n+1 in 1..4081; inputs are narrowed without validation or bank management.
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);
// Enable DMC together with all four other channels by replacing the status mask.
void nes_dmc_start(void);
// Disable DMC while leaving all four other channel-enable bits set.
void nes_dmc_stop(void);
// Install sample parameters, then start DMC using the full-channel enable mask.
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);
#endifkitaqfc/lib/audio.h
audio_vblank.h — VBlank IRQによる音楽再生
#ifndef NES_AUDIO_VBLANK_H
#define NES_AUDIO_VBLANK_H
#include "core.h"
// Original MIT-licensed NMI sequencer. Channel order follows the physical APU:
// CH1 pulse 1, CH2 pulse 2, CH3 triangle, CH4 noise (indices 0, 1, 2, 3).
// Every record is five bytes: delay, CH1, CH2, CH3, CH4.
// Delay 1..255 holds the new state for that many NMI ticks. Delay 0 ends playback.
// Tonal notes 0..71 are C2..B7, tuned for NTSC. Noise 0..15 selects a long-mode
// period; 16..31 selects short mode. HOLD preserves a channel; STOP silences it.
#define NES_AUDIO_NOISE_ENVELOPE 128
#define NES_AUDIO_HOLD 255
#define NES_AUDIO_STOP 254
#define NES_AUDIO_RECORD_BYTES 5
#define NES_AUDIO_QUEUE_CAPACITY 7
// Reset the queue, feeder and diagnostic counters; silence and initialize the
// APU. Owns the four basic channels and disables DMC and APU frame IRQs.
void nes_audio_vblank_init(void);
// Copy one complete validated record to RAM and publish it atomically last.
// Return 1 on success, 0 for null/invalid/full. Call from one foreground producer.
u8 nes_audio_vblank_enqueue(const u8* record);
// Return queued records (0..7), excluding the record whose delay is in progress.
u8 nes_audio_vblank_queued(void);
// Return available record slots (0..7), not bytes.
u8 nes_audio_vblank_free(void);
// Enable consumption at the next NMI. Does not enable PPU NMI itself.
void nes_audio_vblank_start(void);
// Disable consumption, silence, discard queued records and clear the feeder.
void nes_audio_vblank_stop(void);
// Stop and attach a readable five-byte-record stream; return 0 for null, zero
// count or a count above 13107. Nonzero loop repeats the stream. Does not start.
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop);
// Copy at most seven records from the attached stream. Return copies accepted.
// A one-shot feeder appends a delay-zero terminator after the last timed record.
// Keep the stream's ROM bank mapped during this foreground call only.
u8 nes_audio_vblank_refill(void);
// Set the stream, prefill the queue and start. Return 0 for invalid setup/data.
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop);
// Return 1 while the BGM consumer is enabled, including a recoverable queue underrun.
u8 nes_audio_vblank_is_playing(void);
// Saturating count of starvation episodes, cleared by init; END is not starvation.
u8 nes_audio_vblank_underruns(void);
// Configure the next note: pulse bits 7..6 select duty and 3..0 select volume;
// noise uses volume 3..0, or NES_AUDIO_NOISE_ENVELOPE | decay_period (0..15)
// for a one-shot hardware envelope (larger periods decay more slowly). The
// next noise note starts at volume 15; its length permits a full decay.
// Triangle uses zero for silent, nonzero for full level.
// Return 0 for channel >=4. Hardware has no triangle volume control.
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control);
// Any nonzero argument freezes both timelines and mutes immediately. Zero
// resumes from the stored delays and restores held notes on the next NMI.
// BGM is_playing remains enabled during pause. Refill may still fill the queue.
void nes_audio_vblank_pause(u8 on);
// Copy 1..7 timed records. Bits 0..3 select channels temporarily owned by SFX.
// BGM continues underneath and its current notes return when the effect ends.
// Return 1 after copying; 0 for null, invalid count/data or an empty low mask.
// Ignore mask bits 4..7. All fields are validated, even unselected channels.
// Delay zero is not accepted for SFX. Invalid input preserves the current SFX.
// Replacement restarts the effect; the first HOLD starts from a stopped note.
// The source need only stay mapped/readable until this foreground call returns.
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask);
// Release effect ownership at the next NMI, restoring current BGM notes.
// This does not stop or restart the BGM and does not write the APU immediately.
void nes_audio_vblank_stop_sfx(void);
// NMI hook: invoke exactly once per NMI. With a custom handler or a compiler
// without automatic audio-hook linking, call it there. Never call from main
// or call it again if the compiler already inserts the hook.
// Saves A/X/Y, uses no compiler scratch bytes and reads only RAM/fixed ROM.
void __nes_audio_vblank_tick(void);
#endifkitaqfc/lib/audio_vblank.h
bank.h — バンク番号付きのコード・データアクセス
#ifndef BANK_H
#define BANK_H
#include "core.h"
#include "intrinsics.h"
// Pair a bank selector with its CPU-window address; a near pointer alone cannot select the bank.
typedef __packed struct BankPtr {
u8 bank;
const u8* ptr;
} BankPtr;
extern u8 kq_bank_current;
// Record the bank in the library shadow and invoke the mapper-aware compiler intrinsic.
void bank_switch(u8 bank);
// Read this wrapper's bank shadow; arbitrary mapper writes do not update it.
u8 bank_get_current(void);
// Read one byte from an explicit ROM bank and 16-bit CPU address.
u8 far_data_read8(u8 bank, const void* addr);
// Read a 16-bit value through the bank-aware compiler intrinsic.
u16 far_data_read16(u8 bank, const void* addr);
// Copy len bytes from banked ROM to caller-owned writable storage.
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);
// Store a bank/address pair without reading memory; null output is ignored.
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);
// Read one byte using both the bank and address of the supplied far pointer.
u8 farptr_read8(BankPtr ptr);
// Read a 16-bit value using both components of the far pointer.
u16 farptr_read16(BankPtr ptr);
// Copy len bytes from a far pointer; the caller provides a sufficiently large destination.
void farptr_read(BankPtr ptr, void* dst, u16 len);
// Route a declared zero-argument function through the far-call intrinsic; this is not a general
// callback-pointer dispatcher. Mapper support and fixed-bank call-site rules still apply.
#define far_call(bank, func) __farcall((bank), func)
#endifkitaqfc/lib/bank.h
chain.h — 関節追従と座標履歴
#ifndef CHAIN_H
#define CHAIN_H
#include "core.h"
typedef __packed struct ChainPoint {
s16 x;
s16 y;
} ChainPoint;
// head identifies the newest point; count grows to capacity, then old points are overwritten.
// Capacity at most 128 keeps every head + age sum within a byte for this implementation.
typedef __packed struct Chain {
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.
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);
#endifkitaqfc/lib/chain.h
collision.h — 矩形等の接触判定
/*
* Optional declarations for the phase 11 collision helper.
*/
struct NesBox16 {
unsigned short x;
unsigned short y;
unsigned char w;
unsigned char h;
};
// Test two axis-aligned world boxes using exclusive right/bottom edges.
// Coordinate-plus-size sums must fit u16; touching edges return false.
// Both boxes must have positive dimensions; the overlap test has no separate empty-box guard.
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b);
// Test a half-open box: left/top edges are included and right/bottom edges
// excluded. Callers provide valid pointers and non-overflowing edge coordinates.
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py);kitaqfc/lib/collision.h
core.h — 基本の整数型
整数型の共通名を定義します。u8・s8は8ビット、u16・s16は16ビットで、先頭のuは符号なし、sは符号付きを表します。座標、個数、ビットマスクなど、値の範囲に合う型を選びます。
TRUEは1、FALSEは0です。ただし、状態レジスタの読み取り関数には0またはビットマスクを返すものもあります。真偽だけを調べる場合は、戻り値が0かどうかを判定してください。このヘッダーを使うためのCファイルの追加は不要です。
#ifndef CORE_H
#define CORE_H
// Public scalar aliases for the compiler's byte and 16-bit integer types.
typedef unsigned char u8;
typedef unsigned short u16;
typedef signed char s8;
typedef signed short s16;
// Boolean convenience values; API functions returning bit masks need not normalize to TRUE.
#define TRUE 1
#define FALSE 0
#endifkitaqfc/lib/core.h
danmaku.h — 弾プール・扇状生成・被弾とグレイズ
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
#ifndef KITAQFC_DANMAKU_H
#define KITAQFC_DANMAKU_H
#include "core.h"
#include "intrinsics.h"
#define DANMAKU_MAX 64
// Reserve cartridge PRG RAM 0x6000..0x627F (640 bytes), enabled and writable.
// Bullet centers remain in [8,248) x [24,232). These margins prevent byte
// wraparound even at the largest signed Q4.4 velocity accepted by spawn.
extern u8 dm_x[64];
extern u8 dm_y[64];
extern u8 dm_active[64];
extern u8 dm_grazed[64];
extern u8 dm_count;
extern u8 dm_peak;
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 the pool, counters and rotating OAM priority. Player settings survive.
void danmaku_reset(void);
// Remove all bullets and step events; preserve lifetime/peak counters.
void danmaku_clear(void);
// Allocate a bullet at pixel-center x/y with signed 1/16-pixel velocities.
// Return one on success, zero outside the playfield or when all 64 slots are full.
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);
// Emit up to 64 bullets. Angles wrap in 32 steps: 0 right, 8 down, 16 left,
// 24 up. Speed is in 1/16 pixel, clamped to 64. Each failed allocation counts.
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);
// Move all live bullets once. Remove offscreen/hitting bullets. Hit uses a
// +/-3-pixel box; graze uses +/-11, once per bullet. Events describe this step.
// This updates logic only; it neither writes OAM nor calls a game damage handler.
void danmaku_step(void);
// Write up to slots consecutive shadow-OAM entries starting at first_oam.
// Rotate bullet priority by 13 modulo 64 and hide unused entries in that range.
// tile is an 8x8 sprite centered at (4,4). Return the number emitted. DMA is
// caller-owned and belongs in VBlank. Hardware's eight-per-scanline limit remains.
u8 danmaku_draw(u8 first_oam, u8 slots, u8 tile, u8 attributes);
#endifkitaqfc/lib/danmaku.h
debug.h — RAMに値とアサートを記録
#ifndef DEBUG_H
#define DEBUG_H
#include "core.h"
// Choose 1..255 consistently across units; the valid-entry count is a byte.
#ifndef DEBUG_TRACE_MAX
#define DEBUG_TRACE_MAX 32
#endif
// Names are retained pointers, not copied strings; keep their storage and ROM mapping readable.
typedef __packed struct DebugTraceEntry {
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;
// Build the RAM-resident ASSERT label and reset log length, frame tag and last
// assertion code. Old log storage bytes are not erased.
void debug_init(void);
// Choose the frame tag stored in subsequent debug entries.
void debug_set_frame(u16 frame);
// Append a named marker with the reserved marker value 0xFFFF.
void debug_mark_frame(const u8* label);
// Widen a byte value into the trace entry'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);
// Store the assertion code even if the log is full, then attempt an ASSERT
// entry. The helper records failure but does not stop execution.
void debug_assert_fail(u16 code);
// Read the number of valid entries in the fixed trace buffer.
u8 debug_get_trace_count(void);
// Borrow the internal trace array; bound inspection by debug_get_trace_count.
const DebugTraceEntry* debug_get_trace_log(void);
// Read the latest assertion code, initially zero after debug_init.
u16 debug_get_last_assert(void);
#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)
#endifkitaqfc/lib/debug.h
entity.h — 固定配列のオブジェクトプール
#ifndef ENTITY_H
#define ENTITY_H
#include "core.h"
// Choose capacity 1..255 consistently across units; counters are bytes and FF is the failure sentinel.
#ifndef ENTITY_MAX
#define ENTITY_MAX ((u8)16)
#endif
// Coordinates, velocities, state and timer have game-defined units.
// The sprite byte is bookkeeping only; releasing a slot does not hide or free its sprite.
typedef __packed struct Entity {
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.
// Use a callback in common PRG bank 0, or keep its bank mapped throughout dispatch.
typedef void (*EntityFn)(u8 id);
// Reset every slot before the game starts using the pool.
void entity_init(void);
// 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(id) for each active slot in ascending order. A null callback does nothing.
// Changes to later slots take effect during the same traversal. Do not reenter this traversal.
void entity_update_all(EntityFn fn);
// Call the drawing callback for each active slot, using the same traversal rules as update.
// The callback submits drawing data; the pool does not allocate sprites.
void entity_draw_all(EntityFn fn);
// Count occupied slots without changing entity state.
u8 entity_count_active(void);
// Release every entity and restore deterministic defaults, including the no-sprite sentinel.
void entity_clear_all(void);
#endifkitaqfc/lib/entity.h
fc.h — ライブラリをまとめて読むヘッダー
FC開発で使う型、入出力、描画、音源、物理計算などの宣言を一度に取り込むまとめ用ヘッダーです。#include "fc.h"だけで各ライブラリの実装まで組み込まれるわけではありません。使う関数に対応するCファイルは、個別説明のビルド例に従って追加します。
末尾で取り込むnes_game.hには関数形式のマクロがあります。たとえば引数なしのnes_oam_dma()マクロと、転送元ページを受け取るruntimeの同名関数は使い方が異なります。名前が重なる補助APIを使う場合は、必要なヘッダーを個別に取り込んでください。
関数の使い方とサンプル: nes_oam_dma
#ifndef FC_H
#define FC_H
// Convenience header collecting FC interfaces. Inclusion provides declarations
// and aliases, not every implementation; link only the source modules required
// by the selected runtime, avoiding alternative helpers with conflicting symbols.
#include "core.h"
#include "intrinsics.h"
#include "system.h"
#include "input.h"
#include "runtime.h"
#include "ppu.h"
#include "ppu_direct.h"
#include "vram_queue.h"
#include "vram.h"
#include "oam.h"
#include "sprite.h"
#include "pad.h"
#include "input_repeat.h"
#include "palette.h"
#include "scroll.h"
#include "tilemap.h"
#include "nametable_asset.h"
#include "attribute.h"
#include "scene.h"
#include "metasprite.h"
#include "actor.h"
#include "entity.h"
#include "collision.h"
#include "fixed.h"
#include "physics2d.h"
#include "physics3d.h"
#include "bank.h"
#include "asset.h"
#include "debug.h"
#include "chain.h"
#include "audio.h"
#include "audio_vblank.h"
#include "fds_sound.h"
#include "vrc6_sound.h"
#include "vrc7_sound.h"
#include "mapper.h"
// This final include adds function-like macros, including zero-argument nes_oam_dma.
// Use selective headers when calling the page-argument runtime API with the same name.
#include "nes_game.h"
#endifkitaqfc/lib/fc.h
fds.h — FDSディスク操作
FDSの有無を表すビルド設定と、ディスク装置の状態レジスタを調べる組み込み関数をまとめています。__fds_available()はコンパイル時のマッパー選択を返し、実機を検出する関数ではありません。
ディスク待機は挿入状態の監視です。読み書きの完了を待つものではなく、タイムアウトもありません。__fds_side()は状態ビット、__fds_error()はレジスタの生値なので、名前からディスク面番号やBIOSエラー番号と解釈しないでください。
関数の使い方とサンプル: __fds_available, __fds_wait_ready, __fds_side, __fds_error
#ifndef FDS_H
#define FDS_H
#include "intrinsics.h"
// Return the compile-time FDS mapper selection as 0 or 1; this is not a hardware probe.
u8 __fds_available(void);
// Return the inverted disk-absent bit from 4032 as 0 or 1; this checks presence, not transfer completion.
u8 __fds_disk_ready(void);
// Return raw status mask 4032 & 04, either 0 or 4. This is not a numbered disk-side identifier.
u8 __fds_side(void);
// Read the entire 4030 status register; this is not a stored BIOS error code.
u8 __fds_error(void);
// Poll disk presence until the absent bit clears. There is no timeout or cancellation path.
void __fds_wait_ready(void);
// Alias the same disk-presence wait as __fds_wait_ready; the call can block indefinitely.
void __fds_wait_insert(void);
#endifkitaqfc/lib/fds.h
fds_file.h — FDSファイルのロード
FDSファイルの読み込み・上書き保存と、ビルド時のファイル表の照会をまとめています。existsとsizeはコンパイル済みメタデータを参照し、挿入されたディスクのディレクトリを読み直しません。
読み込みはまずファイルに指定されたアドレスへ転送し、必要ならCPU側のデータをdstへ移します。保存はその面の最後にある非ブートPRGファイルを同じサイズで上書きします。ファイルIDと物理的な記録順は別です。呼び出し元の配置、RAM範囲、割り込み・表示状態の条件を各APIで確認してください。
関数の使い方とサンプル: __fds_load_file, __fds_save_file, __fds_file_exists, __fds_file_size
#ifndef FDS_FILE_H
#define FDS_FILE_H
#include "intrinsics.h"
// Native FDS32: load a non-boot file at its disk-header address, then memmove
// PRG bytes to dst. Null dst keeps the native destination (also for PPU files).
// Both CPU spans must fit $0200-$07FF or $6000-$DFFF and be owned by the caller.
// Return 0, a BIOS error, $40 for a missing transfer, or $FF for invalid input.
// Call from common code with disk-I/O interrupt/display prerequisites satisfied.
u8 __fds_load_file(u8 id, u8* dst);
// Overwrite the final non-boot PRG slot on its side, preserving its ID/name/load
// address. len must equal its packaged size; src must be a valid CPU RAM span.
// The resolved physical ordinal selects the slot, independently of file ID.
// Return 0, a BIOS error, or $FF before any write for an invalid slot/span/size.
u8 __fds_save_file(u8 id, const u8* src, u16 len);
// Query the compiled --fds-meta/--fds-manifest table, not the inserted disk; return 0 or 1.
u8 __fds_file_exists(u8 id);
// Return the compiled metadata size, or zero if id is absent. No disk directory is read.
u16 __fds_file_size(u8 id);
#endifkitaqfc/lib/fds_file.h
fds_overlay.h — FDSのオーバーレイコード
FDSのPRG RAMへコードやデータを読み込み、同じメモリ領域を使い回すための宣言です。ファイルIDを指定するload_overlayと、論理バンク番号を指定するload_bankを区別してください。
現在バンクの照会はソフトウェアの管理値です。RAM内容の検査ではなく、転送失敗などで不明になると255を返します。farcallは引数なしの関数名を指定して呼び出し元のバンクを復元します。復元に失敗した場合は安全に戻れないため、共通領域で停止します。
関数の使い方とサンプル: __fds_load_overlay, __fds_load_bank, __fds_current_bank, __fds_overlay_farcall
#ifndef FDS_OVERLAY_H
#define FDS_OVERLAY_H
#include "intrinsics.h"
// Load disk file id through BIOS, return its error byte and mark bank residency FF (unknown).
u8 __fds_load_overlay(u8 id);
// Load logical bank 1 from its separate boot file, or bank 2+ from overlay-start-id+(bank-2).
// Return zero on success, FF for invalid/unavailable mappings, or the BIOS error.
// A failed BIOS transfer marks residency FF (unknown); a matching valid guard can skip loading.
u8 __fds_load_bank(u8 bank);
// Return zero when the software residency record matches, otherwise call __fds_load_bank.
u8 __fds_require_bank(u8 bank);
// Return the software resident-bank byte, or FF after a failed bank transfer.
// This is not a checksum of overlay memory.
u8 __fds_current_bank(void);
// Compare a valid bank with the software resident-bank byte; FF (unknown) never matches.
u8 __fds_is_bank_resident(u8 bank);
// Return the linker-generated overlay-function count; zero if the table is disabled.
u8 __fds_overlay_function_count(void);
// Evaluate bank once and call the named zero-argument function using its linked bank.
// Restore the caller window before returning; restoration failure stops in common code.
u8 __fds_overlay_farcall(u8 bank, u16 func);
// Same named, zero-argument dispatch as __fds_overlay_farcall; evaluate bank once.
u8 __fds_farcall(u8 bank, u16 func);
#endifkitaqfc/lib/fds_overlay.h
fds_save.h — FDS保存の宣言
FDSの保存処理を使うソースから、fds_file.hの宣言を取り込むための入口です。独自の保存形式、作業領域、実装関数を追加するヘッダーではありません。保存領域の確保とファイルサイズはゲーム側で決め、__fds_save_file()の条件に合わせます。
関数の使い方とサンプル: __fds_save_file
#ifndef FDS_SAVE_H
#define FDS_SAVE_H
// Compatibility include for FDS file/save declarations; this header adds no storage or implementation.
#include "fds_file.h"
#endifkitaqfc/lib/fds_save.h
fds_sound.h — FDS波形音源
#ifndef FDS_SOUND_H
#define FDS_SOUND_H
#include "intrinsics.h"
// Write 83 to 4023 and zero to 4089/408A to initialize disk/sound register gates.
void __fds_sound_enable(void);
// Copy exactly 64 readable bytes to wave RAM with wave writes enabled, then set 4089 to zero.
void __fds_wave_load(const u8* wave64);
// Stream 32 readable bytes to 4088; the caller prepares modulation state and write conditions.
void __fds_mod_load(const u8* mod32);
// Write a 12-bit frequency to 4082/4083; upper control bits in 4083 are cleared.
void __fds_freq_set(u16 freq);
// Write (volume & 3F) | 80 to 4080 for direct volume control; masking wraps, rather than clamps.
void __fds_volume_set(u8 vol);
// Write the three raw arguments to 4080, 4084 and 408A in that order.
// The legacy parameter names do not describe three independently packed envelope fields.
void __fds_env_set(u8 speed, u8 gain, u8 mode);
#endifkitaqfc/lib/fds_sound.h
fixed.h — Q8.8固定小数点と矩形
#ifndef FIXED_H
#define FIXED_H
#include "core.h"
// Signed Q8.8 spans -128 to 127 + 255/256; one integer unit is represented by 256.
typedef s16 fix8;
#define FIX_ONE ((fix8)256)
#define FIX_HALF ((fix8)128)
typedef __packed struct Vec2 {
fix8 x;
fix8 y;
} Vec2;
typedef __packed struct KQRect {
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);
#endifkitaqfc/lib/fixed.h
input.h — ボタンの現在値・押下・解放・リピート
#ifndef INPUT_H
#define INPUT_H
#include "core.h"
#include "intrinsics.h"
// These are shared logical masks, not raw controller-register bit positions.
// input_update translates controller 1 before these masks are applied.
#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 new press pulses immediately; the next pulse occurs DELAY + 1 input updates later.
#ifndef INPUT_REPEAT_DELAY
#define INPUT_REPEAT_DELAY ((u8)18)
#endif
// Later pulses occur RATE + 1 updates apart; zero repeats on each 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;
// Clear held/edge/repeat masks and reset each independent repeat countdown.
void input_init(void);
// Read controller 1 through the safe intrinsic, translate its bit order and
// derive press/release edges. Repeat delays count calls to this function, so
// call it once per intended input tick.
void input_update(void);
// Return true when any masked logical button is held.
u8 input_down(u8 mask);
// Return true when any masked button became pressed in the latest input update.
u8 input_pressed(u8 mask);
// Return true when any masked button became released in the latest update.
u8 input_released(u8 mask);
// Return true for a masked initial press or scheduled repeat pulse.
u8 input_repeat(u8 mask);
// Read the complete translated held-button mask.
u8 input_current(void);
// Read the translated held-button mask from before the latest update.
u8 input_previous(void);
#define btn_down input_down
#define btn_pressed input_pressed
#define btn_released input_released
#define btn_repeat input_repeat
#endifkitaqfc/lib/input.h
input_repeat.h — 押し続け入力のリピート
/*
* Optional declarations for the phase 9 NES pad repeat helper.
*
* Button bit layout:
* bit0=A bit1=B bit2=Select bit3=Start bit4=Up bit5=Down bit6=Left bit7=Right
*/
extern unsigned char nes_pad_repeat_delay;
extern unsigned char nes_pad_repeat_interval;
extern unsigned char nes_pad1_repeat;
// Set initial/repeat countdowns and clear repeat output. Zero countdowns are
// allowed and cause a repeat on the next held-button step.
// A fresh press emits immediately. Held repeats follow after delay+1 and then interval+1 steps.
// These are call counts, not milliseconds; use one fresh pad poll before each step.
void nes_pad_repeat_config(unsigned char delay, unsigned char interval);
// Generate per-button repeat bits from the most recently polled pad state.
// Poll first, then call once per tick; a countdown reaching zero emits on the
// following step, so an interval N leaves N decrement steps between pulses.
void nes_pad_repeat_step(void);
// Return the requested newly-pressed bits, not a normalized Boolean.
unsigned char nes_pad_trigger(unsigned char mask);
// Return the requested repeat-pulse bits from the latest repeat step.
unsigned char nes_pad_repeat(unsigned char mask);
// Return the requested newly-released bits from the latest pad poll.
unsigned char nes_pad_release(unsigned char mask);
// Return the requested currently-held bits in the NES pad layout.
unsigned char nes_pad_held(unsigned char mask);kitaqfc/lib/input_repeat.h
intrinsics.h — コンパイラ組み込み命令
__で始まるFC用組み込み関数の宣言を集めたヘッダーです。コンパイラが6502命令または内部補助ルーチンを生成するため、宣言だけを見て「実装がない」と判断する必要はありません。使用例・引数・戻り値はコンパイラ説明書の個別項目で確認できます。
PPUへの直接書き込みは呼び出し側が安全な時刻を確保します。内部の引数領域や作業領域を共有する処理もあるため、通常処理とNMI・IRQから同時に呼んでも安全とは限りません。各関数の制約に従って、描画や割り込みの担当を決めてください。
関数の使い方とサンプル: __memcpy, __ppu_addr, __nmi_wait
#ifndef INTRINSICS_H
#define INTRINSICS_H
/*
* FC/NES native intrinsic API.
* This header intentionally does not expose KITAQGB/CGB compatibility stubs.
* The compiler recognizes these names and emits 6502-oriented code or
* calls an internal 6502 helper generated by the backend.
*/
// Direct PPU operations require caller-controlled access timing. Generated helpers share runtime
// argument/scratch storage: do not assume calls are reentrant across NMI or IRQ handlers.
typedef unsigned char u8;
typedef unsigned short u16;
/* memory / bit / math */
// Copy len bytes forward between readable/writable CPU ranges; overlapping copies are not memmove.
// Zero length performs no memory transfer; argument side effects are evaluated before expansion.
void __memcpy(u8* dst, const u8* src, u16 len);
// Fill len CPU bytes with value. Zero length still evaluates argument side effects.
void __memset(u8* dst, u8 value, u16 len);
// Forward copy with an 8-bit length, zero through 255; the dynamic helper promotes it to a word count.
void __memcpy_small(u8* dst, const u8* src, u8 len);
// Fill zero through 255 CPU bytes; this is ordinary RAM access, not a PPU transfer.
void __memset_small(u8* dst, u8 value, u8 len);
// Copy exactly 16 bytes forward; both ranges must be valid and suitably non-overlapping.
void __copy16(u8* dst, const u8* src);
// Copy exactly 32 bytes forward; bank selection and source lifetime belong to the caller.
void __copy32(u8* dst, const u8* src);
// Return 0 or 1 for half-open bounds. Zero extents are empty; coordinates do not wrap across 255.
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);
// Return abs(x1-x2)+abs(y1-y2) modulo 256; large distances are not saturated.
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);
// Return y*width+x as a word without checking coordinates against map dimensions.
u16 __map_index(u8 x, u8 y, u8 width);
// Replace both persistent state bytes. Reusing a seed reproduces the sequence; this is not cryptographic randomness.
void __rng_seed(u16 seed);
// Advance the shared 16-bit LFSR and return the XOR of its two bytes; zero state is reseeded to A55A.
u8 __rng8(void);
// Return base[bit>>3] & (1<<(bit&7)): zero or the bit mask, not a normalized Boolean.
u8 __bit_test(u8* base, u16 bit);
// Set the indexed bit in caller storage without changing the other seven bits in that byte.
void __bit_set(u8* base, u16 bit);
// Clear the indexed bit in caller storage; no bounds check or interrupt exclusion is inserted.
void __bit_clear(u8* base, u16 bit);
// XOR the indexed bit in caller storage; coordinate concurrent writers to avoid lost updates.
void __bit_toggle(u8* base, u16 bit);
// Return the high byte of the unsigned 8-by-8 product, equivalent to floor(a*b/256).
u8 __mul8x8_hi(u8 a, u8 b);
// Return the low word of the unsigned product; overflow wraps without saturation.
u16 __mul16x8(u16 a, u8 b);
// Interpret a and b as signed two's-complement bit patterns and return the low word of their product.
u16 __smul16x8(u16 a, u8 b);
// Return acc+a*b modulo 65536 using unsigned multiplication.
u16 __mac16(u16 acc, u16 a, u8 b);
// Add the signed-pattern product to acc modulo 65536; the API carries the result as u16 bits.
u16 __smac16(u16 acc, u16 a, u8 b);
// FC alias of __smul16x8: signed-pattern raw product, with no Q1.7 scaling shift.
u16 __smul16x8_q1_7(u16 a, u8 b);
// FC alias of __smac16: accumulate a raw signed-pattern product modulo 65536.
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);
// Return a0*b0+a1*b1 modulo 65536. The FC backend inserts no Q8.8 rescaling or rounding.
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three unsigned word-by-byte products modulo 65536 without a final scaling shift.
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// Sum two signed-pattern products modulo 65536; no Q8.8 scaling shift is generated.
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three signed-pattern products modulo 65536; choose operand units explicitly in the caller.
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// FC alias of __sdot2_q8_8: two raw signed-pattern products, summed without rescaling.
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);
// FC alias of __sdot3_q8_8: three raw signed-pattern products, summed without rescaling.
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
/* PRG bank */
// Resolve a symbol or function placement to its byte bank number at compilation time.
u8 __bankof(u16 symbol_or_function);
// Select the mapper-specific PRG bank; the common bank and legal bank range depend on the target board.
void __bankswitch(u8 bank);
// Use the same mapper PRG-switch helper as __bankswitch; retain executable code in a valid mapping.
void __prg_bank_set(u8 bank);
// Call a declared zero-argument function; placement selects its bank and its declared type determines the result.
u8 __farcall(u8 bank, u16 func);
// Switch to the source bank, copy len bytes forward and restore the previous bank.
// Keep destination RAM and helper code accessible throughout; overlapping copies are not memmove.
void __far_memcpy(u8* dst, u8 bank, u16 src, u16 len);
// Temporarily select bank, read one byte at addr and restore the prior bank mapping.
u8 __farpeek8(u8 bank, u16 addr);
// Temporarily select bank and read a little-endian word, then restore the previous mapping.
u16 __farpeek16(u8 bank, u16 addr);
/* PPU / VRAM */
// Invoke the compiler rendering-enable helper; this header contains declarations, not C fallback implementations.
void __ppu_on(void);
// Invoke the compiler rendering-disable helper before bulk direct PPU updates.
void __ppu_off(void);
// Replace PPUMASK with the supplied byte; the caller chooses rendering, clipping and emphasis bits.
void __ppu_mask_set(u8 value);
// Replace PPUCTRL, including NMI enable, pattern-table choices and address increment mode.
void __ppu_ctrl_set(u8 value);
// Read the software PPUCTRL/PPUMASK shadows; use the setters for coherent state.
// Raw writes to $2000/$2001 bypass these shadows. No PPU hardware read occurs.
u8 __ppu_ctrl_get(void);
u8 __ppu_mask_get(void);
// Set the PPU address through the compiler helper; address writes affect the shared scroll/address latch.
void __ppu_addr(u16 ppu_addr);
// Write one PPUDATA byte using the current PPU address and increment mode.
void __ppu_data(u8 value);
// Read PPUSTATUS; the hardware clears VBlank status and resets the shared write latch.
u8 __ppu_read_status(void);
// Read PPUSTATUS to reset the scroll/address write latch; the read also clears VBlank status.
void __scroll_latch_reset(void);
// Save X/Y in runtime shadows, reset the PPU latch and write both scroll bytes immediately.
void __scroll_set(u8 x, u8 y);
// Replace saved X and write an X/Y pair using the retained Y shadow.
void __scroll_x_set(u8 x);
// Replace saved Y and write an X/Y pair using the retained X shadow.
void __scroll_y_set(u8 y);
// Write a byte-counted source range directly; arrange PPU access timing and source-bank visibility.
void __vram_write(u16 ppu_addr, const u8* src, u8 len);
// Write repeated bytes directly; this is not an enqueue operation.
void __vram_fill(u16 ppu_addr, u8 value, u8 len);
// Write one tile directly at 2000+y*32+x; use x<32, y<30 for visible nametable cells.
void __nametable_put(u8 x, u8 y, u8 tile);
// Write one tile in nametable nt&3. Mirroring can alias logical nametables; coordinates are unchecked.
void __nametable_put_nt(u8 nt, u8 x, u8 y, u8 tile);
// Fill rows of nametable zero directly, advancing each row start by 32 bytes.
// Keep the rectangle in bounds and use PPU increment one; this does not enqueue or clip.
void __nametable_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
// Fill a rectangle in nametable nt&3 with the same unchecked, direct-write row loop.
void __nametable_rect_nt(u8 nt, u8 x, u8 y, u8 w, u8 h, u8 tile);
// Write the entire packed attribute byte at 23C0+(y>>2)*8+(x>>2).
// Use tile coordinates below 32; this does not merge a single palette quadrant.
void __attr_set(u8 x, u8 y, u8 attr);
// Write a whole packed attribute byte in nametable nt&3; other quadrant fields are overwritten.
void __attr_set_nt(u8 nt, u8 x, u8 y, u8 attr);
// Stream exactly 16 source bytes to 3F00. Arrange PPU access timing and increment one.
void __palette_bg_load(const u8* pal16);
// Stream exactly 16 source bytes to 3F10; hardware palette aliases also affect background entries.
void __palette_sp_load(const u8* pal16);
/* NMI VRAM transfer queue. Queue entries are consumed by __vramq_exec(),
* and the default NMI handler calls __vramq_exec() automatically.
*/
// Reset pending intrinsic-queue records; this is separate from runtime.c nes_vram_queue_clear.
void __vramq_clear(void);
// Append one address/value update; use the overflow latch to detect capacity failure.
void __vramq_put(u16 ppu_addr, u8 value);
// Retain the source address for later consumption; keep its storage and bank mapping valid.
void __vramq_copy(u16 ppu_addr, const u8* src, u8 len);
// Append a repeated-byte update; the source value is stored in the record.
void __vramq_fill(u16 ppu_addr, u8 value, u8 len);
// Publish the pending records to the configured consumer. Do not mutate them while consumption can run.
void __vramq_commit(void);
// Execute a ready queue directly; committing and safe PPU timing are caller responsibilities.
void __vramq_exec(void);
// Return encoded bytes currently occupied, not transfer payload bytes or command count.
u8 __vramq_len(void);
// Read the latched capacity failure; reading does not reset it.
u8 __vramq_overflow(void);
// Clear the overflow latch explicitly after handling the failed submission.
void __vramq_clear_overflow(void);
// Return total encoded command-buffer capacity; this is independent of PPU VRAM capacity.
u8 __vramq_capacity(void);
/* NMI / IRQ */
// Spin until the runtime NMI counter changes; the configured handler must advance it. No timeout is emitted.
void __nmi_wait(void);
// Return the current wrapping NMI counter byte, not a Boolean ready flag.
u8 __nmi_ready(void);
// Set bit 7 in the PPUCTRL shadow and write it to hardware, preserving the other shadow bits.
void __nmi_enable(void);
// Clear bit 7 in the PPUCTRL shadow and write it to hardware; CPU IRQ masking is separate.
void __nmi_disable(void);
// Emit SEI to mask CPU IRQs; NMI is unaffected.
void __irq_disable(void);
// Emit CLI to unmask CPU IRQs; the caller must have a valid interrupt handler.
void __irq_enable(void);
// Return the processor-status byte captured before SEI. Pass that byte to __irq_restore.
u8 __irq_save(void);
// Restore processor status with PLP, including arithmetic flags and the saved IRQ mask.
void __irq_restore(u8 state);
/* OAM / input */
// Set OAMADDR to zero and start DMA from CPU page 02; arrange safe PPU timing in the caller.
void __oam_dma(void);
// Set OAMADDR to zero and DMA 256 bytes from page<<8. The source page must remain readable.
void __oam_dma_page(u8 page);
// Hide all 64 page-02 shadow sprites by writing Y=F0; hardware OAM changes only after DMA.
void __oam_clear(void);
// Write Y, tile, attributes and X to one shadow entry. Use indices 0..63; coordinates are raw OAM bytes.
void __sprite_set(u8 index, u8 x, u8 y, u8 tile, u8 attr);
// Replace only shadow X/Y for index 0..63; tile and attribute bytes remain unchanged.
void __sprite_move(u8 index, u8 x, u8 y);
// Replace the selected shadow tile byte; this does not upload tile patterns.
void __sprite_tile(u8 index, u8 tile);
// Replace the selected shadow attribute byte, including palette, priority and flip bits.
void __sprite_attr(u8 index, u8 attr);
// Write F0 to the selected shadow Y byte; issue DMA to make the change visible.
void __sprite_hide(u8 index);
// Expand dx,dy,tile,attr records into page-02 shadow OAM until dx=FF; return the next index.
// Coordinates and the byte OAM cursor wrap; keep the terminated stream within the available slots.
u8 __metasprite_draw(u8 oam_index, u8 base_x, u8 base_y, const u8* metasprite);
// Latch and sample eight D0 bits from port 1; bit order is A,B,Select,Start,Up,Down,Left,Right.
u8 __pad_read1(void);
// Latch and sample eight D0 bits from port 2 in the same NES button order.
u8 __pad_read2(void);
// Repeat pairs of port-1 polls until both bytes match; the retry loop has no fixed limit.
u8 __pad_read1_safe(void);
// Repeat pairs of port-2 polls until both bytes match; this does not populate pad.c globals.
u8 __pad_read2_safe(void);
// Return pad & 0F for A/B/Select/Start; this masks a supplied snapshot and performs no polling.
u8 __pad_buttons(u8 pad);
// Return pad & F0 in the original bit positions; this does not normalize directions to bits 0..3.
u8 __pad_dirs(u8 pad);
/* Famicom/NES expansion input devices.
* D1 readers are for expansion-port controllers/joysticks that expose a
* standard 8-bit joypad stream on data line D1 instead of D0.
* __joypad2p_voice() / __mic_read2p() return the Famicom 2P microphone bit.
*/
// Latch and read an eight-button stream from port-1 data line D1 rather than D0.
u8 __pad_read1_d1(void);
// Latch and read an eight-button stream from port-2 data line D1 rather than D0.
u8 __pad_read2_d1(void);
// Alias the port-1 D1 controller reader; the attached device must use the expected serial protocol.
u8 __exp_pad_read1(void);
// Alias the port-2 D1 controller reader; this is not an expansion-device discovery operation.
u8 __exp_pad_read2(void);
// Return controller-port 4016 bit 2 normalized to 0 or 1; this is a microphone level bit, not PCM audio.
u8 __joypad2p_voice(void);
// Alias the same normalized microphone input bit as __joypad2p_voice.
u8 __mic_read2p(void);
/* Zapper / light gun. The no-suffix names default to the usual port-2/Famicom
* expansion-port mapping. __zapper_light*() returns 1 when light is detected.
*/
// Return raw 4016 & 18, retaining trigger and active-low light bits.
u8 __zapper_raw1(void);
// Return raw 4017 & 18 for the second port.
u8 __zapper_raw2(void);
// Return port-1 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger1(void);
// Return port-2 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger2(void);
// Invert port-1 bit 3 and normalize: one means light detected.
u8 __zapper_light1(void);
// Invert port-2 bit 3 and normalize: one means light detected.
u8 __zapper_light2(void);
// Default to the port-2 normalized trigger reader.
u8 __zapper_trigger(void);
// Default to the port-2 normalized light reader.
u8 __zapper_light(void);
/* Family BASIC Keyboard. __fkb_scan() writes 18 bytes: 9 rows x 2 columns,
* each byte containing raw $4017 & 0x1E for that row/column.
*/
// Probe selected-row and disabled-matrix bit patterns and return 0 or 1; this is a heuristic detection.
u8 __fkb_detect(void);
// Write 18 raw masked samples (4017 & 1E), two per row, to caller storage; reset 4016 afterward.
void __fkb_scan(u8* dst18);
// Select a matrix row and the low bit of col, then return raw 4017 & 1E.
// Use rows 0..8 for keys; row 9 is used by detection. No row-range validation is emitted.
u8 __fkb_read_row_col(u8 row, u8 col);
/* R.O.B. / Famicom Robot optical flash primitives. These are timing primitives;
* game-specific command sequences should be built on top of __rob_pulse() or
* __rob_send_byte().
*/
// Set PPUMASK and its shadow to 1E or 00, then wait for an NMI-counter change.
// The caller supplies image/palette brightness; rendering enable alone does not create a white screen.
void __rob_flash(u8 bright);
// Run on_frames enabled-mask waits followed by off_frames disabled-mask waits; NMI must advance.
void __rob_pulse(u8 on_frames, u8 off_frames);
// Send eight bits MSB first: one uses 4 on/2 off waits; zero uses 2 on/4 off.
// Requires an advancing runtime NMI counter; 48 waits in total, final PPUMASK=0.
// This raw pattern does not add a robot command preamble or validate reception.
void __rob_send_byte(u8 pattern);
/* Expansion-port TTL serial / MIDI adapter support.
* The low-level serial layer assumes an external adapter that maps OUT0 to TX
* and D4 on $4017 to RX. MIDI helpers bit-bang 8N1 bytes at an approximate
* 31250 bps timing suitable for adapter bring-up and simple music sync.
*/
// Write bit & 1 to 4016, replacing the port output value; coordinate with other port users.
void __serial_tx_bit(u8 bit);
// Read 4017 bit 4 and normalize it to 0 or 1; an external serial adapter supplies the signal.
u8 __serial_rx_bit(void);
// Send start-low, eight least-significant-first bits and stop-high in 57-cycle
// NTSC bit cells (about 31400 bps). Stop DMC DMA and mask IRQ/NMI during transfers;
// PAL timing and electrical adapter compatibility are not established here.
void __midi_out_byte(u8 byte);
// Block for a low start signal and sample eight bits. No timeout, framing check or receive buffer is provided.
u8 __midi_in_byte(void);
// Send 90|(ch&0F), note and velocity. Supply 7-bit data bytes; they are not masked by this helper.
void __midi_note_on(u8 ch, u8 note, u8 velocity);
// Send 80|(ch&0F), note and velocity through the same synchronous serial path.
void __midi_note_off(u8 ch, u8 note, u8 velocity);
// Send B0|(ch&0F), controller and value; both data arguments must be valid 7-bit values.
void __midi_control_change(u8 ch, u8 cc, u8 value);
// Send C0|(ch&0F) followed by the caller-supplied 7-bit program value.
void __midi_program_change(u8 ch, u8 program);
// Transmit the single realtime byte F8 synchronously; this does not schedule future ticks.
void __midi_clock(void);
// Transmit realtime Start (FA) through the serial adapter path.
void __midi_start(void);
// Transmit realtime Continue (FB) through the serial adapter path.
void __midi_continue(void);
// Transmit realtime Stop (FC) through the serial adapter path.
void __midi_stop(void);
/* Mapper */
// Return the low byte of the mapper number selected for this build; no cartridge probe is performed.
u8 __mapper_id(void);
// Select a CNROM bank or MMC3 register 0 bank; unsupported mapper profiles produce a compile error.
void __chr_bank_set(u8 bank);
// Use the same CNROM/MMC3 register 0 selection as __chr_bank_set.
void __chr_bank_set0(u8 bank);
// Select MMC3 register 1, or the same full CNROM bank register; this is mapper-specific banking.
void __chr_bank_set1(u8 bank);
// Emit the selected mapper mirroring operation; available modes and writable control depend on the board.
void __mirroring_set(u8 mode);
// Write the MMC3 IRQ latch and reload registers. This does not enable IRQ delivery.
void __irq_scanline_set(u8 line);
// Alias the MMC3 IRQ latch/reload operation; other mapper profiles are rejected.
void __mapper_irq_set(u8 scanline);
// Write MMC3 E001. The CPU IRQ mask and a valid handler are separate caller responsibilities.
void __mapper_irq_enable(void);
// Write MMC3 E000 to disable and acknowledge mapper IRQs.
void __mapper_irq_disable(void);
// Use the same E000 write as disable; explicitly re-enable when another interrupt is needed.
void __mapper_irq_ack(void);
// Wait for the current hit flag to clear, then for a new hit. No timeout is provided.
void __sprite0_wait_hit(void);
// Wait for a new sprite-zero hit, reset the PPU latch and write X/Y scroll; requires a visible hit setup.
void __split_scroll_sprite0(u8 x, u8 y);
/* FDS. Status and sound primitives are emitted on --mapper=fds.
* File/overlay APIs use the FDS BIOS direct-pointer routines. __fds_load_file()
* and __fds_load_overlay() load to the destination encoded in the disk file
* header. __fds_file_exists() and __fds_file_size() are side-effect-free when
* a disk-builder manifest is supplied with --fds-meta / --fds-manifest.
*/
// Return the compile-time FDS mapper selection as 0 or 1; this is not a hardware probe.
u8 __fds_available(void);
// Return the inverted disk-absent bit from 4032 as 0 or 1; this checks presence, not transfer completion.
u8 __fds_disk_ready(void);
// Return raw status mask 4032 & 04, either 0 or 4. This is not a numbered disk-side identifier.
u8 __fds_side(void);
// Read the entire 4030 status register; this is not a stored BIOS error code.
u8 __fds_error(void);
// Poll disk presence until the absent bit clears. There is no timeout or cancellation path.
void __fds_wait_ready(void);
// Alias the same disk-presence wait as __fds_wait_ready; the call can block indefinitely.
void __fds_wait_insert(void);
// Native FDS32: load a non-boot file at its disk-header address, then memmove
// PRG bytes to dst. Null dst keeps the native destination (also for PPU files).
// Both CPU spans must fit $0200-$07FF or $6000-$DFFF and be owned by the caller.
// Return 0, a BIOS error, $40 for a missing transfer, or $FF for invalid input.
// Call from common code with disk-I/O interrupt/display prerequisites satisfied.
u8 __fds_load_file(u8 id, u8* dst);
// Overwrite the final non-boot PRG slot on its side, preserving its ID/name/load
// address. len must equal its packaged size; src must be a valid CPU RAM span.
// The resolved physical ordinal selects the slot, independently of file ID.
// Return 0, a BIOS error, or $FF before any write for an invalid slot/span/size.
u8 __fds_save_file(u8 id, const u8* src, u16 len);
// Load disk file id through BIOS, return its error byte and mark bank residency FF (unknown).
u8 __fds_load_overlay(u8 id);
// Load logical bank 1 from its separate boot file, or bank 2+ from overlay-start-id+(bank-2).
// Return zero on success, FF for invalid/unavailable mappings, or the BIOS error.
// A failed BIOS transfer marks residency FF (unknown); a matching valid guard can skip loading.
u8 __fds_load_bank(u8 bank);
// Return zero when the software residency record matches, otherwise call __fds_load_bank.
u8 __fds_require_bank(u8 bank);
// Return the software resident-bank byte, or FF after a failed bank transfer.
// This is not a checksum of overlay memory.
u8 __fds_current_bank(void);
// Compare a valid bank with the software resident-bank byte; FF (unknown) never matches.
u8 __fds_is_bank_resident(u8 bank);
// Return the linker-generated overlay-function count; zero if the table is disabled.
u8 __fds_overlay_function_count(void);
// Evaluate bank once and call the named zero-argument function using its linked bank.
// Restore the caller window before returning; restoration failure stops in common code.
u8 __fds_overlay_farcall(u8 bank, u16 func);
// Same named, zero-argument dispatch as __fds_overlay_farcall; evaluate bank once.
u8 __fds_farcall(u8 bank, u16 func);
// Query the compiled --fds-meta/--fds-manifest table, not the inserted disk; return 0 or 1.
u8 __fds_file_exists(u8 id);
// Return the compiled metadata size, or zero if id is absent. No disk directory is read.
u16 __fds_file_size(u8 id);
// Write 83 to 4023 and zero to 4089/408A to initialize disk/sound register gates.
void __fds_sound_enable(void);
// Copy exactly 64 readable bytes to wave RAM with wave writes enabled, then set 4089 to zero.
void __fds_wave_load(const u8* wave);
// Stream 32 readable bytes to 4088; the caller prepares modulation state and write conditions.
void __fds_mod_load(const u8* mod);
// Write a 12-bit frequency to 4082/4083; upper control bits in 4083 are cleared.
void __fds_freq_set(u16 freq);
// Write (volume & 3F) | 80 to 4080 for direct volume control; masking wraps, rather than clamps.
void __fds_volume_set(u8 volume);
// Write the three raw arguments to 4080, 4084 and 408A in that order.
// The legacy parameter names do not describe three independently packed envelope fields.
void __fds_env_set(u8 a, u8 b, u8 c);
#endifkitaqfc/lib/intrinsics.h
keyboard.h — キーボードの走査
ファミリーベーシックのキーボード行列を走査する組み込み関数です。接続判定、全行の読み取り、指定行・列群の読み取りを使い分けます。接続判定は信号パターンによる推定です。
全行走査には18バイトの書き込み先を用意します。戻る値はキー名や文字コードではなく、ポートのマスク済みビット列です。通常のキー走査は行0~8を使い、押下状態をゲーム側で対応するキーへ変換してください。
関数の使い方とサンプル: __fkb_detect, __fkb_scan, __fkb_read_row_col
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include "intrinsics.h"
// Probe selected-row and disabled-matrix bit patterns and return 0 or 1; this is a heuristic detection.
u8 __fkb_detect(void);
// Write 18 raw masked samples (4017 & 1E), two per row, to caller storage; reset 4016 afterward.
void __fkb_scan(u8* dst18);
// Select a matrix row and the low bit of col, then return raw 4017 & 1E.
// Use rows 0..8 for keys; row 9 is used by detection. No row-range validation is emitted.
u8 __fkb_read_row_col(u8 row, u8 col);
#endifkitaqfc/lib/keyboard.h
mapper.h — マッパーのバンクとIRQ
カートリッジのPRG・CHRバンク、ミラーリング、マッパー割り込みなどを操作します。同じ関数名でも使える範囲は--mapperで選ぶ基板に依存し、未対応の操作はコンパイル時に拒否されるものがあります。
PRGを切り替えると、CPUから見えるコードやデータも変わります。通常のポインタが常に全バンクを参照できるわけではありません。離れたバンクの読み取り・コピーにはfar系APIを選びます。スプライト0待機はマッパーIRQとは別方式で、成立する画面条件が必要です。
関数の使い方とサンプル: __bankswitch, __far_memcpy, __mapper_irq_set, __sprite0_wait_hit
#ifndef MAPPER_H
#define MAPPER_H
#include "intrinsics.h"
// Resolve a symbol or function placement to its byte bank number at compilation time.
u8 __bankof(u16 symbol_or_function);
// Select the mapper-specific PRG bank; the common bank and legal bank range depend on the target board.
void __bankswitch(u8 bank);
// Use the same mapper PRG-switch helper as __bankswitch; retain executable code in a valid mapping.
void __prg_bank_set(u8 bank);
// Invoke the compiler banked-call path; target placement and calling convention must match the selected mapper.
u8 __farcall(u8 bank, u16 func);
// Switch to the source bank, copy len bytes forward and restore the previous bank.
// Keep destination RAM and helper code accessible throughout; overlapping copies are not memmove.
void __far_memcpy(u8* dst, u8 bank, u16 src, u16 len);
// Temporarily select bank, read one byte at addr and restore the prior bank mapping.
u8 __farpeek8(u8 bank, u16 addr);
// Temporarily select bank and read a little-endian word, then restore the previous mapping.
u16 __farpeek16(u8 bank, u16 addr);
// Return the low byte of the mapper number selected for this build; no cartridge probe is performed.
u8 __mapper_id(void);
// Select a CNROM bank or MMC3 register 0 bank; unsupported mapper profiles produce a compile error.
void __chr_bank_set(u8 bank);
// Use the same CNROM/MMC3 register 0 selection as __chr_bank_set.
void __chr_bank_set0(u8 bank);
// Select MMC3 register 1, or the same full CNROM bank register; this is mapper-specific banking.
void __chr_bank_set1(u8 bank);
// Emit the selected mapper mirroring operation; available modes and writable control depend on the board.
void __mirroring_set(u8 mode);
// Write the MMC3 IRQ latch and reload registers. This does not enable IRQ delivery.
void __irq_scanline_set(u8 line);
// Alias the MMC3 IRQ latch/reload operation; other mapper profiles are rejected.
void __mapper_irq_set(u8 scanline);
// Write MMC3 E001. The CPU IRQ mask and a valid handler are separate caller responsibilities.
void __mapper_irq_enable(void);
// Write MMC3 E000 to disable and acknowledge mapper IRQs.
void __mapper_irq_disable(void);
// Use the same E000 write as disable; explicitly re-enable when another interrupt is needed.
void __mapper_irq_ack(void);
// Wait for the current hit flag to clear, then for a new hit. No timeout is provided.
void __sprite0_wait_hit(void);
// Wait for a new sprite-zero hit, reset the PPU latch and write X/Y scroll; requires a visible hit setup.
void __split_scroll_sprite0(u8 x, u8 y);
#endifkitaqfc/lib/mapper.h
math_fast.h — 高速な整数計算
距離の目安、矩形内判定、タイル座標の一次元化、乱数のための組み込み関数をまとめています。距離はユークリッド距離ではなく縦横差の和で、戻り値は8ビットです。矩形は右端と下端を含まず、マップ添字の計算は範囲検査を行いません。
乱数は共有状態を持つLFSRです。同じシードから同じ列を再現できるためゲームの再現試験に使えますが、暗号用途には使いません。これらはコンパイラの組み込み処理で、別のmath_fast.cをリンクする必要はありません。
関数の使い方とサンプル: __manhattan, __xy_in_rect, __map_index, __rng8
#ifndef MATH_FAST_H
#define MATH_FAST_H
#include "intrinsics.h"
/*
* Fast math primitives are compiler intrinsics. These declarations are kept
* here so game code can include a game-facing math header instead of the full
* intrinsic surface.
*/
// Return abs(x1-x2)+abs(y1-y2) modulo 256; large distances are not saturated.
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);
// Return 0 or 1 for half-open bounds. Zero extents are empty; coordinates do not wrap across 255.
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);
// Return y*width+x as a word without checking coordinates against map dimensions.
u16 __map_index(u8 x, u8 y, u8 width);
// Advance the shared 16-bit LFSR and return the XOR of its two bytes; zero state is reseeded to A55A.
u8 __rng8(void);
// Replace both persistent state bytes. Reusing a seed reproduces the sequence; this is not cryptographic randomness.
void __rng_seed(u16 seed);
#endifkitaqfc/lib/math_fast.h
math_fixed.h — 固定小数点計算の組み込み窓口
整数の乗算、積和、内積に使う組み込み関数です。8×8の上位バイトを得る処理と、16ビットの積や積和を得る処理は、値の尺度が異なります。積の下位16ビットを返す関数では桁あふれが周回し、飽和しません。
FCの__dot*_q8_8は名前にQ8.8が含まれていても最後の縮尺調整や丸めを行いません。符号付き版は渡されたビット列を符号付き整数として扱います。座標や速度の小数部を何ビットにするかは、各APIの実際の計算に合わせて決めてください。
関数の使い方とサンプル: __mul8x8_hi, __smul16x8, __dot2_q8_8
#ifndef MATH_FIXED_H
#define MATH_FIXED_H
#include "intrinsics.h"
/* Fixed-point helpers are compiler-recognized intrinsic entry points. */
// Return the high byte of the unsigned 8-by-8 product, equivalent to floor(a*b/256).
u8 __mul8x8_hi(u8 a, u8 b);
// Return the low word of the unsigned product; overflow wraps without saturation.
u16 __mul16x8(u16 a, u8 b);
// Interpret a and b as signed two's-complement bit patterns and return the low word of their product.
u16 __smul16x8(u16 a, u8 b);
// Return acc+a*b modulo 65536 using unsigned multiplication.
u16 __mac16(u16 acc, u16 a, u8 b);
// Add the signed-pattern product to acc modulo 65536; the API carries the result as u16 bits.
u16 __smac16(u16 acc, u16 a, u8 b);
// Return a0*b0+a1*b1 modulo 65536. The FC backend inserts no Q8.8 rescaling or rounding.
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three unsigned word-by-byte products modulo 65536 without a final scaling shift.
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// Sum two signed-pattern products modulo 65536; no Q8.8 scaling shift is generated.
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three signed-pattern products modulo 65536; choose operand units explicitly in the caller.
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
#endifkitaqfc/lib/math_fixed.h
math_lut.h — ルックアップテーブル
math_lut.cに格納された、128を中心とする符号なしの正弦波テーブルMATH_SINを宣言します。関数ではないため、添字で値を読み出します。テーブル本体を使うにはmath_lut.cもビルドに含めます。
1周期を256等分した値で、有効な添字は0~255です。角度0・64・128・192の値は128・255・128・1になり、255の次は0へ戻せます。式は128 + round(127*sin(2*pi*phase/256))です。符号付きの振幅として使う場合は、16ビットの符号付き型へ広げて128を引きます。ワイヤーフレーム用の内部テーブルとは別のデータです。
#ifndef MATH_LUT_H
#define MATH_LUT_H
// Uniform 256-phase unsigned sine table in ROM. Range 1..255, centered at 128.
// Link math_lut.c once; indices 0..255 are valid and byte phase wrap is supported.
extern unsigned char MATH_SIN[256];
#endifkitaqfc/lib/math_lut.h
metasprite.h — 複数OBJをまとめた画像
/*
* Optional declarations for the phase 10 NES metasprite helper.
*
* Metasprite data layout:
* [dx, dy, tile, attr] repeated, terminated by dx=0xFF
*/
// Read four-byte records [dx,dy,tile,attr] until dx=0xFF, writing them to
// shadow OAM. Coordinates and destination offsets wrap as bytes. The caller
// must supply a terminator and enough unused slots; there is no capacity guard.
// Return the next slot index derived from the final wrapped byte offset.
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite);
// Compiler intrinsic counterpart; this declaration does not route through the C function above.
unsigned char __metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, const unsigned char* metasprite);
// Hide consecutive shadow slots by setting Y to 0xF8. Keep the requested
// range within 64 slots to avoid wrapping and hiding earlier entries.
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count);kitaqfc/lib/metasprite.h
mic.h — マイク入力
2Pコントローラーのマイク入力ビットを、0または1として読む組み込み関数です。__joypad2p_voice()と__mic_read2p()は同じ入力を読みます。音声サンプルの録音や音声認識ではなく、マイク入力に応じたゲーム操作のきっかけとして使います。
関数の使い方とサンプル: __mic_read2p
#ifndef MIC_H
#define MIC_H
#define VOICE_H
#include "intrinsics.h"
// Return controller-port 4016 bit 2 normalized to 0 or 1; this is a microphone level bit, not PCM audio.
u8 __joypad2p_voice(void);
// Alias the same normalized microphone input bit as __joypad2p_voice.
u8 __mic_read2p(void);
#endifkitaqfc/lib/mic.h
midi.h — MIDI入出力の窓口
外部シリアルアダプターへビットやMIDIメッセージを送受信する宣言です。note_on・note_offなどは指定したメッセージを同期送信し、FC内蔵音源を直接鳴らす関数ではありません。データ引数には有効な7ビット値を指定します。
送信は固定命令列の待機を使い、割り込み時間も加わるため、この実装だけでMIDIの通信速度が保証されるわけではありません。受信にはタイムアウトや受信キューがありません。接続する回路と信号タイミングを確認し、同じコントローラーポートを使う処理と調整してください。
関数の使い方とサンプル: __serial_tx_bit, __midi_out_byte, __midi_in_byte, __midi_note_on
#ifndef MIDI_H
#define MIDI_H
#include "intrinsics.h"
// Write bit & 1 to 4016, replacing the port output value; coordinate with other port users.
void __serial_tx_bit(u8 bit);
// Read 4017 bit 4 and normalize it to 0 or 1; an external serial adapter supplies the signal.
u8 __serial_rx_bit(void);
// Send start-low, eight least-significant-first bits and stop-high using fixed NOP delays.
// Instruction and interrupt overhead is additional; the emitted loop does not establish a verified MIDI baud rate.
void __midi_out_byte(u8 byte);
// Block for a low start signal and sample eight bits. No timeout, framing check or receive buffer is provided.
u8 __midi_in_byte(void);
// Send 90|(ch&0F), note and velocity. Supply 7-bit data bytes; they are not masked by this helper.
void __midi_note_on(u8 ch, u8 note, u8 velocity);
// Send 80|(ch&0F), note and velocity through the same synchronous serial path.
void __midi_note_off(u8 ch, u8 note, u8 velocity);
// Send B0|(ch&0F), controller and value; both data arguments must be valid 7-bit values.
void __midi_control_change(u8 ch, u8 cc, u8 value);
// Send C0|(ch&0F) followed by the caller-supplied 7-bit program value.
void __midi_program_change(u8 ch, u8 program);
// Transmit the single realtime byte F8 synchronously; this does not schedule future ticks.
void __midi_clock(void);
// Transmit realtime Start (FA) through the serial adapter path.
void __midi_start(void);
// Transmit realtime Continue (FB) through the serial adapter path.
void __midi_continue(void);
// Transmit realtime Stop (FC) through the serial adapter path.
void __midi_stop(void);
#endifkitaqfc/lib/midi.h
nametable_asset.h — ネームテーブル用素材と配置
/*
* Optional declarations for phase12 nametable / attribute asset helpers.
*/
extern unsigned char nes_attr_shadow[64];
// Mask the index to the four logical nametables and return its PPU base.
// Physical storage still depends on the cartridge's mirroring configuration.
unsigned short nes_nt_base_from_index(unsigned char index);
// Locate the 64-byte attribute table at offset 0x03C0 from a nametable base.
unsigned short nes_attr_base_from_nt(unsigned short nt_base);
// Fill all 64 shadow bytes with an already-packed attribute value.
void nes_attr_shadow_clear(unsigned char value);
// Copy an entire packed attribute table into shadow RAM without writing the PPU.
void nes_attr_shadow_copy(unsigned char* src64);
// Replace only the chosen quadrant's palette bits, preserving its neighbors.
// Palette IDs are masked to two bits; tile coordinates must fit the shadow table.
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index);
// Upload all attribute shadow bytes immediately; the caller provides safe PPU timing.
void nes_attr_apply_now(unsigned short nt_base);
// Copy all shadow attribute bytes into the runtime queue and return its admission result.
unsigned char nes_attr_queue_all(unsigned short nt_base);
// Write one complete 32-tile row directly. The caller validates the row and PPU timing.
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32);
// Copy one complete nametable row into the runtime queue.
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32);
// Write each rectangle row immediately, advancing source by pitch rather than
// width. Caller-provided coordinates, storage and timing are not clipped or checked.
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);
// Copy rows into the runtime queue until complete or full. Failure leaves
// earlier rows queued; it does not roll back a partially admitted rectangle.
// Widths must respect the queue literal-record limit.
// Pitch is source bytes per row. Each copied literal record costs width+3 queue bytes;
// check total admission needs when partial updates are unacceptable.
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);
// Queue one fill record per row, stopping on failure while retaining previously
// queued rows. The runtime fill-length and rectangle bounds requirements apply.
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value);
// Upload 960 tile bytes followed by 64 attribute bytes. This bulk operation
// does not disable rendering or wait for VBlank on its own.
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64);kitaqfc/lib/nametable_asset.h
nes_game.h — ゲーム向け操作名のマクロ
#ifndef NES_GAME_H
#define NES_GAME_H
#include "core.h"
#include "intrinsics.h"
/*
* AI-friendly NES operation aliases.
*
* These names intentionally describe gameplay-side operations. They are thin
* aliases over KITAQFC intrinsics so the compiler can still record the exact
* NES action for diagnostics and KUROSAKI metadata.
*/
// These aliases use the compiler intrinsic queue. They do not select the separate C runtime queue.
#define nes_vram_put(addr, value) __vramq_put((addr), (value))
#define nes_vram_copy(addr, src, len) __vramq_copy((addr), (src), (len))
#define nes_vram_fill(addr, value, len) __vramq_fill((addr), (value), (len))
#define nes_vram_commit() __vramq_commit()
#define nes_vram_clear_queue() __vramq_clear()
#define nes_oam_clear() __oam_clear()
// This zero-argument macro hides the same-named page-argument C runtime function in included code.
#define nes_oam_dma() __oam_dma()
#define nes_sprite_set(i,x,y,t,a) __sprite_set((i),(x),(y),(t),(a))
#define nes_sprite_move(i,x,y) __sprite_move((i),(x),(y))
#define nes_sprite_hide(i) __sprite_hide((i))
// Select the compiler intrinsic implementation even when the C metasprite header is also included.
#define nes_metasprite_draw(i,x,y,data) __metasprite_draw((i),(x),(y),(data))
#define nes_pad1() __pad_read1_safe()
#define nes_pad2() __pad_read2_safe()
#define nes_mapper_irq_set(scanline) __mapper_irq_set((scanline))
#define nes_mapper_irq_enable() __mapper_irq_enable()
#define nes_mapper_irq_disable() __mapper_irq_disable()
#define nes_mapper_irq_ack() __mapper_irq_ack()
#define nes_split_scroll_sprite0(x,y) __split_scroll_sprite0((x),(y))
#define nes_fds_wave_load(wave64) __fds_wave_load((wave64))
#define nes_fds_load_bank(bank) __fds_load_bank((bank))
#define nes_fds_farcall(bank, func) __fds_overlay_farcall((bank), (func))
#endifkitaqfc/lib/nes_game.h
oam.h — OAMシャドウ操作
スプライトの表示情報をCPU側のシャドーOAMへ書き、DMAでPPUへ転送する組み込み関数です。通常のシャドーはCPUページ02の64項目です。set・move・tile・attrは項目の全部または一部を更新し、hideはY値を変更して隠します。
シャドーの変更だけでは画面に反映されません。VBlankなど安全なタイミングでDMAを実行してください。__oam_dma()はページ02を転送し、__oam_dma_page(page)は転送元ページを指定します。タイル番号の変更は絵柄データのアップロードではありません。
関数の使い方とサンプル: __sprite_set, __sprite_hide, __oam_dma, __oam_dma_page
#ifndef OAM_H
#define OAM_H
#include "intrinsics.h"
// Set OAMADDR to zero and start DMA from CPU page 02; arrange safe PPU timing in the caller.
void __oam_dma(void);
// Set OAMADDR to zero and DMA 256 bytes from page<<8. The source page must remain readable.
void __oam_dma_page(u8 page);
// Hide all 64 page-02 shadow sprites by writing Y=F0; hardware OAM changes only after DMA.
void __oam_clear(void);
// Write Y, tile, attributes and X to one shadow entry. Use indices 0..63; coordinates are raw OAM bytes.
void __sprite_set(u8 index, u8 x, u8 y, u8 tile, u8 attr);
// Replace only shadow X/Y for index 0..63; tile and attribute bytes remain unchanged.
void __sprite_move(u8 index, u8 x, u8 y);
// Replace the selected shadow tile byte; this does not upload tile patterns.
void __sprite_tile(u8 index, u8 tile);
// Replace the selected shadow attribute byte, including palette, priority and flip bits.
void __sprite_attr(u8 index, u8 attr);
// Write F0 to the selected shadow Y byte; issue DMA to make the change visible.
void __sprite_hide(u8 index);
#endifkitaqfc/lib/oam.h
oam_fair.h — 優先順位付きスプライト交替表示
#ifndef KQ_OAM_FAIR_H
#define KQ_OAM_FAIR_H
#include "core.h"
/* 64-entry software sprite pool. Define the five arrays/cursor declared below
* and include oam_fair_impl.h in ONE translation unit, in a fixed PRG bank.
* X/Y are center coordinates, ACTIVE is a byte array. OAM is a 256-byte
* DMA shadow; USED is its byte cursor. Entries already in OAM stay first.
* The odd stride selects every starting entry over 64 calls. Hardware still
* has a 64-sprite / 8-sprite-per-scanline limit: this distributes flicker.
* Call only from the main loop; commit the completed OAM in VBlank.
*/
extern u8 oam_fair_phase;
extern u8 oam_fair_x[64];
extern u8 oam_fair_y[64];
extern u8 oam_fair_active[64];
extern u8 oam_fair_shadow[256];
extern u8 oam_fair_used;
extern u8 oam_fair_limit;
extern u8 oam_fair_tile;
extern u8 oam_fair_attr;
extern u8 oam_fair_drawn;
// Append active pool entries using the configured common tile/attributes. The emitter stops at byte cursor 252,
// leaving slot 63 unused to avoid cursor wrap. Hide unused trailing OAM entries separately before DMA.
void OAM_FairDraw(void);
#endifkitaqfc/lib/oam_fair.h
oam_fair_impl.h — スプライト交替表示の実装
OAM_FairDraw()の実装を含むヘッダーです。ゲーム側に用意した64項目の座標・有効フラグを巡回し、開始位置を毎回13進めて表示順を分散します。宣言だけのヘッダーとは異なり、プログラム全体で一度だけ取り込みます。
既に書いたOAM項目の後ろへ追加するため、oam_fair_usedには4の倍数のバイト位置を設定します。カーソルの周回を避けるため最後の4バイト枠には書きません。未使用項目の消去とDMAは呼び出し側の担当で、走査線ごとのスプライト数制限も残ります。
関数の使い方とサンプル: OAM_FairDraw
#include "oam_fair.h"
u8 oam_fair_phase;
u8 oam_fair_limit;
u8 oam_fair_tile;
u8 oam_fair_attr;
u8 oam_fair_drawn;
u8 oam_fair_scanned;
// Rotate the starting pool entry by 13 modulo 64, then append active sprites
// until the caller's draw limit or OAM byte cursor limit is reached. Coordinates
// describe 8x8 centers: subtract four for X and five for raw OAM Y. Include once
// and invoke it from the main loop with a four-byte-aligned used cursor.
void OAM_FairDraw(void) {
__asm {
LDA oam_fair_phase
CLC
ADC #13
AND #63
STA oam_fair_phase
TAX
LDY oam_fair_used
LDA #0
STA oam_fair_drawn
STA oam_fair_scanned
// Visit each pool entry at most once, preserving already-written OAM entries
// and sharing the configured tile/attribute values across emitted sprites.
kq_fair_loop:
LDA oam_fair_active,X
BEQ kq_fair_next
LDA oam_fair_drawn
CMP oam_fair_limit
BCS kq_fair_done
// Reserve the final four-byte slot so advancing this byte cursor cannot wrap to zero.
CPY #252
BCS kq_fair_done
// Convert the 8x8 center to raw OAM coordinates; Y also needs the hardware minus-one bias.
LDA oam_fair_y,X
SEC
SBC #5
STA oam_fair_shadow,Y
LDA oam_fair_tile
STA oam_fair_shadow+1,Y
LDA oam_fair_attr
STA oam_fair_shadow+2,Y
LDA oam_fair_x,X
SEC
SBC #4
STA oam_fair_shadow+3,Y
INY
INY
INY
INY
INC oam_fair_drawn
// Advance cyclically and stop after returning to this frame's starting entry.
kq_fair_next:
TXA
CLC
ADC #1
AND #63
TAX
CPX oam_fair_phase
BNE kq_fair_loop
// Publish the updated byte cursor for later OAM writers and the VBlank DMA path.
kq_fair_done:
STY oam_fair_used
}
}kitaqfc/lib/oam_fair_impl.h
pad.h — NESコントローラー入力
/*
* Optional declarations for the phase 8 NES pad runtime.
*
* Button bit layout in nes_pad1_cur / pressed / released:
* bit0=A bit1=B bit2=Select bit3=Start bit4=Up bit5=Down bit6=Left bit7=Right
*/
extern unsigned char nes_pad1_cur;
extern unsigned char nes_pad1_prev;
extern unsigned char nes_pad1_pressed;
extern unsigned char nes_pad1_released;
// Latch controller input with a 1-to-0 strobe and shift eight controller-1 bits
// into held/pressed/released masks. This direct path does not perform repeated
// reads for DMC interference; the final JOY2 read does not populate player-2 state.
void nes_pad_poll(void);kitaqfc/lib/pad.h
palette.h — NESのパレット操作
/*
* Optional declarations for the phase 11 NES palette helper.
*/
extern unsigned char nes_palette_shadow[32];
// Copy exactly 32 palette bytes to the fixed shadow buffer without writing the PPU.
void nes_palette_copy(unsigned char* src32);
// Replace shadow palette data and upload all 32 bytes immediately. The caller
// provides a safe PPU access period; this helper does not wait for VBlank.
void nes_palette_apply_now(unsigned char* src32);
// Upload the current palette shadow directly, with timing controlled by the caller.
void nes_palette_apply_shadow_now(void);
// Update the shadow and copy all colors into the runtime queue. A queue
// failure leaves the new shadow contents in place.
// Return one if the runtime queue accepted the copied payload, zero if it was full.
// Admission does not mean the NMI consumer has already updated the PPU.
unsigned char nes_palette_queue_all(unsigned char* src32);
// Update and queue four background palette bytes. pal_index must be 0..3;
// there is no range check before writing the shadow.
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4);
// Update and queue four sprite palette bytes at the 0x3F10 region. The
// caller supplies pal_index 0..3; NES palette mirroring still applies.
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4);kitaqfc/lib/palette.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"
// Lightweight deterministic 2D physics for KITAQFC.
// 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 uses non-reentrant helper 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 by a signed eight-bit coefficient divided by 256.
// 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);
// Optional byte-sized eighth-pixel channels for small custom update loops.
#define KQ2D_SUBPIXEL_BITS ((u8)3)
#define KQ2D_SUBPIXEL_ONE ((u8)8)
#define KQ2D_SUBPIXEL_MASK ((u8)7)
// Optional byte-channel identifiers for application-specific movement.
#define KQ2D_DRAG_BRAKE ((u8)1)
#define KQ2D_DRAG_STUN ((u8)2)
#define KQ2D_DRAG_COAST ((u8)3)
#define KQ2D_DRAG_THRUST ((u8)4)
#define KQ2D_DIR_NONE ((u8)0)
#define KQ2D_DIR_NEGATIVE ((u8)1)
#define KQ2D_DIR_POSITIVE ((u8)2)
/*
* Byte-sized Q5.3 channels for KITAQFC.
*
* Keep the channels as flat game-state fields in a hot loop. That avoids
* aggregate/pointer call traffic while retaining eighth-pixel integration.
*/
// Store integer pixel position separately from the 0..7 fractional remainder.
// Speed is an unsigned eighth-pixel magnitude; direction is a separate channel.
typedef u8 KQ2DPosition;
typedef u8 KQ2DFraction;
typedef u8 KQ2DSpeed;
typedef u8 KQ2DDirection;kitaqfc/lib/physics2d.h
physics3d.h — 3DのAABB物理計算
#pragma once
#include "core.h"
// Lightweight deterministic 3D physics for KITAQFC.
// 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 uses non-reentrant helper 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, dividing each by 256 (toward zero) 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);kitaqfc/lib/physics3d.h
ppu.h — PPU画面操作の宣言
#ifndef PPU_H
#define PPU_H
// Link ppu.c. Bulk palette/nametable transfers require rendering to be disabled.
// Disable background and sprite rendering, preserving other PPUMASK bits.
void nes_ppu_screen_off(void);
// Set scroll to (0,0), then apply the requested PPUCTRL and PPUMASK values.
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask);
// Copy 32 bytes into $3F00..$3F1F with increment one; preserve PPUCTRL.
// Null is ignored. Hardware palette mirrors apply; source bank must stay mapped.
void nes_ppu_load_palette(unsigned char* pal32);
// Fill 960 tiles and 64 packed attributes; preserve PPUCTRL.
// Accept exactly $2000/$2400/$2800/$2C00; invalid bases are ignored.
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr);
// Address-byte variants reset the latch and coexist with runtime.c's word seek.
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo);
// Direct transfers follow the current PPUCTRL increment; count is 0..255.
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count);
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count);
#endifkitaqfc/lib/ppu.h
ppu_direct.h — 直接PPUへ書く操作
PPUの表示制御、アドレス指定、データ書き込みをすぐに行う組み込み関数です。VRAMキューへ後回しにする処理ではありません。大量の書き換えは表示停止中に行い、表示中はVBlank内に収まる量へ制限します。
PPUのアドレス指定とスクロールは書き込みラッチを共有します。状態レジスタの読み取りにもラッチのリセットやVBlankフラグのクリアという副作用があります。アドレス、転送、スクロール復元の順序を各APIの例に合わせてください。
関数の使い方とサンプル: __ppu_addr, __vram_write, __ppu_read_status
#ifndef PPU_DIRECT_H
#define PPU_DIRECT_H
#include "intrinsics.h"
/* Direct PPU access. Use only during vblank or when rendering is disabled. */
// Invoke the compiler rendering-enable helper; this header contains declarations, not C fallback implementations.
void __ppu_on(void);
// Invoke the compiler rendering-disable helper before bulk direct PPU updates.
void __ppu_off(void);
// Replace PPUMASK with the supplied byte; the caller chooses rendering, clipping and emphasis bits.
void __ppu_mask_set(u8 value);
// Replace PPUCTRL, including NMI enable, pattern-table choices and address increment mode.
void __ppu_ctrl_set(u8 value);
// Set the PPU address through the compiler helper; address writes affect the shared scroll/address latch.
void __ppu_addr(u16 ppu_addr);
// Write one PPUDATA byte using the current PPU address and increment mode.
void __ppu_data(u8 value);
// Read PPUSTATUS; the hardware clears VBlank status and resets the shared write latch.
u8 __ppu_read_status(void);
// Write a byte-counted source range directly; arrange PPU access timing and source-bank visibility.
void __vram_write(u16 ppu_addr, const u8* src, u8 len);
// Write repeated bytes directly; this is not an enqueue operation.
void __vram_fill(u16 ppu_addr, u8 value, u8 len);
#endifkitaqfc/lib/ppu_direct.h
rob.h — ROB制御
画面の明暗とフレーム待機を組み合わせて、R.O.B.向けの光パルス列を作る組み込み関数です。flashは表示マスクの変更、pulseはオン・オフ期間、send_byteは8ビットのパターン送信を担当します。send_byteは上位ビットから4/2または2/4フレームのパルスを出します。
表示を有効にするだけでは白い画面になりません。明るさを決める絵柄とパレットをゲーム側で用意し、NMIのフレームカウンターが進む状態で呼びます。実際の受光装置との通信成立と、エミュレーターで確認する命令列・画面変化は区別してください。
関数の使い方とサンプル: __rob_flash, __rob_pulse, __rob_send_byte
#ifndef ROB_H
#define ROB_H
#include "intrinsics.h"
// Set PPUMASK and its shadow to 1E or 00, then wait for an NMI-counter change.
// The caller supplies image/palette brightness; rendering enable alone does not create a white screen.
void __rob_flash(u8 bright);
// Run on_frames enabled-mask waits followed by off_frames disabled-mask waits; NMI must advance.
void __rob_pulse(u8 on_frames, u8 off_frames);
// Send eight bits MSB first, using 4/2-frame pulses for one and 2/4 for zero.
// Preserve the outer bit counter and shifted pattern around each pulse helper.
void __rob_send_byte(u8 pattern);
#endifkitaqfc/lib/rob.h
runtime.h — NMI、OAM、PPU転送のCヘルパー
/*
* Core NES runtime declarations.
*
* Declare frame waits, direct PPU transfers, OAM DMA and the copied-payload VRAM queue.
*/
// The four-byte record follows hardware OAM byte order: Y, tile, attributes, X.
struct NesSprite4 {
unsigned char y;
unsigned char tile;
unsigned char attr;
unsigned char x;
};
extern unsigned char nes_nmi_counter;
extern unsigned char nes_oam_shadow[256];
// This counter measures the separate 192-byte copied-payload runtime queue, not the compiler intrinsic queue.
extern unsigned char nes_vram_queue_used;
extern unsigned char nes_vram_queue_overflow;
// Wait until the NMI handler advances its counter. NMI must already be enabled
// and able to execute; this polling loop has no timeout.
void nes_wait_nmi(void);
// Wait for the current VBlank to end, then for the next one to begin. Each
// PPUSTATUS read has hardware side effects, including clearing the VBlank flag.
void nes_vblank_wait(void);
// Reset the PPU address latch with a status read, then write the high/low address bytes.
void nes_ppu_seek(unsigned short ppu_addr);
// Write len source bytes directly through PPUDATA. Call only during an appropriate
// PPU access period; the address increment mode comes from the current PPUCTRL.
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len);
// Write value repeatedly through PPUDATA, using the current PPU address increment
// mode. This routine does not wait for VBlank or disable rendering.
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len);
// Reset the OAM destination and DMA one 256-byte CPU page. The hardware stalls
// the CPU during transfer; page is the source address high byte.
void nes_oam_dma(unsigned char page);
// Discard pending VRAM commands and clear the overflow latch; no PPU writes occur.
void nes_vram_queue_clear(void);
// Copy a literal payload into the queue and publish its new used length last.
// Accept lengths 0..127; reject larger lengths and latch overflow without changing the queue.
// The caller must ensure queue production cannot race its NMI consumer.
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len);
// Queue a four-byte fill record: address high/low, length with bit 7 set, value.
// Reject lengths above 127 and latch overflow on any capacity/format failure.
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len);
// Consume complete records in order during a safe PPU access period. Reading
// PPUSTATUS resets the shared address latch before each address pair. The caller
// is responsible for ensuring the transfer fits its NMI/VBlank time budget.
void nes_vram_queue_nmi_flush(void);kitaqfc/lib/runtime.h
scene.h — シーンの登録、変更と更新
#ifndef SCENE_H
#define SCENE_H
#include "core.h"
typedef void (*SceneFunc)(void);
typedef __packed struct SceneDef {
SceneFunc enter;
SceneFunc update;
SceneFunc draw;
SceneFunc exit;
} SceneDef;
// One shared in-flight transfer. remaining counts source bytes not yet copied into the queue.
// An inactive stream may still have commands awaiting the NMI consumer.
struct NesSceneStreamState {
unsigned char* src;
unsigned short ppu_addr;
unsigned short remaining;
unsigned char chunk;
unsigned char active;
};
extern struct NesSceneStreamState nes_scene_stream_state;
// 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();
// Replace the single active transfer and retain its source pointer. Keep the
// source storage and any required ROM bank readable until all bytes are queued.
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk);
// Prepare the 960-byte tile portion of a nametable using 32-byte queue records.
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960);
// Prepare the 64-byte attribute table at nametable base + 0x03C0.
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64);
// Prepare a 32-byte palette transfer to PPU address 0x3F00.
void nes_scene_stream_begin_palette(unsigned char* src32);
// Queue as many chunks as available capacity allows, capped at 127 bytes each.
// Return one when queue exhaustion requires a later retry, zero when all data has
// been queued. Zero does not mean the NMI consumer has already written the PPU.
unsigned char nes_scene_stream_step(void);
#endifkitaqfc/lib/scene.h
scroll.h — 背景・ウィンドウ・分割スクロール
/*
* Optional declarations for the phase 9 NES scroll helper.
*/
extern unsigned char nes_scroll_ctrl_base;
extern unsigned short nes_scroll_camera_x;
extern unsigned short nes_scroll_camera_y;
// Store PPUCTRL bits other than the two nametable-selection bits for later application.
void nes_scroll_set_base_ctrl(unsigned char ctrl);
// Store camera coordinates without touching hardware registers.
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y);
// Combine bit 8 of each camera coordinate with base PPUCTRL, reset the latch
// by reading PPUSTATUS, and write low-byte X/Y scroll. This uses 256-unit
// wrapping on both axes rather than normalizing Y to a 240-pixel nametable height.
// Initialize base control to the intended PPUCTRL value, including NMI enable, before applying.
// Only coordinate bits 0..8 are used; arrange safe PPU timing in the caller.
void nes_scroll_apply(void);
// Subtract the screen anchor from the world point into unsigned camera
// coordinates. Underflow wraps; world-edge clamping belongs to the game.
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y);kitaqfc/lib/scroll.h
sprite.h — OBJの確保、配置、メタスプライト、アニメーション
#ifndef SPRITE_H
#define SPRITE_H
#include "core.h"
#include "intrinsics.h"
// Keep this value within 1..64; tracking arrays and hardware OAM contain exactly 64 slots.
#ifndef SPRITE_MAX
#define SPRITE_MAX ((u8)64)
#endif
#define SPRITE_FLAG_PAL1 ((u8)0x01)
#define SPRITE_FLAG_PAL2 ((u8)0x02)
#define SPRITE_FLAG_PAL3 ((u8)0x03)
#define SPRITE_FLAG_PRIORITY ((u8)0x20)
#define SPRITE_FLAG_XFLIP ((u8)0x40)
#define SPRITE_FLAG_YFLIP ((u8)0x80)
// Signed per-part offsets are added modulo 256. This struct API has an explicit count,
// unlike the FF-terminated byte-stream API in metasprite.h.
typedef struct MetaSpritePart {
s8 dx;
s8 dy;
u8 tile;
u8 flags;
} MetaSpritePart;
// Initialize frame/ticks to valid values. Returned tile IDs advance by one regardless of hardware sprite size.
typedef struct SpriteAnim {
u8 first_tile;
u8 frame_count;
u8 frame;
u8 ticks;
u8 ticks_per_frame;
} SpriteAnim;
extern u8 kq_sprite_active[64];
extern u8 kq_sprite_used;
extern u8 kq_sprite_height;
// Reset allocation/position state and hide each OAM slot through the intrinsic.
// The software height defaults to eight; hardware sprite size is configured elsewhere.
void sprite_init(void);
// Claim and hide the first free slot, or return 0xFF if all slots are active.
u8 sprite_alloc(void);
// Release an active valid slot and hide it; invalid/repeated frees are ignored.
void sprite_free(u8 id);
// Update software coordinates and the intrinsic OAM position for a valid slot.
void sprite_set_pos(u8 id, u8 x, u8 y);
// Replace the tile byte of a valid OAM slot without changing allocation state.
void sprite_set_tile(u8 id, u8 tile);
// Replace the hardware attribute byte of a valid OAM slot.
void sprite_set_flags(u8 id, u8 flags);
// Mark the software position off screen and hide its OAM entry without freeing it.
void sprite_hide(u8 id);
// Transfer intrinsic shadow OAM immediately; this wrapper does not wait for VBlank.
void sprite_flush_oam_now(void);
// Wait for NMI before requesting OAM DMA.
void sprite_flush_oam(void);
// 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(void);
// Flag software overlap estimates above eight sprites on one scanline.
u8 sprite_warn_scanline_overflow(void);
// Estimate peak overlap across 240 lines from software Y/height values.
// This does not model pixel transparency, OAM priority or every hardware overflow quirk.
u8 sprite_max_scanline_count(void);
// 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 and wrap the frame index, returning the selected tile.
// Zero ticks-per-frame freezes advancement; null state returns zero.
u8 anim_update(SpriteAnim* anim);
#endifkitaqfc/lib/sprite.h
system.h — 起動、フレーム数、待機と割り込み
#ifndef SYSTEM_H
#define SYSTEM_H
#include "core.h"
#include "intrinsics.h"
// A synchronous callback invoked after the software frame count is incremented.
typedef void (*SystemCallback)(void);
extern u16 kq_system_frame;
// Reset the software frame counter and callback storage, then enable NMI.
void system_init(void);
// Wait for NMI, count the completed wait, then invoke the registered callback.
void system_wait_vblank(void);
// Replace the per-wait callback; null disables callback dispatch.
void system_set_vblank_callback(SystemCallback callback);
// Return the wrapping 16-bit count of completed waits through this wrapper.
u16 system_get_frame(void);
// Return the low byte of the software frame counter.
u8 system_get_frame8(void);
// Enable maskable CPU IRQs through the intrinsic; NMI is controlled separately.
void system_enable_interrupts(void);
// Disable maskable CPU IRQs; this does not disable the PPU's NMI source.
void system_disable_interrupts(void);
#define fc_init system_init
#define fc_wait_vblank system_wait_vblank
#define fc_set_vblank_callback system_set_vblank_callback
#define fc_frame_count system_get_frame
#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
#endifkitaqfc/lib/system.h
tilemap.h — タイルマップの矩形操作
/*
* Optional declarations for the phase 11 tilemap helper.
*/
// The map borrows row-major tile bytes; keep width*height bytes readable in the active CPU mapping.
// solid_from is a tile-ID threshold, not an independent collision bitmap.
struct NesTilemap {
unsigned char* tiles;
unsigned char width;
unsigned char height;
unsigned char solid_from;
};
// Compute a row-major byte offset by adding the row stride ty times. This
// internal arithmetic helper does not validate coordinates.
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Return a tile byte, or 0xFF when coordinates exceed map dimensions. The
// map/storage pointers must be valid; 0xFF may also be an actual stored tile.
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Treat out-of-bounds/0xFF tiles as solid, then compare other tile IDs with
// the map's solid_from threshold.
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Convert pixels to eight-pixel tile X and narrow to a byte. Keep world X
// below 2048 when wraparound is not intended.
unsigned char nes_world_to_tile8_x(unsigned short world_x);
// Convert pixels to eight-pixel tile Y with the same byte-coordinate limit.
unsigned char nes_world_to_tile8_y(unsigned short world_y);
// Convert a world point to tile coordinates and apply the tile solidity rule.
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y);
// Test only the four corners of a nonempty world box. Interior/edge tiles
// between the corners are not scanned, so large boxes can miss obstacles.
// Width/height must be nonzero and endpoint arithmetic must not overflow.
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height);kitaqfc/lib/tilemap.h
vram.h — VRAM更新の予約と転送
#ifndef VRAM_H
#define VRAM_H
#include "core.h"
#include "intrinsics.h"
// Coordinates use 32-byte nametable rows. These wrappers do not clip to the 32x30 visible tile area.
#ifndef VRAM_NT_BASE
#define VRAM_NT_BASE ((u16)0x2000)
#endif
// Clear the intrinsic queue and its overflow latch.
void vram_init(void);
// Discard queued commands and explicitly reset the overflow latch.
void vram_clear_queue(void);
// Queue one tile at the default nametable base. Return the current overflow
// status, which may include a failure from an earlier operation.
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);
// Alias the queued background-tile operation.
u8 vram_queue_tile(u8 x, u8 y, u8 tile);
// Queue one fill per row. This is not transactional: capacity exhaustion can
// leave earlier rows queued, and the loop continues attempting later rows.
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
// Queue each tightly packed source row as a pointer-based copy record. Keep
// source storage and its ROM bank accessible until queue execution finishes;
// partial queuing remains possible on overflow.
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);
// Split a byte transfer into copy records of at most 127 bytes. The runtime
// retains source pointers rather than copying payloads into the queue. Return
// the overflow latch state after all records have been attempted.
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);
// Alias the byte-copy queue helper; len remains a byte count, not a tile count.
u8 vram_queue_tiles(u16 dst, const void* src, u16 len);
// Queue fills of at most 127 bytes each. Overflow can leave a partially queued
// transfer; no rollback is performed.
u8 vram_queue_memset(u16 dst, u8 value, u16 len);
// Invoke the queue executor directly. Its ready flag must already be set by
// commit, and the caller supplies appropriate PPU timing.
void vram_flush_now(void);
// Commit queued records for consumption and wait for the NMI path.
void vram_flush(void);
// Return the encoded queue length in bytes, not the number of commands.
u8 vram_get_queue_used(void);
// Return the remaining command-buffer capacity in bytes. A command requires
// space for its complete encoded record, including its address and metadata.
// Read before committing: NMI may consume a committed queue asynchronously.
u8 vram_get_queue_free(void);
// Return the queue's total capacity in bytes, independent of its current usage.
// This describes the command buffer, not free space in PPU VRAM.
u8 vram_get_queue_capacity(void);
// Read the overflow latch without clearing it.
u8 vram_get_overflowed(void);
#endifkitaqfc/lib/vram.h
vram_queue.h — NMIで処理するVRAMキュー
PPUへの書き込み命令をCPU側に蓄え、後でまとめて実行する組み込みキューです。値1個、コピー、塗りつぶしを登録し、commitで実行可能な状態へ進めます。runtime.cのnes_vram_queue_*とは別のキューです。
copyは元データのアドレスを保存するため、消費が終わるまでデータとバンク配置を保ちます。lenは転送する画素数ではなく、命令バッファ内の使用バイト数です。overflowを確認して登録失敗を処理し、消費中にキューを書き換えないでください。
関数の使い方とサンプル: __vramq_copy, __vramq_commit, __vramq_exec, __vramq_overflow
#ifndef VRAM_QUEUE_H
#define VRAM_QUEUE_H
#include "intrinsics.h"
/* Safe frame-time VRAM queue. These names are compiler intrinsic entry points. */
// Reset pending intrinsic-queue records; this is separate from runtime.c nes_vram_queue_clear.
void __vramq_clear(void);
// Append one address/value update; use the overflow latch to detect capacity failure.
void __vramq_put(u16 ppu_addr, u8 value);
// Retain the source address for later consumption; keep its storage and bank mapping valid.
void __vramq_copy(u16 ppu_addr, const u8* src, u8 len);
// Append a repeated-byte update; the source value is stored in the record.
void __vramq_fill(u16 ppu_addr, u8 value, u8 len);
// Publish the pending records to the configured consumer. Do not mutate them while consumption can run.
void __vramq_commit(void);
// Execute a ready queue directly; committing and safe PPU timing are caller responsibilities.
void __vramq_exec(void);
// Return encoded bytes currently occupied, not transfer payload bytes or command count.
u8 __vramq_len(void);
// Read the latched capacity failure; reading does not reset it.
u8 __vramq_overflow(void);
// Clear the overflow latch explicitly after handling the failed submission.
void __vramq_clear_overflow(void);
// Return total encoded command-buffer capacity; this is independent of PPU VRAM capacity.
u8 __vramq_capacity(void);
#endifkitaqfc/lib/vram_queue.h
vrc6_sound.h — VRC6拡張音源
#ifndef VRC6_SOUND_H
#define VRC6_SOUND_H
#include "core.h"
// Retained names for raw control codes 0..3; their numeric suffixes are not measured duty percentages.
// The KUROSAKI pulse model uses (code+1)/16 for these codes. Combine with a low volume nibble.
#define VRC6_PULSE_DUTY_12 0x00
#define VRC6_PULSE_DUTY_25 0x10
#define VRC6_PULSE_DUTY_50 0x20
#define VRC6_PULSE_DUTY_75 0x30
#define VRC6_PULSE_GATE 0x80
// Clear channel-enable/high-timer registers and output controls for all VRC6 voices.
// Requires the VRC6 register layout used by this module. No mapper setup or global frequency-control write is included.
void nes_vrc6_silence_all(void);
// Write pulse-1 control and the low 12 timer bits, then enable the channel.
void nes_vrc6_pulse1_set(u8 control, u16 period);
// Write pulse-2 control and the low 12 timer bits, then enable the channel.
void nes_vrc6_pulse2_set(u8 control, u16 period);
// Disable pulse 1 by clearing its high timer/enable register.
void nes_vrc6_pulse1_off(void);
// Disable pulse 2 by clearing its high timer/enable register.
void nes_vrc6_pulse2_off(void);
// Mask the saw accumulation rate to six bits, load a 12-bit timer and enable output.
void nes_vrc6_saw_set(u8 rate, u16 period);
// Disable the saw channel by clearing its high timer/enable register.
void nes_vrc6_saw_off(void);
#endifkitaqfc/lib/vrc6_sound.h
vrc7_sound.h — VRC7 FM音源
#ifndef VRC7_SOUND_H
#define VRC7_SOUND_H
#include "core.h"
#define VRC7_KEY_ON 0x10
#define VRC7_SUSTAIN 0x20
// Write register selection followed immediately by data. No explicit device
// settling delay or cartridge detection is inserted by this wrapper.
// The caller must arrange device timing and serialize register/data pairs against interrupt writers.
void nes_vrc7_write(u8 reg, u8 value);
// Copy eight caller-supplied patch bytes into user-instrument registers 0..7.
void nes_vrc7_set_user_patch(const u8* patch8);
// Validate channel 0..5, then pack a 9-bit frequency, 3-bit block, key flags,
// instrument and volume nibbles into its registers. Volume is the raw hardware
// attenuation field, not a linear loudness value.
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags);
// Clear the channel's entire frequency-high/block/key register, not just the
// key-on bit. A later note must restore frequency and block settings.
void nes_vrc7_key_off(u8 channel);
// Key off all six voices and set instrument zero with maximum attenuation.
void nes_vrc7_silence_all(void);
#endifkitaqfc/lib/vrc7_sound.h
wire3d.h — DMG向けワイヤーフレーム
FC向けワイヤーフレーム描画の型と関数を宣言します。頂点を回転・平行移動・投影し、辺を1ビットの線画として描きます。描画範囲は64×48、96×64、128×96から選び、小さい範囲ほど描画量とCHR転送量を抑えられます。
CHR RAMとPRG RAMを備える構成が必要です。初期化が管理するパターンテーブル・ネームテーブルや作業RAMへ、別の描画処理が同時に書かないようにします。EndFrameは変更タイルを複数のVBlankに分けて転送し、完了後に画面を切り替えます。サイズ選択とビルド例は専用ガイドにあります。
関数の使い方とサンプル: Wire3DFC_Init, Wire3DFC_DrawModel, Wire3DFC_EndFrame
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
#ifndef KITAQFC_WIRE3D_H
#define KITAQFC_WIRE3D_H
#include "core.h"
#include "intrinsics.h"
// Choose a supported viewport before including wire3d.c. Smaller views reduce
// line work and dirty CHR transfers; all use original one-bit line graphics.
#ifndef WIRE3D_FC_WIDTH
#define WIRE3D_FC_WIDTH 64
#endif
#ifndef WIRE3D_FC_HEIGHT
#define WIRE3D_FC_HEIGHT 48
#endif
#if WIRE3D_FC_WIDTH == 64 && WIRE3D_FC_HEIGHT == 48
#define WIRE3D_FC_TILES 48
#elif WIRE3D_FC_WIDTH == 96 && WIRE3D_FC_HEIGHT == 64
#define WIRE3D_FC_TILES 96
#elif WIRE3D_FC_WIDTH == 128 && WIRE3D_FC_HEIGHT == 96
#define WIRE3D_FC_TILES 192
#else
#error Select 64x48, 96x64 or 128x96 for the wireframe viewport
#endif
#define WIRE3D_FC_VERTEX_LIMIT 24
typedef struct Wire3DFC_Vec3 { s8 x; s8 y; s8 z; } Wire3DFC_Vec3;
typedef struct Wire3DFC_Edge { u8 a; u8 b; } Wire3DFC_Edge;
extern u8 wire3d_transfer_frames;
extern u8 wire3d_uploaded_tiles;
// Own both CHR-RAM pattern tables and nametable zero. Requires writable 8 KiB
// CHR RAM and PRG RAM 0x6800..0x70BF; reserves ZP 0x00..0x0A for drawing/transfer.
// Build with --nes-chr-ram and provide cartridge PRG RAM for the stage.
// Center the viewport, clear video memory, select cyan-on-dark colors and turn
// on background rendering with NMI disabled. Do not run another PPU writer.
void Wire3DFC_Init(void);
// Clear the previous staged drawing. The displayed front image remains intact.
void Wire3DFC_BeginFrame(void);
// Draw a connected, inclusive segment. Endpoints must be -512..511. Pixels
// outside the viewport are clipped; completely outside bounding boxes return.
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by);
// Rotate a small model-space point in Y/X/Z order. Angles wrap in 32 steps.
// Start with components in -63..63; supply three distinct writable pointers.
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz);
// Project camera-space coordinates using an integer reciprocal table. Require
// x/y within -127..127 and z within 32..255. Return zero without touching outputs
// on invalid input; success returns one and writes unclipped screen coordinates.
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy);
// Draw when both endpoints pass the projection range. Near-plane crossings
// are rejected, not intersected with the near plane; screen pixels are clipped.
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz);
// Rotate, translate and project up to 24 vertices, then draw indexed edges.
// Invalid edge indices/depth endpoints are skipped. Model data must remain
// readable in the current PRG mapping. This draws all edges, without face culling.
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz);
// Upload changed tiles to the hidden CHR table in bounded VBlank batches and
// swap only after completion. Blocks across as many frames as needed. The two
// counters report this call's upload batches plus swap, and tile count.
void Wire3DFC_EndFrame(void);
#endifkitaqfc/lib/wire3d.h
wire3d_tables.h —
ワイヤーフレーム実装が使う32方向の正弦値、投影用の逆数、描画行のアドレス表を定義します。WIRE3D_FC_WIDTHに対応する表を選ぶため、wire3d.cを取り込む前に画面サイズを指定します。ゲームから直接呼ぶ関数や、別途リンクするモジュールではありません。
正弦値は64を基準にした整数です。投影表は深度32未満を扱わず、最大画面の最短深度では8ビットに収まるよう値を255へ制限します。表の数値だけを変更すると描画ルーチンの尺度やアドレス計算と合わなくなるため、通常は公開描画APIを使ってください。
関数の使い方とサンプル: Wire3DFC_ProjectPoint, Wire3DFC_DrawLine2D
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
// Generated from round(64*sin(2*pi*i/32)) and round(64*width/z).
// Depths below 32 are rejected. Saturating 256 to 255 at the largest
// viewport/nearest depth bounds the projection error without a ninth bit.
__prg_rom const s8 w3dfc_sin[32]={
0,12,24,36,45,53,59,63,64,63,59,53,45,36,24,12,
0,-12,-24,-36,-45,-53,-59,-63,-64,-63,-59,-53,-45,-36,-24,-12,
};
#if WIRE3D_FC_WIDTH == 64
__prg_rom const u8 w3dfc_recip[256]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
128,124,120,117,114,111,108,105,102,100,98,95,93,91,89,87,
85,84,82,80,79,77,76,74,73,72,71,69,68,67,66,65,
64,63,62,61,60,59,59,58,57,56,55,55,54,53,53,52,
51,51,50,49,49,48,48,47,47,46,46,45,45,44,44,43,
43,42,42,41,41,41,40,40,39,39,39,38,38,38,37,37,
37,36,36,36,35,35,35,34,34,34,34,33,33,33,33,32,
32,32,32,31,31,31,31,30,30,30,30,29,29,29,29,29,
28,28,28,28,28,27,27,27,27,27,27,26,26,26,26,26,
26,25,25,25,25,25,25,25,24,24,24,24,24,24,24,23,
23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,
21,21,21,21,21,21,21,21,20,20,20,20,20,20,20,20,
20,20,20,19,19,19,19,19,19,19,19,19,19,19,18,18,
18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,16,16,16,16,16,16,16,
};
__prg_rom const u8 w3dfc_row_lo[48]={
0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
128,129,130,131,132,133,134,135,192,193,194,195,196,197,198,199,
0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
};
__prg_rom const u8 w3dfc_row_hi[48]={
104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
};
__prg_rom const u8 w3dfc_tile_row[48]={
0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,
16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24,
32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40,
};
#elif WIRE3D_FC_WIDTH == 96
__prg_rom const u8 w3dfc_recip[256]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
192,186,181,176,171,166,162,158,154,150,146,143,140,137,134,131,
128,125,123,120,118,116,114,112,110,108,106,104,102,101,99,98,
96,95,93,92,90,89,88,87,85,84,83,82,81,80,79,78,
77,76,75,74,73,72,71,71,70,69,68,68,67,66,65,65,
64,63,63,62,61,61,60,60,59,59,58,57,57,56,56,55,
55,54,54,53,53,53,52,52,51,51,50,50,50,49,49,48,
48,48,47,47,47,46,46,46,45,45,45,44,44,44,43,43,
43,42,42,42,42,41,41,41,40,40,40,40,39,39,39,39,
38,38,38,38,37,37,37,37,37,36,36,36,36,36,35,35,
35,35,35,34,34,34,34,34,33,33,33,33,33,33,32,32,
32,32,32,32,31,31,31,31,31,31,30,30,30,30,30,30,
30,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,
27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,
26,25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,
};
__prg_rom const u8 w3dfc_row_lo[64]={
0,1,2,3,4,5,6,7,96,97,98,99,100,101,102,103,
192,193,194,195,196,197,198,199,32,33,34,35,36,37,38,39,
128,129,130,131,132,133,134,135,224,225,226,227,228,229,230,231,
64,65,66,67,68,69,70,71,160,161,162,163,164,165,166,167,
};
__prg_rom const u8 w3dfc_row_hi[64]={
104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,
105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,
};
__prg_rom const u8 w3dfc_tile_row[64]={
0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,
24,24,24,24,24,24,24,24,36,36,36,36,36,36,36,36,
48,48,48,48,48,48,48,48,60,60,60,60,60,60,60,60,
72,72,72,72,72,72,72,72,84,84,84,84,84,84,84,84,
};
#elif WIRE3D_FC_WIDTH == 128
__prg_rom const u8 w3dfc_recip[256]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
255,248,241,234,228,221,216,210,205,200,195,191,186,182,178,174,
171,167,164,161,158,155,152,149,146,144,141,139,137,134,132,130,
128,126,124,122,120,119,117,115,114,112,111,109,108,106,105,104,
102,101,100,99,98,96,95,94,93,92,91,90,89,88,87,86,
85,84,84,83,82,81,80,80,79,78,77,77,76,75,74,74,
73,72,72,71,71,70,69,69,68,68,67,67,66,66,65,65,
64,64,63,63,62,62,61,61,60,60,59,59,59,58,58,57,
57,56,56,56,55,55,55,54,54,54,53,53,53,52,52,52,
51,51,51,50,50,50,49,49,49,48,48,48,48,47,47,47,
47,46,46,46,46,45,45,45,45,44,44,44,44,43,43,43,
43,42,42,42,42,42,41,41,41,41,41,40,40,40,40,40,
39,39,39,39,39,38,38,38,38,38,38,37,37,37,37,37,
37,36,36,36,36,36,36,35,35,35,35,35,35,35,34,34,
34,34,34,34,34,33,33,33,33,33,33,33,33,32,32,32,
};
__prg_rom const u8 w3dfc_row_lo[96]={
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
};
__prg_rom const u8 w3dfc_row_hi[96]={
104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,
107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,
109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,
};
__prg_rom const u8 w3dfc_tile_row[96]={
0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,
32,32,32,32,32,32,32,32,48,48,48,48,48,48,48,48,
64,64,64,64,64,64,64,64,80,80,80,80,80,80,80,80,
96,96,96,96,96,96,96,96,112,112,112,112,112,112,112,112,
128,128,128,128,128,128,128,128,144,144,144,144,144,144,144,144,
160,160,160,160,160,160,160,160,176,176,176,176,176,176,176,176,
};
#endifkitaqfc/lib/wire3d_tables.h
zapper.h — 光線銃の入力窓口
光線銃の引き金と受光状態をコントローラーポートから読む組み込み関数です。末尾1・2でポートを選び、末尾なしのtrigger・lightはポート2を使います。光線銃の画面座標を直接返す機能ではありません。
rawは引き金と受光のビットを残した値を返します。triggerは0・1へ正規化し、lightはアクティブローの受光ビットを反転して「光を検出したら1」にします。照準の判定は、画面の明暗を変えるゲーム側の処理と組み合わせます。
関数の使い方とサンプル: __zapper_raw2, __zapper_trigger, __zapper_light
#ifndef ZAPPER_H
#define ZAPPER_H
#include "intrinsics.h"
// Return raw 4016 & 18, retaining trigger and active-low light bits.
u8 __zapper_raw1(void);
// Return raw 4017 & 18 for the second port.
u8 __zapper_raw2(void);
// Return port-1 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger1(void);
// Return port-2 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger2(void);
// Invert port-1 bit 3 and normalize: one means light detected.
u8 __zapper_light1(void);
// Invert port-2 bit 3 and normalize: one means light detected.
u8 __zapper_light2(void);
// Default to the port-2 normalized trigger reader.
u8 __zapper_trigger(void);
// Default to the port-2 normalized light reader.
u8 __zapper_light(void);
#endifkitaqfc/lib/zapper.h
zx0.h —
#pragma once
#include "core.h"
// 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 into caller-owned RAM, then transfer to CHR RAM or nametable memory.
// Rendering must be off. Preserve PPUCTRL; set scroll before enabling rendering.
// The workspace must hold the whole output. No palette upload or CHR ROM writes.
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size);kitaqfc/lib/zx0.h