KITAQ SERIES • 参考版
KITAQGB 库
构建游戏的工具
源码快照:2026 年 9 月 14 日 · 简体中文版:2026 年 9 月 14 日
使用原生编译器调用库
lib/ 包含编入游戏机 ROM 的 C 头文件和源文件。PC 编译器和辅助工具使用 Rust;游戏源码、目标平台库和 API 仍使用 C。提供的 SOURCE_MANIFEST.json 和 LIBRARY_MERGE.json 记录库源码的选择。
kitaqgb · Rust 原生编译器与辅助工具 · GitHub Actions
Windows、Linux、macOS ARM 和 macOS Intel 的原生构建及运行验证均成功。KITAQGB 在每个环境通过 48 项测试和 395 项辅助工具检查;KITAQFC 通过 55 项测试和 401 项检查,也验证了 Rust 1.85。PUBLIC_DISTRIBUTION.json 记录已放置程序的哈希及验证来源。公开 GitHub Actions 会独立构建和验证这些源码。
保存的参考输出用于检查 ROM 字节、诊断和辅助工具格式。以前的 C# 模拟器验证保留为对应原始源码指纹的历史记录,并不自动证明全部 Rust API、真实硬件或完整的 FDS BIOS 游戏启动。原始 PNG 转换脚本无法取得,因此依据规格重新实现,不能声称与原脚本字节一致。
完整示例程序
程序、公共头文件、提供的 ascii.c 及转换素材均已附带。m_ 开头的名称是本手册的辅助函数。请将构建命令与预期结果、验证记录一起阅读,并从各并列仓库的父目录执行命令。
显示所提供的字符集 gb_font.c
用途: 查看所提供原创字体包含哪些字母、数字和标点,以及如何绘制。
工作原理: m_init 准备显示和字体。m_text 绘制每一行;m_put 通过字符码 34 绘制双引号。
操作与预期结果: 应看到 26 个大写字母、26 个小写字母、10 个数字及 30 个标点符号。检查每个字形是否清晰,无需输入。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_font.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_font.gb --profile=dev --rst-disable --stack-bank=fixed// Display the letters, digits and punctuation from the supplied ascii.c font.
// Expected: Uppercase, lowercase, numbers and all 30 supplied punctuation glyphs
#include "gb_common.h"
// Lay out every supplied character category, yielding between rows. The final
// m_put uses character code 34 to display the double quote separately.
void main() {
m_init();
m_text(2,3,"FONT");
m_text(2,5,"ABCDEFGHIJKLMNOP"); m_wait();
m_text(2,6,"QRSTUVWXYZ"); m_wait();
m_text(2,8,"0123456789"); m_wait();
m_text(2,10,"abcdefghijklmnop"); m_wait();
m_text(2,11,"qrstuvwxyz"); m_wait();
m_text(2,13,"!#$%&'()*+,-./"); m_wait();
m_text(2,14,":;<=>?@[]^_`{}~"); m_wait();
m_put(16,13,34); m_wait();
while (1) { m_wait(); }
}第一次显示文字 gb_hello.c
用途: 学习启动 ROM、在背景绘制文字和数值的基本步骤。
工作原理: m_init 后,m_text 绘制问候语,m_number 格式化 42。最后的循环等待各帧,同时保持画面可见。
操作与预期结果: 画面显示 HELLO WORLD 和 042。前导零来自三位十进制格式。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_hello.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_hello.gb --profile=dev --rst-disable --stack-bank=fixed// Display a first message on screen.
// Expected: HELLO WORLD and 042
#include "gb_common.h"
// Initialize the display/font, write HELLO WORLD and a three-digit 042,
// then wait for frames so the result remains visible.
void main() {
m_init();
m_text(2,3,"HELLO");
m_text(2,5,"HELLO WORLD");
m_number(42);
while (1) { m_wait(); }
}使用更宽中间值的算术 gb_arithmetic.c
用途: 学习保留超出八位范围的中间结果。
工作原理: (u16)200 + 100 得到 300。程序将其除以 10,再转为 u8 显示。注意:扩大类型的强制转换发生在加法之前。
操作与预期结果: 030 表示计算结果 30。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_arithmetic.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_arithmetic.gb --profile=dev --rst-disable --stack-bank=fixed// Variables, arithmetic and type conversion.
// Expected: 030
#include "gb_common.h"
// Widen before adding 200 + 100, divide the 16-bit sum, and display 30.
// The cast demonstrates avoiding an 8-bit intermediate result.
void main() {
m_init();
m_text(2,3,"ARITHMETIC");
u16 sum;
sum = (u16)200 + 100;
m_number((u8)(sum / 10));
while (1) { m_wait(); }
}分支与循环 gb_control.c
用途: 在小型计算中尝试 for、continue、while、do/while 和 switch。
工作原理: 将 0 至 7 相加但跳过 3,得到 25。while 循环推进到 30,do/while 推进到 32,switch 选择 case 32 并赋值 42。
操作与预期结果: 042 是这些循环和分支的最终结果。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_control.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_control.gb --profile=dev --rst-disable --stack-bank=fixed// Conditional branches and loops.
// Expected: 042
#include "gb_common.h"
// Exercise loop control and a final branch, then display the selected value 42.
void main() {
m_init();
m_text(2,3,"CONTROL");
u8 i; u8 sum; sum=0;
for (i=0;i<8;i++) { if (i==3) continue; sum=(u8)(sum+i); }
while (sum<30) { sum++; }
do { sum++; } while (sum<32);
switch (sum) { case 32: sum=42; break; default: sum=0; break; }
m_number(sum);
while (1) { m_wait(); }
}函数、数组、结构体与指针 gb_aggregate.c
用途: 学习组织数值,并通过函数和指针访问。
工作原理: 程序复制含 (10,20) 的 Point,再通过指针将 1 至 4 写入数组。将函数结果 30、数组值 1、2、4 和常量 5 相加。
操作与预期结果: 总和显示为 042。请跟踪结构体复制、ptr[i] 写入和函数返回值。
![函数、数组、结构体与指针:总和显示为 042。请跟踪结构体复制、ptr[i] 写入和函数返回值。](../verification/gb_aggregate.png)
.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_aggregate.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_aggregate.gb --profile=dev --rst-disable --stack-bank=fixed// Functions, arrays, structures and pointers.
// Expected: 042
#include "gb_common.h"
typedef struct { u8 x; u8 y; } Point;
// Add two byte values and explicitly narrow the returned result to u8.
u8 add(u8 a,u8 b) { return (u8)(a+b); }
// Copy a structure, fill an array through a pointer, and combine selected values
// through add() to produce 42. Keep the final screen visible in the idle loop.
void main() {
m_init();
m_text(2,3,"AGGREGATE");
Point a; Point b; u8 i; u8 data[4]; u8* ptr;
a.x=10; a.y=20; b=a; ptr=data;
for(i=0;i<4;i++) ptr[i]=(u8)(i+1);
m_number((u8)(add(b.x,b.y)+ptr[0]+ptr[1]+ptr[3]+5));
while (1) { m_wait(); }
}位标志与缓冲区复制 gb_bits_memory.c
用途: 将标志打包到字节中,并将缓冲区复制到独立存储空间。
工作原理: __memset 清除四个字节。程序设置第 3 位、翻转第 1 位,再用 __memcpy 复制全部四字节。
操作与预期结果: 目标首字节显示为 010:第 3 位贡献 8,第 1 位贡献 2。

.\kitaqgb\kitaqgb.exe kitaq-docs/samples/gb_bits_memory.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bits_memory.gb --profile=dev --rst-disable --stack-bank=fixed// Bit operations and memory copying.
// Expected: 010
#include "gb_common.h"
void __memset(void* dst,u8 value,u16 len);
void __memcpy(void* dst,const void* src,u16 len);
void __bit_set(u8* base,u16 bit);
void __bit_toggle(u8* base,u16 bit);
// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main() {
m_init();
m_text(2,3,"BITS MEMORY");
u8 data[4]; u8 copy[4];
__memset(data,0,4); __bit_set(data,3); __bit_toggle(data,1);
__memcpy(copy,data,4); m_number(copy[0]);
while (1) { m_wait(); }
}统计新按下的按键 gb_input.c
用途: 区分新按下的按键与持续按住的按键。
工作原理: 每帧调用 input_update。只有 input_pressed(BTN_A) 为真时,计数器才递增。
操作与预期结果: 初始计数为 000。按 A 得到 001;持续按住不会增加。松开再按得到 002。字节计数器从 255 回绕到 000。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/input.c kitaq-docs/samples/gb_input.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_input.gb --profile=dev --rst-disable --stack-bank=fixed// Distinguish a new button press from a held button.
// Expected: 000 initially; A increments once per press
#include "gb_common.h"
#include "input.h"
u8 manual_count;
// Sample buttons once per frame and increment only on a new A press.
// Holding A does not repeatedly increment; the byte-sized counter wraps after 255.
void main() {
m_init();
m_text(2,3,"INPUT");
input_init();
m_number(0);
while (1) { m_wait(); input_update(); if(input_pressed(BTN_A)) { manual_count++; m_number(manual_count); } }
}Q8.8 定点乘法 gb_fixed.c
用途: 学习整数转 Q8.8、乘法以及转回整数。
工作原理: fix_from_int 将 3 和 4 转为 Q8.8,fix_mul 将它们相乘,fix_to_int 再将乘积转回整数。
操作与预期结果: 012 表示 3 × 4 = 12。这是转换后的整数,不是按 256 缩放的内部值。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaq-docs/samples/gb_fixed.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_fixed.gb --profile=dev --rst-disable --stack-bank=fixed// Calculate with signed Q8.8 fixed-point values.
// Expected: 012
#include "gb_common.h"
#include "fixed.h"
// Convert 3 and 4 to Q8.8, multiply them, convert the result back to an
// integer, and display 12.
void main() {
m_init();
m_text(2,3,"FIXED");
fix8 a; fix8 b; a=fix_from_int(3); b=fix_from_int(4);
m_number((u8)fix_to_int(fix_mul(a,b)));
while (1) { m_wait(); }
}从固定对象池分配 gb_entity.c
用途: 从固定槽位集合创建对象,无需动态分配。
工作原理: entity_init 重置对象池。entity_create 在 (20,30) 创建类型 1;程序检查失败 ID 0xFF 后,才将 vx 设为 2。
操作与预期结果: 001 是活动对象数,不是 ID 或坐标。本示例不更新或绘制对象,因此不会出现移动角色。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/entity.c kitaq-docs/samples/gb_entity.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_entity.gb --profile=dev --rst-disable --stack-bank=fixed// Allocate an entity from a fixed-capacity object pool.
// Expected: 001
#include "gb_common.h"
#include "entity.h"
// Create one entity and check the 0xFF allocation-failure sentinel before
// accessing its velocity. Display the pool occupancy, not the entity ID.
void main() {
m_init();
m_text(2,3,"ENTITY");
u8 id; entity_init(); id=entity_create(1,20,30);
if(id!=0xFF) entity_get(id)->vx=2; m_number(entity_count_active());
while (1) { m_wait(); }
}在 RAM 中记录诊断值 gb_debug.c
用途: 将有名称的游戏数值存入诊断缓冲区,与画面显示分开。
工作原理: 程序初始化调试,设置帧标签 1,并以 HP 为名记录值 42。
操作与预期结果: 001 是跟踪条目数。HP=42 保存在诊断缓冲区中;仅凭画面不能查看或验证该有效载荷。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/debug.c kitaq-docs/samples/gb_debug.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_debug.gb --profile=dev --rst-disable --stack-bank=fixed// Record diagnostic values in the debug trace.
// Expected: 001; trace log contains HP=42
#include "gb_common.h"
#include "debug.h"
// Record HP = 42 with frame tag 1 and display the number of trace entries.
// The trace buffer contains the value; the screen displays the count, not HP.
void main() {
m_init();
m_text(2,3,"DEBUG");
debug_init(); debug_set_frame(1); debug_trace_u8("HP",42);
m_number(debug_get_trace_count());
while (1) { m_wait(); }
}保存位置历史 gb_chain.c
用途: 学习用环形缓冲区保存位置轨迹。
工作原理: chain_init 关联可容纳四个点的数组,随后 chain_push_head 记录 (20,30)。
操作与预期结果: 042 是固定完成标记,不是坐标或计数。检查 trail 和 points 可确认保存的历史。本程序不绘制跟随身体。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/chain.c kitaq-docs/samples/gb_chain.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_chain.gb --profile=dev --rst-disable --stack-bank=fixed// Store a position history in a ring buffer.
// Expected: 042; history contains (20,30)
#include "gb_common.h"
#include "chain.h"
Chain trail; ChainPoint points[4];
// Attach four history slots and record one point. The displayed 42 is a fixed
// completion marker; inspect the chain to verify the stored (20,30) point.
void main() {
m_init();
m_text(2,3,"CHAIN");
chain_init(&trail,points,4); chain_push_head(&trail,20,30);
m_number(42);
while (1) { m_wait(); }
}读取库的帧计数器 gb_system.c
用途: 观察计数式帧等待如何影响库帧计数器。
工作原理: system_init 后,程序调用 system_wait_vblank 两次,再显示 system_get_frame8。
操作与预期结果: 结果为 002。空闲循环使用手册辅助函数 m_wait,因此显示值不会持续增加。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/system.c kitaq-docs/samples/gb_system.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_system.gb --profile=dev --rst-disable --stack-bank=fixed// Count completed frame waits.
// Expected: 002
#include "gb_common.h"
#include "system.h"
// Reset the library frame count, perform two counted waits, and display 2.
// Subsequent m_wait calls leave that library counter unchanged.
void main() {
m_init();
m_text(2,3,"SYSTEM");
system_init(); system_wait_vblank(); system_wait_vblank();
m_number(system_get_frame8());
while (1) { m_wait(); }
}选择当前场景 ID gb_scene.c
用途: 学习选择 ID 以区分标题画面、游戏过程等场景。
工作原理: 程序注册两个零初始化的 SceneDef 条目,通过 scene_change 选择 ID 1。
操作与预期结果: 001 是 scene_get_current 返回的当前 ID。本示例未注册场景回调或过渡绘制。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scene.c kitaq-docs/samples/gb_scene.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scene.gb --profile=dev --rst-disable --stack-bank=fixed// Switch the current scene ID.
// Expected: 001; no callbacks registered
#include "gb_common.h"
#include "scene.h"
SceneDef manual_scenes[2];
// Register two zero-initialized scene entries, select ID 1 and display it.
// No callbacks are registered in this example.
void main() {
m_init();
m_text(2,3,"SCENE");
scene_init(manual_scenes,2); scene_change(1);
m_number(scene_get_current());
while (1) { m_wait(); }
}将世界坐标转换为屏幕坐标 gb_camera.c
用途: 减去摄像机位置,求世界坐标对应的屏幕位置。
工作原理: Camera_Set 接收 Q8.8 值 2560,即 X=10 像素。随后 Camera_WorldToScreenX 转换世界 X=52。
操作与预期结果: 052 减去 010 得到显示的 042。这是坐标计算,不会滚动背景。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaqgb/lib/camera.c kitaq-docs/samples/gb_camera.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_camera.gb --profile=dev --rst-disable --stack-bank=fixed// Convert world coordinates to screen coordinates.
// Expected: 042: world x=52 minus camera x=10
#include "gb_common.h"
#include "camera.h"
Camera8_8 cam;
// Set the camera X coordinate to 10 pixels in Q8.8 (10 * 256), then subtract
// it from world X = 52 to display screen X = 42.
void main() {
m_init();
m_text(2,3,"CAMERA");
Camera_Init(&cam); Camera_Set(&cam,2560,0);
m_number((u8)Camera_WorldToScreenX(&cam,52));
while (1) { m_wait(); }
}将 RLE 解码到 RAM gb_rle.c
用途: 尝试用紧凑的计数/数值形式表示重复字节。
工作原理: 打包字节 {3,42,0} 表示三个 42,后接计数为零的结束标记。rle_decode 写入三字节目标空间。
操作与预期结果: 最后一个字节 out[2] 显示为 042。三个解码字节预期都为 42。
![将 RLE 解码到 RAM:最后一个字节 out[2] 显示为 042。三个解码字节预期都为 42。](../verification/gb_rle.png)
.\kitaqgb\kitaqgb.exe kitaqgb/lib/rle.c kitaq-docs/samples/gb_rle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rle.gb --profile=dev --rst-disable --stack-bank=fixed// Decode run-length encoded data.
// Expected: 042; three bytes each equal to 42
#include "gb_common.h"
#include "rpg.h"
__prg_rom u8 packed[] = {3,42,0};
// Decode count/value pair (3,42), terminated by a zero count, into a
// three-byte buffer and display the final decoded byte.
void main() {
m_init();
m_text(2,3,"RLE");
u8 out[3]; rle_decode(out,packed); m_number(out[2]);
while (1) { m_wait(); }
}读写棋盘格子 gb_board.c
用途: 学习棋盘游戏中网格坐标与存储位置的对应关系。
工作原理: 16 字节数组保存 4 × 4 棋盘。程序先清空,在格子 (2,1) 写入 42,再读取同一格。
操作与预期结果: 042 是读回值。程序不绘制 4 × 4 棋盘本身。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/slg_board.c kitaq-docs/samples/gb_board.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_board.gb --profile=dev --rst-disable --stack-bank=fixed// Read and write cells in a game board.
// Expected: 042
#include "gb_common.h"
#include "slg.h"
SLGBoard board; u8 cells[16];
// Create a cleared 4-by-4 board in caller-owned storage, write 42 at (2,1),
// and display the value read back from the same cell.
void main() {
m_init();
m_text(2,3,"BOARD");
slg_board_init(&board,4,4,cells); slg_board_clear(&board,0);
slg_board_set(&board,2,1,42); m_number(slg_board_get(&board,2,1));
while (1) { m_wait(); }
}将方框推进一个物理步长 gb_physics.c
用途: 初始化世界和方框物体,再根据速度更新位置。
工作原理: 物体起始于 (40,40),速度 (2,0),重力为零。程序调用 kq2d_step 一次。
操作与预期结果: 042 是得到的 X 坐标,本示例以整数像素为单位。不绘制方框形状或连续动画。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaqgb/lib/physics2d.c kitaq-docs/samples/gb_physics.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_physics.gb --profile=dev --rst-disable --stack-bank=fixed// Advance a rectangular body through one physics step.
// Expected: 042: integer pixel coordinates
#include "gb_common.h"
#include "physics2d.h"
KQWorld2D world; KQBody2D boxes[1];
// Create one box at X = 40, set horizontal velocity 2 and zero gravity,
// then display its X coordinate after a single physics step.
void main() {
m_init();
m_text(2,3,"PHYSICS");
kq2d_world_init(&world,boxes,1);
kq2d_body_init(&boxes[0],40,40,4,4);
kq2d_body_set_velocity(&boxes[0],2,0);
world.gravity_x=0; world.gravity_y=0;
kq2d_step(&world); m_number((u8)boxes[0].x);
while (1) { m_wait(); }
}处理串行完成与超时 gb_link.c
用途: 区分已完成的传送与等待上限内未完成的传送。
工作原理: 主机发送 42,再用 Link_WaitByte 等待。完成时显示接收字节;超时则取消传送并显示零。
操作与预期结果: 未连接的传送若完成,可能返回 255。超时显示 000;连接对端时,显示收到的字节。结果不一定为 042。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/link_hwregs_gb.c kitaqgb/lib/link.c kitaq-docs/samples/gb_link.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_link.gb --profile=dev --rst-disable --stack-bank=fixed// Handle serial transfers that do not deliver a byte in time.
// Expected: 255 for disconnected link; 000 if timeout; peer value when connected
#include "gb_common.h"
#include "link.h"
// Start a master transfer of byte 42 and wait with a bounded timeout.
// Display the received byte on completion; cancel and display zero on timeout.
void main() {
m_init();
m_text(2,3,"LINK");
u8 value; Link_InitMaster(); Link_BeginTransfer(42);
if(Link_WaitByte(4,&value)) m_number(value);
else { Link_Cancel(); m_number(0); }
while (1) { m_wait(); }
}带存储体参数读取 ROM 数据 gb_bank.c
用途: 学习将 ROM 数据复制到 RAM 的参数。
工作原理: far_data_read 使用存储体参数 0、长度 2,将两个字节 7 和 42 复制到 copied。
操作与预期结果: 目标第二字节显示为 042。本示例读取固定 ROM,不演示不同可切换存储体之间的传送。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/bank.c kitaq-docs/samples/gb_bank.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bank.gb --profile=dev --rst-disable --stack-bank=fixed// Read data using an explicit ROM bank.
// Expected: 042; explicit bank argument 0
#include "gb_common.h"
#include "bank.h"
__prg_rom u8 values[] = {7,42}; u8 copied[2];
// Copy two ROM bytes using bank argument zero and display the second byte.
// This introduces the API; it does not test switching between distinct payload banks.
void main() {
m_init();
m_text(2,3,"BANK");
far_data_read(0,values,copied,2); m_number(copied[1]);
while (1) { m_wait(); }
}按资源 ID 加载数据 gb_asset.c
用途: 注册资源位置和长度,再按 ID 取回字节。
工作原理: 资源 0 的类型为 RAW,存储体为 0,指向 {7,42},长度为 2。asset_load_raw 将其复制到两字节 RAM 缓冲区。
操作与预期结果: 第二个字节显示为 042。这演示资源查找和复制,不是图像渲染。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/asset.c kitaq-docs/samples/gb_asset.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_asset.gb --profile=dev --rst-disable --stack-bank=fixed// Load asset bytes into RAM by asset ID.
// Expected: 042
#include "gb_common.h"
#include "asset.h"
AssetDesc table[1]; __prg_rom u8 values[] = {7,42}; u8 copied[2];
// Register a two-byte raw asset, copy it into RAM and display its second byte.
// This example uses bank zero and a destination sized for the complete payload.
void main() {
m_init();
m_text(2,3,"ASSET");
table[0].type=ASSET_TYPE_RAW; table[0].bank=0;
table[0].ptr=values; table[0].len=2;
asset_set_table(0,table,1); asset_load_raw(0,copied,2);
m_number(copied[1]);
while (1) { m_wait(); }
}分配一颗子弹 gb_danmaku.c
用途: 初始化子弹池,并查看生成后的占用数量。
工作原理: danmaku_reset 后,程序调用 danmaku_spawn(40,40,16,0),并显示 dm_count。
操作与预期结果: 001 是已分配子弹数。这里没有子弹更新循环或背景合成器,因此不绘制移动子弹。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/danmaku.c kitaq-docs/samples/gb_danmaku.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_danmaku.gb --profile=dev --rst-disable --stack-bank=fixed// Allocate a bullet and inspect the pool count.
// Expected: 001; numeric pool example, no bullet BG compositor
#include "gb_common.h"
#include "danmaku.h"
// Reset the bullet pool, spawn one bullet and display the active count.
// This numeric example does not install the background bullet compositor.
void main() {
m_init();
m_text(2,3,"DANMAKU");
danmaku_reset(); danmaku_spawn(40,40,16,0);
m_number(dm_count);
while (1) { m_wait(); }
}设置 CGB 背景调色板 gb_cgb_palette.c
用途: 使用 Game Boy Color 的 RGB 分量选择背景和文字颜色。
工作原理: 程序在 VBlank 期间关闭 LCD,将调色板 0 的颜色 0 设为白色 (31,31,31),颜色 3 设为紫色 (12,0,22),然后开启显示。
操作与预期结果: 在 CGB 模式运行,可看到白底紫色文字和 042。DMG 单色输出无法展示这些颜色。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/cgb_palette.c kitaq-docs/samples/gb_cgb_palette.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_cgb_palette.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb// Set Game Boy Color palette entries.
// Expected: 042 in purple on white; CGB mode
#include "gb_common.h"
#include "cgb_palette.h"
// Set palette-zero background colors to white and purple, then display 42.
// Run the ROM in CGB mode to observe the color result.
void main() {
m_init();
m_text(2,3,"CGB PALETTE");
// Disable the LCD during VBlank so both palette bytes remain accessible.
m_wait(); M_LCDC = 0;
cgb_bg_rgb(0,0,31,31,31); cgb_bg_rgb(0,3,12,0,22);
M_LCDC = 0x91;
m_number(42);
while (1) { m_wait(); }
}水平滚动背景 gb_scroll.c
用途: 改变视点位置,无需重绘背景内容。
工作原理: 每帧递增 phase,并传给 Scroll_SetBg。八位位置在 255 之后回绕。
操作与预期结果: SCROLL 一词向左移动。请观察随时间变化的位置;单张截图不能展示移动。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaq-docs/samples/gb_scroll.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scroll.gb --profile=dev --rst-disable --stack-bank=fixed// Scroll the background horizontally.
// Expected: title moves left as SCX increases
#include "gb_common.h"
#include "scroll.h"
u8 phase;
// Advance an 8-bit horizontal scroll position once per frame, wrapping naturally
// after 255. Background motion demonstrates the changing scroll register.
void main() {
m_init();
m_text(2,3,"SCROLL");
Scroll_SetBg(0,0);
while (1) { m_wait(); phase++; Scroll_SetBg(phase,0); }
}将图块更新加入队列 gb_vram_queue.c
用途: 将绘图请求与实际应用它的 VRAM 传送分离。
工作原理: vram_queue_bg_tile 将图块坐标 (3,8) 处的图块 52 加入队列,vram_flush 执行传送。所提供字体中,图块 52 是数字 4。
操作与预期结果: 应在背景图块坐标 (3,8) 看到一个 4。这是图块更新,不是数值计数器。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/vram.c kitaq-docs/samples/gb_vram_queue.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_vram_queue.gb --profile=dev --rst-disable --stack-bank=fixed// Queue a VRAM update and flush it at VBlank.
// Expected: 4 at (3,8)
#include "gb_common.h"
#include "vram.h"
// Queue font tile 52 (the digit 4) at tile coordinate (3,8), then wait for
// VBlank and execute the queued transfer.
void main() {
m_init();
m_text(2,3,"VRAM QUEUE");
vram_init();
vram_queue_bg_tile(3,8,52); vram_flush();
while (1) { m_wait(); }
}通过 OAM DMA 显示精灵 gb_sprite.c
用途: 分配并显示一个独立于背景的精灵。
工作原理: 新初始化的对象池提供槽位 0。程序选择图块 65,设置位置 (70,80),启用 OBJ 显示,并每帧传送影子 OAM。
操作与预期结果: 指定精灵位置出现静止字母 A。游戏中应在使用返回 ID 前检查分配失败。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/sprite.c kitaq-docs/samples/gb_sprite.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sprite.gb --profile=dev --rst-disable --stack-bank=fixed// Display a sprite through the OAM DMA path.
// Expected: letter A at sprite position
#include "gb_common.h"
#include "sprite.h"
// Allocate the first sprite, choose font tile 65 (A), set its position and
// enable sprite display. Flush shadow OAM after each frame wait.
void main() {
m_init();
m_text(2,3,"SPRITE");
// The freshly reset pool guarantees slot zero for this first allocation.
// In a running game, check the allocation-failure sentinel before using the returned ID.
sprite_init(); sprite_set_tile(sprite_alloc(),65);
sprite_set_pos(0,70,80); M_LCDC=(u8)(M_LCDC|2);
while (1) { m_wait(); sprite_flush_oam_now(); }
}生成可复现的随机值 gb_rng.c
用途: 通过种子复现伪随机序列。
工作原理: rng_seed(1234) 初始化生成器,再调用一次 rng_range(10)。结果不包含上限 10。
操作与预期结果: 画面显示 000 至 009 中的一个值。以相同种子重新启动同一程序会复现该值;它不会每帧变化。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/rng.c kitaq-docs/samples/gb_rng.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rng.gb --profile=dev --rst-disable --stack-bank=fixed// Generate a repeatable pseudorandom value from a seed.
// Expected: value 000 through 009; same seed repeats
#include "gb_common.h"
#include "rpg.h"
// Seed the generator with 1234 and display one value in [0,10).
// Restarting with the same seed reproduces the sequence.
void main() {
m_init();
m_text(2,3,"RNG");
rng_seed(1234); m_number(rng_range(10));
while (1) { m_wait(); }
}保存游戏状态标志 gb_flags.c
用途: 用标志编号表示事件是否完成等布尔状态。
工作原理: 程序清除标志 12,再设置并通过 flag_get 读回。
操作与预期结果: 001 表示标志 12 为真。本示例操作 RAM 标志,不会在断电后保留。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/flags.c kitaq-docs/samples/gb_flags.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_flags.gb --profile=dev --rst-disable --stack-bank=fixed// Read and write RAM-backed game-state flags.
// Expected: 001
#include "gb_common.h"
#include "rpg.h"
// Clear and set flag 12, then display its Boolean value. This does not write
// a save file or demonstrate persistence across power cycles.
void main() {
m_init();
m_text(2,3,"FLAGS");
flag_clear(12); flag_set(12); m_number(flag_get(12));
while (1) { m_wait(); }
}将圆形推进一个物理步长 gb_circle.c
用途: 设置圆形物体,并根据速度更新位置。
工作原理: 活动圆形半径为 4,位置 (40,40),速度 (2,0)。重力为零,linear_damping_q8 为 255;kq2dc_step 执行一次。
操作与预期结果: 042 是得到的整数 X 坐标。本示例不绘制圆形轮廓或碰撞动画。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/physics2d_circle.c kitaq-docs/samples/gb_circle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_circle.gb --profile=dev --rst-disable --stack-bank=fixed// Advance a circular body through one physics step.
// Expected: 042 after one step; this example uses integer pixels
#include "gb_common.h"
#include "physics2d_circle.h"
KQCircleBody2D balls[1]; KQCircleWorld2D world;
// Activate one circle at X = 40 with horizontal velocity 2 and zero gravity.
// Advance one step and display its resulting integer-pixel X coordinate.
void main() {
m_init();
m_text(2,3,"CIRCLE");
kq2dc_world_init(&world,balls,1);
balls[0].active=1; balls[0].x=40; balls[0].y=40;
balls[0].radius=4; balls[0].inv_mass_q8=256;
balls[0].vx=2; balls[0].vy=0;
world.gravity_x=0; world.gravity_y=0; world.linear_damping_q8=255;
kq2dc_step(&world); m_number((u8)balls[0].x);
while (1) { m_wait(); }
}每帧更新音效 gb_sound.c
用途: 启动音效流,并通过定期帧更新推进播放。
工作原理: Audio_Init 后,Audio_PlaySFX 在通道 ID 1 上启动 tone。主循环每帧调用一次 Audio_Update。
操作与预期结果: 播放短脉冲音效,同时保持显示 042。该数值是固定标记,不是音量或播放位置。请通过音频输出或录音判断声音。

.\kitaqgb\kitaqgb.exe kitaqgb/lib/audio_hwregs_gb.c kitaqgb/lib/audio.c kitaq-docs/samples/gb_sound.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sound.gb --profile=dev --rst-disable --stack-bank=fixed// Play a Game Boy pulse-channel sound effect.
// Expected: 042 and a short pulse tone
#include "gb_common.h"
#include "audio.h"
__prg_rom u8 tone[] = { 24,0xF0,24,0xE0,24,0xC0,24,0xA0,24,0x80,24,0x60,0 };
// Start a pulse-channel effect and service Audio_Update once per frame
// so the effect stream advances while 42 remains on screen.
void main() {
m_init();
m_text(2,3,"SOUND");
Audio_Init(); Audio_PlaySFX(tone,1); m_number(42);
while (1) { m_wait(); Audio_Update(); }
}库 API 字典
共 554 个条目。展开可查看语法、参数、源码说明和示例。调用片段依赖外围初始化,不能单独构建 ROM。参数传递示例只说明如何把准备好的值交给 API,不代表已完成设备级验证。
zx0
将 ZX0 兼容素材解压到 RAM 或 VRAM。先在 PC 上用 kitaqgb-zx0.exe 准备素材,运行时仅解压当前需要的数据。--format=auto 比较原始、RLE 和 ZX0 载荷,选择最小者;大小相同时依次优先原始、RLE、ZX0。包含九字节头部的整个容器不得超过 65535 字节,还须按实际 CPU 存储体窗口和 RAM 容量拆分素材。
ZX0 格式及原始压缩算法由 Einar Saukas 设计。这里提供的压缩器和解压器是 KITAQ 的独立实现,版权归 DAISUKE OBA 所有,采用 MIT 许可证。
请在仓库根目录运行以下主机命令。先用 tools/zx0/build.ps1 构建工具。
.\kitaqgb-zx0.exe tiles.bin tiles.zx0
.\kitaqgb-zx0.exe tiles.bin tiles.h --header=tiles
.\kitaqgb-zx0.exe map.bin map.kqa --format=auto
.\kitaqgb-zx0.exe tiles.zx0 restored.bin --decompresszx0_decompress
将 ZX0 v2 数据流解压到有容量限制的 RAM。匹配引用先前已解码的数据,因此目标必须同时可读、可写。适合 CPU 使用前还需处理的背景图或关卡表。
语法
u16 zx0_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);参数
dst可写目标 RAM 的首地址,不得为空指针。容量按字节指定,不是数组元素数。
capacity目标可写字节数。普通 ZX0 流至少需要一个字节;空的原始格式 KQA1 素材允许容量为零。
src在当前存储体映射下可读的压缩数据首地址,不得为空指针。
packed_size完整输入的字节数。ZX0 流须包含结束标记,后面不能再有多余字节;KQA1 容器长度包括九字节头部。最大为 65535 字节。
返回值
成功时返回解压字节数,错误时返回零。必须同时检查 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 用十六个方块组成水平条。VRAM TILES 将像素数据解压到 VRAM 后重复显示相同的三个图块。CGB 和 FC 上方块为白色、菱形为红色、棋盘格为蓝色;DMG 使用灰阶。CHECKS OK 表示三个调用都返回了预期长度且没有错误。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/zx0.h:15
Return decoded bytes, or zero on error (possibly after partial writes). Source and output must not overlap or cross their current CPU bank windows. Capacity and packed_size are byte counts. No prefix/backward/v1 streams.
kitaqgb/lib/zx0.c:52
u16 zx0_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
u8 phase; u8 low; u8* out; u8* match;
u16 count; u16 distance; u16 used; u16 upper;
zx0_error=ZX0_OK;
if(!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
kq_zx_input=(const u8*)src; kq_zx_left=packed_size;
kq_zx_mask=0; kq_zx_borrowed=0;
out=(u8*)dst; used=0; distance=1; phase=0;
// Phase 0 is literal, 1 reuses the last distance, 2 carries a new distance.
while(1) {
if(phase==2) {
upper=kq_zx_number(1);
if(zx0_error) return 0;
if(upper==256) {
if(kq_zx_left!=0) { zx0_error=ZX0_BAD_STREAM; return 0; }
return used;
}
if(upper>255) { zx0_error=ZX0_BAD_STREAM; return 0; }
low=kq_zx_byte();
distance=(u16)((upper<<7)-(low>>1));
kq_zx_borrowed=1; kq_zx_borrow_value=(u8)(low&1);
count=kq_zx_number(0);
if(count==65535) { zx0_error=ZX0_BAD_STREAM; return 0; }
count++;
} else count=kq_zx_number(0);
if(zx0_error) return 0;
if(count>capacity-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
if(phase==0) {
if(count>kq_zx_left) { zx0_error=ZX0_TRUNCATED; return 0; }
kq_zx_left-=count; used+=count;
while(count!=0) { *out++=*kq_zx_input++; count--; }
phase=kq_zx_bit()?2:1;
} else {
if(distance>used) { zx0_error=ZX0_BAD_STREAM; return 0; }
match=out-distance; used+=count;
// Forward byte copying deliberately supports overlapping matches.
while(count!=0) { *out++=*match++; count--; }
phase=kq_zx_bit()?2:0;
}
if(zx0_error) return 0;
}
}asset_decompress
从 --format=auto 生成的 KQA1 头部读取编码类型和长度,再复制原始字节或将 RLE/ZX0 解压到 RAM。调用方无需为各编码类型分别编写分支。
语法
u16 asset_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);参数
dst可写目标 RAM 的首地址,不得为空指针。容量按字节指定,不是数组元素数。
capacity目标可写字节数。普通 ZX0 流至少需要一个字节;空的原始格式 KQA1 素材允许容量为零。
src在当前存储体映射下可读的压缩数据首地址,不得为空指针。
packed_size完整输入的字节数。ZX0 流须包含结束标记,后面不能再有多余字节;KQA1 容器长度包括九字节头部。最大为 65535 字节。
返回值
成功时返回解压字节数,错误时返回零。必须同时检查 zx0_error。
使用条件与限制
包含 zx0.h 并编译 lib/zx0.c。这些函数使用共享临时区,不允许从中断重入。输入和输出不得重叠、越过 CPU 地址空间末端或跨出当前映射的存储体窗口。库不会切换素材所在存储体。
每次调用都会更新 zx0_error:0 成功,1 参数无效,2 输入被截断,3 数据流格式错误,4 输出空间不足,5 显示处于开启状态。失败时可能已写入部分结果,不要使用这些数据。若同时存在多个错误,以首先检测到的条件决定错误码。
KQA1 是 KITAQ 素材容器:四字节标记、一个字节的编码类型(0 原始、1 RLE、2 ZX0)、u16 解压长度、u16 载荷长度,之后为载荷。u16 使用小端序。RLE 以数量/数值对重复数据,数量为 1~255,以数量零结束。空原始素材成功时返回零,错误码也为零。
使用示例
count=asset_decompress(decoded_auto,16,packed_auto,packed_auto_SIZE);
if(count!=16||zx0_error!=ZX0_OK)failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
RAM MAP 一排重复显示方块、菱形和棋盘格图块。AUTO RLE MAP 用十六个方块组成水平条。VRAM TILES 将像素数据解压到 VRAM 后重复显示相同的三个图块。CGB 和 FC 上方块为白色、菱形为红色、棋盘格为蓝色;DMG 使用灰阶。CHECKS OK 表示三个调用都返回了预期长度且没有错误。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/zx0.h:18
Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto. Empty raw assets return zero with zx0_error==ZX0_OK.
kitaqgb/lib/zx0.c:95
u16 asset_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
const u8* in; u8* out; u16 raw; u16 packed; u16 used;
u8 codec; u8 count; u8 value;
zx0_error=ZX0_OK;
if(packed_size<9||!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
in=(const u8*)src; out=(u8*)dst;
if(in[0]!=75||in[1]!=81||in[2]!=65||in[3]!=49) { zx0_error=ZX0_BAD_STREAM; return 0; }
codec=in[4]; raw=(u16)((u16)in[5]|((u16)in[6]<<8));
packed=(u16)((u16)in[7]|((u16)in[8]<<8));
if(packed!=packed_size-9||codec>2) { zx0_error=ZX0_BAD_STREAM; return 0; }
if(raw>capacity) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
in+=9;
if(codec==2) {
used=zx0_decompress(dst,raw,in,packed);
if(zx0_error==0&&used!=raw) zx0_error=ZX0_BAD_STREAM;
return zx0_error?0:used;
}
if(codec==0) {
if(raw!=packed) { zx0_error=ZX0_BAD_STREAM; return 0; }
used=raw; while(raw!=0) { *out++=*in++; raw--; } return used;
}
used=0;
while(packed!=0) {
count=*in++; packed--;
if(count==0) {
if(packed!=0||used!=raw) { zx0_error=ZX0_BAD_STREAM; return 0; }
return used;
}
if(packed==0) { zx0_error=ZX0_TRUNCATED; return 0; }
value=*in++; packed--;
if((u16)count>raw-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
used+=count; while(count!=0) { *out++=value; count--; }
}
zx0_error=ZX0_TRUNCATED; return 0;
}zx0_decompress_vram
LCD 关闭时,将 ZX0 图块像素或背景图数据直接解压到当前选择的 VRAM 存储体。VRAM 本身保存匹配历史,因此无需中间 RAM 缓冲。
语法
u16 zx0_decompress_vram(u16 vram_addr, const void* src, u16 packed_size);参数
vram_addrVRAM 起始地址 0x8000~0x9FFF。输出限制在 VRAM 末端 0xA000 之前。CGB 应在调用前选择 VBK。
src在当前存储体映射下可读的压缩数据首地址,不得为空指针。
packed_size完整输入的字节数。ZX0 流须包含结束标记,后面不能再有多余字节;KQA1 容器长度包括九字节头部。最大为 65535 字节。
返回值
成功时返回解压字节数,错误时返回零。必须同时检查 zx0_error。
使用条件与限制
包含 zx0.h 并编译 lib/zx0.c。这些函数使用共享临时区,不允许从中断重入。输入和输出不得重叠、越过 CPU 地址空间末端或跨出当前映射的存储体窗口。库不会切换素材所在存储体。
每次调用都会更新 zx0_error:0 成功,1 参数无效,2 输入被截断,3 数据流格式错误,4 输出空间不足,5 显示处于开启状态。失败时可能已写入部分结果,不要使用这些数据。若同时存在多个错误,以首先检测到的条件决定错误码。
接受正向 ZX0 v2 数据流。此接口不支持 ZX0 v1、反向数据流或外部字典。应掌握 PC 端生成素材的解压长度,并提供足够空间。
LCD 开启时返回错误 5,仅处于 VBlank 并不满足要求。函数不改变显示、VBK 或中断设置,LCD 的关闭与重新开启由调用方负责。
使用示例
count=zx0_decompress_vram(0x8800,packed_tiles,packed_tiles_SIZE);
if(count!=48||zx0_error!=ZX0_OK)failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
RAM MAP 一排重复显示方块、菱形和棋盘格图块。AUTO RLE MAP 用十六个方块组成水平条。VRAM TILES 将像素数据解压到 VRAM 后重复显示相同的三个图块。CGB 和 FC 上方块为白色、菱形为红色、棋盘格为蓝色;DMG 使用灰阶。CHECKS OK 表示三个调用都返回了预期长度且没有错误。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\zx0_decode.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/zx0.h:21
Decode directly into the selected GB VRAM bank ($8000..$9FFF), with LCD off. Preserve LCDC, VBK and interrupt state. Output must fit before $A000.
kitaqgb/lib/zx0.c:132
u16 zx0_decompress_vram(u16 vram_addr,const void* src,u16 packed_size) {
zx0_error=ZX0_OK;
if(vram_addr<0x8000||vram_addr>=0xA000) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
if(kq_zx_lcdc&0x80) { zx0_error=ZX0_DISPLAY_ACTIVE; return 0; }
return zx0_decompress((void*)vram_addr,(u16)(0xA000-vram_addr),src,packed_size);
}asset.h — 素材 ID 与数据描述符
素材库通过8位ID查找ROM存储体中的素材。请包含asset.c;FC版还需要bank.c。紧凑排列的AssetDesc占6字节,依次保存1字节类型、1字节素材存储体编号、16位地址和以字节计的长度。描述符表与素材数据可以位于不同的存储体。
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查ID和asset_get的输出指针,但不会验证已登记的表、素材地址或卡带内容是否正确。
查找RAW或TILES素材,用__vram_copy传送全部desc.len字节。调用前必须选中素材数据所在的ROM存储体;GB版直接使用desc.ptr,传送时不会选择desc.bank。
asset_get
从已登记表所在的ROM存储体读取一个AssetDesc,复制到调用方提供的空间。获取的是素材信息,不是素材数据本身。
语法
u8 asset_get(u8 asset_id, AssetDesc* out_desc);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。out_desc可写入一个6字节描述符的空间。若为null,在访问表之前就返回失败。
返回值
复制描述符后返回1;输出指针为null或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与null输出必须失败;查找失败时保留输出空间中的原值。
预期图像中,红色一行从像素坐标(16,32)开始,占72×8像素。依次重复8×8三角形、边框厚1像素的8×8空心正方形、填满左侧4列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:24
Copy one descriptor from its ROM bank into caller storage. Return zero for a null destination or an out-of-range ID; table validity is the caller's responsibility.
kitaqgb/lib/asset.c:18
u8 asset_get(u8 asset_id, AssetDesc* out_desc)
{
if (out_desc == 0) return 0;
if (asset_id >= asset_table_count) return 0;
__farmemcpy(out_desc, asset_table_bank, asset_table + asset_id, sizeof(AssetDesc));
return 1;
}asset_get_bank
从描述符读取素材数据的存储体编号。这不是描述符表所在的存储体编号,函数返回后也不会保持选中素材存储体。
语法
u8 asset_get_bank(u8 asset_id);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。
返回值
返回描述符的8位bank;查找失败则为0。有效存储体也可能为0,需要区分成败时请使用asset_get()。
使用条件与限制
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查ID和asset_get的输出指针,但不会验证已登记的表、素材地址或卡带内容是否正确。
每个获取函数都会再次调用asset_get并复制描述符。若同时需要存储体、指针和长度,请只调用一次asset_get后读取各字段。
示例将调用代码放在固定存储体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列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:32
Return the descriptor bank, or zero on lookup failure; zero can also be a valid bank.
kitaqgb/lib/asset.c:51
u8 asset_get_bank(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.bank;
}asset_get_len
从描述符读取以字节计的素材长度。返回的是登记信息;函数不检查素材本身,也不换算为图块数、元素数或帧数。
语法
u16 asset_get_len(u8 asset_id);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。
返回值
返回以字节计的16位len;查找失败为0。空素材的长度也是0,区分两者请使用asset_get()。
使用条件与限制
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查ID和asset_get的输出指针,但不会验证已登记的表、素材地址或卡带内容是否正确。
每个获取函数都会再次调用asset_get并复制描述符。若同时需要存储体、指针和长度,请只调用一次asset_get后读取各字段。
示例将调用代码放在固定存储体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个图块的素材应返回144字节,而不是图块数9。空素材与无效ID均应返回0。
预期图像中,红色一行从像素坐标(16,32)开始,占72×8像素。依次重复8×8三角形、边框厚1像素的8×8空心正方形、填满左侧4列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:37
Return the descriptor byte length, or zero if its ID cannot be resolved.
kitaqgb/lib/asset.c:68
u16 asset_get_len(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.len;
}asset_get_ptr
读取描述符中保存的16位素材地址。返回的近指针不含存储体编号。读取其他ROM存储体中的素材时,应搭配asset_get_bank()的结果和跨存储体复制函数使用。
语法
const u8* asset_get_ptr(u8 asset_id);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。
返回值
返回保存的const u8*;查找失败时为null。函数不检查描述符中的素材指针是否为null,区分查找成败请使用asset_get()。
使用条件与限制
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查ID和asset_get的输出指针,但不会验证已登记的表、素材地址或卡带内容是否正确。
每个获取函数都会再次调用asset_get并复制描述符。若同时需要存储体、指针和长度,请只调用一次asset_get后读取各字段。
示例将调用代码放在固定存储体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列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:35
Return the stored address without switching banks. Use the descriptor bank with far-data helpers before dereferencing banked assets; failure returns null.
kitaqgb/lib/asset.c:60
const u8* asset_get_ptr(u8 asset_id)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
return desc.ptr;
}asset_load_raw
查找素材,从其ROM存储体向可写内存复制min(desc.len, max_len)字节。不检查类型标记;图块、地图和乐曲均按原始字节复制,不解析格式,也不解压。
语法
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。dst目标可写内存,容量必须能容纳实际复制的全部字节。除零字节传送外,不会检查null或无效目标地址。
max_len最多复制的字节数,范围0–65535。小于素材长度时只复制前一部分;为0时不复制素材字节。
返回值
查找失败返回0,否则返回1,包括截断复制、上限为0和空素材。返回值不是字节数,也不表示整个素材均已复制。
使用条件与限制
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查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列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:27
Copy at most max_len bytes using the asset's bank. Success means the ID was valid, even when the payload was truncated to fit the supplied limit.
kitaqgb/lib/asset.c:28
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len)
{
AssetDesc desc;
u16 len;
if (asset_get(asset_id, &desc) == 0) return 0;
len = desc.len;
if (len > max_len) len = max_len;
__farmemcpy(dst, desc.bank, desc.ptr, len);
return 1;
}asset_load_tiles
查找RAW或TILES素材,用__vram_copy传送全部desc.len字节。调用前必须选中素材数据所在的ROM存储体;GB版直接使用desc.ptr,传送时不会选择desc.bank。
语法
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);参数
asset_id从0开始的素材索引;只有小于已登记表的
count的asset_id才有效。vram_dst$8000–$9FFF范围内的VRAM字节地址,不是图块编号。图块图案应使用16字节对齐的地址,描述符长度应为16的倍数;库不会检查这些格式要求。
返回值
ID无效或类型不是RAW/TILES时返回0;接受并完成传送后返回1,长度为0也一样。成功值不代表已验证图块编码或VRAM显示正确。
使用条件与限制
查找前请先登记表。登记信息是共享全局状态,不要在查找过程中同时修改。库会检查ID和asset_get的输出指针,但不会验证已登记的表、素材地址或卡带内容是否正确。
示例将调用代码放在固定存储体0中,防止切换源存储体后正在执行的代码不可见。传送为同步操作。不要在传送中通过中断再次执行切换存储体的复制,也不要认为此辅助函数能保证整个程序的中断安全。
上传只写入字节,不会在地图上放置图块、设置颜色或分配VRAM。图块地图与调色板需要另行设置。示例在关闭渲染时上传,准备好图像后才启用显示。
GB辅助例程根据编译器保存的ROM存储体状态恢复映射。请用__bankswitch维护该状态;直接写映射器不会更新它。HRAM中的返回存储体保存位置是共享的,嵌套传送覆盖此位置会造成风险。
使用示例
__bankswitch(payload_bank); // GB tile upload expects the payload bank already visible.
loaded=asset_load_tiles(0,0x8010); // Nine tiles = 144 bytes, LCD off.
expect(loaded==1,24);
__bankswitch(1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图块上传例使用9个自制8×8图案,共144字节,并检查MAP类型和无效ID会被拒绝。FC的144字节传送同时覆盖128字节块和剩余16字节。
预期图像中,红色一行从像素坐标(16,32)开始,占72×8像素。依次重复8×8三角形、边框厚1像素的8×8空心正方形、填满左侧4列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:30
Accept tile/raw descriptors and transfer their bytes to VRAM. This path passes the descriptor pointer directly; callers must arrange the correct visible ROM bank.
kitaqgb/lib/asset.c:41
u8 asset_load_tiles(u8 asset_id, u16 vram_dst)
{
AssetDesc desc;
if (asset_get(asset_id, &desc) == 0) return 0;
if (desc.type != ASSET_TYPE_TILES && desc.type != ASSET_TYPE_RAW) return 0;
__vram_copy(vram_dst, desc.ptr, desc.len);
return 1;
}asset_set_table
登记当前描述符表的存储体编号、地址和条目数。同一时间只保留一张表。此函数只保存这三个值,不复制表、不读取素材,也不分配内存。
语法
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);参数
bank描述符表所在的ROM存储体编号,取8位值;不是素材数据的编号。
table指向连续
count个AssetDesc的近指针。登记有效期间,表所在存储体中的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列的图块,共三组。三角形从左上角1像素逐渐扩展到底行8像素。
GB中,(16,64)的蓝色三角形和方框显示__farmemcpy结果,(16,96)的绿色一对显示__far_memcpy结果。每对占16×8像素。CGB显示上述颜色,DMG在白底上以黑色显示相同形状。
预期状态为FAILED CHECKS 000和FIRST FAILURE 00。验证运行300帧,比较文字、图形掩码和颜色是否符合预期。这是模拟器证据,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/asset.h:21
Retain the descriptor table address, bank and count; the table is not copied.
kitaqgb/lib/asset.c:9
void asset_set_table(u8 bank, const AssetDesc* table, u8 count)
{
asset_table_bank = bank;
asset_table = table;
asset_table_count = count;
}audio.h — 音乐、音效与音频控制
提供四个声道的直接发声、跨存储体音乐与音效、立体声路由、音量渐变和自定义波形。音乐流与直接 API 的声道 ID 一致:CH1=0、CH2=1、CH3=2、CH4=3。CH3 播放波形,CH4 产生噪声。
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
Audio_CancelMasterVolumeFade
停止后续音量渐变,保留当前音量及寄存器状态。
语法
void Audio_CancelMasterVolumeFade();返回值
无返回值。修改音源寄存器或驱动状态。
使用示例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}预期结果
A4 依次从左侧、右侧和中央发声。居中时,音量从 7 渐降至 1,再升至 4 并保持。音量 0 仍有微弱声音,停止声道后才静音。改变输出路由可能产生逐渐衰减的滤波瞬态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:125
Cancel further volume steps, retaining the current cached levels and hardware output.
kitaqgb/lib/audio.c:548
void Audio_CancelMasterVolumeFade() {
Audio_FadeTargetLeft = Audio_MasterLeft;
Audio_FadeTargetRight = Audio_MasterRight;
Audio_FadeStepFrames = 0;
Audio_FadeCounter = 0;
Audio_FadeActive = 0;
}Audio_Ch1NoteOn
设置 CH1 扫频、占空比、包络及音高,然后触发脉冲发声。
语法
void __stackcall Audio_Ch1NoteOn(u8 note, u8 vol_env, u8 duty2);参数
note音高表索引 0~67。高音区的折返规则和 CH3 的八度差见使用说明。
vol_envNR12/NR22/NR42 编码:高四位为初始音量,第 3 位选择递增包络,低三位为包络变化周期。例如 0xC0 保持音量 12。
duty2脉冲占空比:0=12.5%、1=25%、3=75%;其他值均选择 50%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
note 是音高表索引,范围为 0~67:0 对应 C2,33 对应 A4。超过 67 时限制为 67;60~67 使用与 48~55 相同的频率。相同索引下,CH3 比脉冲声道低一个八度。NoteOn 函数直接触发硬件发声,不检查启用或暂停标志。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:151
Trigger CH1 with the cached sweep and the supplied raw envelope. Clamp the note index; duty values 0, 1 and 3 select their encodings, and all others use 2.
kitaqgb/lib/audio.c:132
void __stackcall Audio_Ch1NoteOn(u8 note, u8 vol_env, u8 duty2) {
u16 freq;
u8 duty_reg;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
duty_reg = 128;
if (duty2 == 0) duty_reg = 0;
else if (duty2 == 1) duty_reg = 64;
else if (duty2 == 3) duty_reg = 192;
NR10 = Audio_Ch1Sweep;
NR11 = duty_reg;
NR12 = vol_env;
NR13 = (u8)(freq & 0xFF);
hi = (u8)((u16)freq >> 8);
NR14 = (u8)(hi | 0x80);
}Audio_Ch2NoteOn
设置 CH2 占空比、包络及音高,然后触发脉冲发声。
语法
void __stackcall Audio_Ch2NoteOn(u8 note, u8 vol_env, u8 duty2);参数
note音高表索引 0~67。高音区的折返规则和 CH3 的八度差见使用说明。
vol_envNR12/NR22/NR42 编码:高四位为初始音量,第 3 位选择递增包络,低三位为包络变化周期。例如 0xC0 保持音量 12。
duty2脉冲占空比:0=12.5%、1=25%、3=75%;其他值均选择 50%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
note 是音高表索引,范围为 0~67:0 对应 C2,33 对应 A4。超过 67 时限制为 67;60~67 使用与 48~55 相同的频率。相同索引下,CH3 比脉冲声道低一个八度。NoteOn 函数直接触发硬件发声,不检查启用或暂停标志。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:154
Trigger CH2 with the supplied raw envelope and a clamped note index. Duty values 0, 1 and 3 select their encodings; all other values use duty 2.
kitaqgb/lib/audio.c:666
void __stackcall Audio_Ch2NoteOn(u8 note, u8 vol_env, u8 duty2) {
u16 freq;
u8 duty_reg;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
duty_reg = 128;
if (duty2 == 0) duty_reg = 0;
else if (duty2 == 1) duty_reg = 64;
else if (duty2 == 3) duty_reg = 192;
NR21 = duty_reg;
NR22 = vol_env;
NR23 = (u8)(freq & 0xFF);
hi = (u8)((u16)freq >> 8);
NR24 = (u8)(hi | 0x80);
}Audio_Ch3NoteOn
以指定音高和 NR32 输出电平播放已载入的 CH3 波形。
语法
void __stackcall Audio_Ch3NoteOn(u8 note, u8 level);参数
note音高表索引 0~67。高音区的折返规则和 CH3 的八度差见使用说明。
levelNR32 编码:0x00=静音、0x20=100%、0x40=50%、0x60=25%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
note 是音高表索引,范围为 0~67:0 对应 C2,33 对应 A4。超过 67 时限制为 67;60~67 使用与 48~55 相同的频率。相同索引下,CH3 比脉冲声道低一个八度。NoteOn 函数直接触发硬件发声,不检查启用或暂停标志。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:157
Clamp the note index, program the raw NR32 output level, and trigger CH3. Wave RAM must already contain the desired waveform; this does not load one.
kitaqgb/lib/audio.c:115
void __stackcall Audio_Ch3NoteOn(u8 note, u8 level) {
u16 freq;
u8 hi;
if (note > AUDIO_NOTE_MAX) note = AUDIO_NOTE_MAX;
freq = GB_FREQ_TABLE[note];
NR30 = 128;
NR31 = 0;
NR32 = level;
NR33 = (u8)(freq & 255);
hi = (u8)((u16)freq >> 8);
NR34 = (u8)(hi | 128);
}Audio_Ch4NoteOn
缓存噪声参数,设置包络和多项式计数器寄存器,并触发 CH4。
语法
void __stackcall Audio_Ch4NoteOn(u8 param, u8 vol_env);参数
paramNR43 原始值:第 7~4 位为时钟移位量,第 3 位选择短 LFSR,第 2~0 位为分频比编号。
vol_envNR12/NR22/NR42 编码:高四位为初始音量,第 3 位选择递增包络,低三位为包络变化周期。例如 0xC0 保持音量 12。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
未启用基于长度计数器的自动停止。请用包络或显式停止操作结束发声。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:160
Cache the supplied noise parameter, program the raw envelope and polynomial registers, then trigger CH4 with the length-enable bit clear.
kitaqgb/lib/audio.c:688
void __stackcall Audio_Ch4NoteOn(u8 param, u8 vol_env) {
Audio_SetCh4Param(param);
NR41 = 0x20;
NR42 = vol_env;
NR43 = Audio_Ch4Param;
NR44 = 0x80;
}Audio_FadeToMasterVolume
按指定更新间隔,将左右输出音量分别向目标移动一级。
语法
void __stackcall Audio_FadeToMasterVolume(u8 left, u8 right, u8 step_frames);参数
left左输出总音量 0~7,超过 7 时限制为 7。零也不是完全静音。
right右输出总音量 0~7,超过 7 时限制为 7。
step_frames每移动一级音量所需的 Audio_Update 调用次数。为零时立即应用目标音量。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
使用示例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}预期结果
A4 依次从左侧、右侧和中央发声。居中时,音量从 7 渐降至 1,再升至 4 并保持。音量 0 仍有微弱声音,停止声道后才静音。改变输出路由可能产生逐渐衰减的滤波瞬态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:123
Move left/right volume toward clamped targets at the requested Audio_Update call interval. An interval of zero applies the levels immediately.
kitaqgb/lib/audio.c:528
void __stackcall Audio_FadeToMasterVolume(u8 left, u8 right, u8 step_frames) {
Audio_FadeTargetLeft = Audio_ClampVolume(left);
Audio_FadeTargetRight = Audio_ClampVolume(right);
if (step_frames == 0) {
Audio_SetMasterVolume(Audio_FadeTargetLeft, Audio_FadeTargetRight);
return;
}
Audio_FadeStepFrames = step_frames;
Audio_FadeCounter = step_frames;
if (Audio_MasterLeft == Audio_FadeTargetLeft && Audio_MasterRight == Audio_FadeTargetRight) {
Audio_FadeActive = 0;
Audio_FadeCounter = 0;
} else {
Audio_FadeActive = 1;
}
}Audio_Init
启用 APU,初始化音乐、音效、暂停、混音和波形状态,使所有声道保持静音。
语法
void Audio_Init();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:76
Enable the APU and reset software streams, instruments, mix and fade state. Load the initial triangle wave, then leave every channel silent. Call at startup before concurrent interrupt-driven audio service can access these shared fields.
kitaqgb/lib/audio.c:864
void Audio_Init() {
NR52 = 0x80;
NR51 = 0xFF;
Audio_Pan = 0xFF;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_UpdateNR51();
Audio_SetMasterVolume(AUDIO_VOLUME_MAX, AUDIO_VOLUME_MAX);
Audio_SilenceAll();
Audio_PlayingMusic = 0;
Audio_Paused = 0;
Audio_MusicEnabled = 1;
Audio_SfxEnabled = 1;
Audio_Delay = 0;
Audio_MusicBank = 0;
Audio_MusicPointer = 0;
Audio_MusicLoopPoint = 0;
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectUsesCh2 = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_LastChMask = 0;
Audio_LastCh1Note = 0;
Audio_LastCh2Note = 0;
Audio_LastCh3Note = 0;
Audio_LastCh4Param = 0;
Audio_Ch1Duty = 2;
Audio_Ch2Duty = 2;
Audio_Ch1Env = 0xF1;
Audio_Ch2Env = 0xF2;
Audio_Ch4Env = 0xF1;
Audio_Ch4Param = 0x00;
Audio_Ch3Level = AUDIO_CH3_LEVEL_100;
Audio_Ch3Wave = AUDIO_WAVE_TRIANGLE;
Audio_Ch1Sweep = 0x00;
Audio_SetCustomWaveTriangle();
#ifdef AUDIO_EXCLUDE_WAVE_LOAD_API
Audio_ApplyWaveBytes(Audio_CustomWave);
#else
Audio_LoadWave(Audio_Ch3Wave);
#endif
Audio_Ch3Off();
}Audio_LoadCustomWave
将十六字节复制到自定义波形缓存和 CH3 波形 RAM。
语法
void __stackcall Audio_LoadCustomWave(const u8 *wave16);参数
wave16十六个可读字节,每字节的高、低半字节依次表示两个采样值。空指针会被忽略。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
复制完成后,调用方可复用源数组。函数在写入时禁用 DAC,之后需由调用方重新触发音符。
使用示例
// Compare a triangle preset with an original packed 32-sample pulse waveform.
#include "gb_sound_example.h"
__prg_rom u8 custom_wave[16]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0,0,0,0,0};
void main() {
sound_begin("WAVE PRESET CUSTOM");
Audio_SetMasterVolume(3,3);
Audio_LoadWave(AUDIO_WAVE_TRIANGLE);
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// Loading copies all sixteen bytes and enables the DAC without triggering a note.
Audio_LoadCustomWave(custom_wave);
sound_result[0]=Audio_CustomWave[0]; sound_result[1]=Audio_CustomWave[8];
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// The cache survives another preset load; select CUSTOM to reuse it.
Audio_LoadWave(AUDIO_WAVE_SAW);
Audio_LoadWave(AUDIO_WAVE_CUSTOM);
sound_result[2]=WAVE0; sound_result[3]=WAVE8;
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
sound_end();
}预期结果
依次播放三角波预设、原创脉冲波形及从缓存重新载入的同一脉冲波形。每段持续 45 帧,间隔 15 帧。三段均约为 220 Hz,只有第一段音色不同。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:138
Copy 16 packed sample bytes into the persistent custom cache and wave RAM. A null pointer is ignored; the source is needed only for this call.
kitaqgb/lib/audio.c:614
void __stackcall Audio_LoadCustomWave(const u8 *wave16) {
if (wave16 == 0) return;
Audio_CustomWave[0] = wave16[0]; Audio_CustomWave[1] = wave16[1];
Audio_CustomWave[2] = wave16[2]; Audio_CustomWave[3] = wave16[3];
Audio_CustomWave[4] = wave16[4]; Audio_CustomWave[5] = wave16[5];
Audio_CustomWave[6] = wave16[6]; Audio_CustomWave[7] = wave16[7];
Audio_CustomWave[8] = wave16[8]; Audio_CustomWave[9] = wave16[9];
Audio_CustomWave[10] = wave16[10]; Audio_CustomWave[11] = wave16[11];
Audio_CustomWave[12] = wave16[12]; Audio_CustomWave[13] = wave16[13];
Audio_CustomWave[14] = wave16[14]; Audio_CustomWave[15] = wave16[15];
Audio_ApplyWaveBytes(Audio_CustomWave);
}Audio_LoadWave
将预设或缓存的自定义波形载入 CH3 波形 RAM。
语法
void __stackcall Audio_LoadWave(u8 wave_id);参数
wave_idAUDIO_WAVE_* 编号 0~7,其中 7 选择缓存的自定义波形。大于 7 时不执行操作。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
载入波形既不启动音符,也不改变 Audio_Ch3Wave。三角波预设还会将自定义缓存重置为三角波。要发声需调用 Audio_Ch3NoteOn。
使用示例
// Compare a triangle preset with an original packed 32-sample pulse waveform.
#include "gb_sound_example.h"
__prg_rom u8 custom_wave[16]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0,0,0,0,0};
void main() {
sound_begin("WAVE PRESET CUSTOM");
Audio_SetMasterVolume(3,3);
Audio_LoadWave(AUDIO_WAVE_TRIANGLE);
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// Loading copies all sixteen bytes and enables the DAC without triggering a note.
Audio_LoadCustomWave(custom_wave);
sound_result[0]=Audio_CustomWave[0]; sound_result[1]=Audio_CustomWave[8];
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// The cache survives another preset load; select CUSTOM to reuse it.
Audio_LoadWave(AUDIO_WAVE_SAW);
Audio_LoadWave(AUDIO_WAVE_CUSTOM);
sound_result[2]=WAVE0; sound_result[3]=WAVE8;
Audio_Ch3NoteOn(33,AUDIO_CH3_LEVEL_100);
sound_wait(45); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
sound_end();
}预期结果
依次播放三角波预设、原创脉冲波形及从缓存重新载入的同一脉冲波形。每段持续 45 帧,间隔 15 帧。三段均约为 220 Hz,只有第一段音色不同。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:135
Install a named preset or the cached custom wave. Unknown IDs are ignored. This does not update Audio_Ch3Wave or trigger a new note.
kitaqgb/lib/audio.c:631
void __stackcall Audio_LoadWave(u8 wave_id) {
if (wave_id == AUDIO_WAVE_TRIANGLE) Audio_LoadWave0();
else if (wave_id == AUDIO_WAVE_SAW) Audio_LoadWave1();
else if (wave_id == AUDIO_WAVE_VOWEL_A) Audio_LoadWave2();
else if (wave_id == AUDIO_WAVE_VOWEL_I) Audio_LoadWave3();
else if (wave_id == AUDIO_WAVE_VOWEL_U) Audio_LoadWave4();
else if (wave_id == AUDIO_WAVE_VOWEL_E) Audio_LoadWave5();
else if (wave_id == AUDIO_WAVE_VOWEL_O) Audio_LoadWave6();
else if (wave_id == AUDIO_WAVE_CUSTOM) Audio_ApplyWaveBytes(Audio_CustomWave);
}Audio_PlayMusic
准备从指定存储体中曲目的开头播放,并重置乐器、声像和等待时间。
语法
void __stackcall Audio_PlayMusic(u8 bank, u8 *song);参数
bank音频流所在的 ROM 存储体编号,必须与数据的实际位置一致。
song曲目数据首地址。驱动不复制数组,整个播放期间须保持数据可读。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
下一次 Audio_Update 才会读取数据。不检查空指针,已有音效保持活动。
使用示例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}预期结果
依次听到一个音符、暂停静音、继续播放、禁用音乐更新后保留的音、显式停止声道、重新启用后的下一个音符,最后停止。RAM 检查还确认:暂停期间的 30 次更新保持等待计数不变,而禁用音乐写入时计数仍推进到 30。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:83
Retain the bank and song pointer, reset legacy instruments/cursors, and arm playback from the beginning. Supply a valid, persistent song; no bytes are copied and null is not checked. Existing effect streams remain active.
kitaqgb/lib/audio.c:924
void __stackcall Audio_PlayMusic(u8 bank, u8 *song) {
Audio_PlayingMusic = 0;
Audio_MusicBank = bank;
Audio_MusicPointer = song;
Audio_MusicLoopPoint = song;
Audio_Delay = 0;
Audio_LastChMask = 0;
Audio_Ch1Duty = 2;
Audio_Ch2Duty = 2;
Audio_Ch1Env = 0xF1;
Audio_Ch2Env = 0xF2;
Audio_Ch4Env = 0xF1;
Audio_Ch4Param = 0x00;
Audio_Ch3Level = AUDIO_CH3_LEVEL_100;
Audio_Ch3Wave = AUDIO_WAVE_TRIANGLE;
Audio_Ch1Sweep = 0x00;
Audio_Pan = 0xFF;
Audio_UpdateNR51();
Audio_LoadWave(Audio_Ch3Wave);
Audio_PlayingMusic = 1;
}Audio_PlaySFX
记录当前 ROM 存储体,申请播放沿用基础声像的音效。
语法
void __stackcall Audio_PlaySFX(u8 *sfx, u8 priority);参数
sfx音效字节流首地址。脉冲和 CH3 的数据格式见使用说明。
priority优先级 0~255,数值越大越优先;相同数值也可替换当前音效。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:93
Capture the currently mapped ROM bank and start an effect with inherited pan. Use the explicit-bank API when the pointer belongs to another bank.
kitaqgb/lib/audio.c:1085
void __stackcall Audio_PlaySFX(u8 *sfx, u8 priority) {
Audio_PlaySFXBanked(__rom_bank, sfx, priority);
}Audio_PlaySFXBanked
在显式指定的源存储体中识别音效类型,申请播放并沿用基础声像。
语法
void __stackcall Audio_PlaySFXBanked(u8 bank, u8 *sfx, u8 priority);参数
bank音频流所在的 ROM 存储体编号,必须与数据的实际位置一致。
sfx音效字节流首地址。脉冲和 CH3 的数据格式见使用说明。
priority优先级 0~255,数值越大越优先;相同数值也可替换当前音效。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:95
Start a banked effect with the channel's base pan, subject to priority arbitration.
kitaqgb/lib/audio.c:1078
void __stackcall Audio_PlaySFXBanked(u8 bank, u8 *sfx, u8 priority) {
Audio_PlaySFXPannedBanked(bank, sfx, priority, AUDIO_PAN_INHERIT);
}Audio_PlaySFXPanned
记录当前 ROM 存储体,申请播放使用指定声像的音效。
语法
void __stackcall Audio_PlaySFXPanned(u8 *sfx, u8 priority, u8 pan);参数
sfx音效字节流首地址。脉冲和 CH3 的数据格式见使用说明。
priority优先级 0~255,数值越大越优先;相同数值也可替换当前音效。
pan0=居中,1=左,2=右,3=静音。音效 API 将大于 3 的值(包括 0xFF)解释为沿用基础声像。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:98
Capture the currently mapped ROM bank and request an effect with a pan override. Use the explicit-bank API when the pointer belongs to another bank.
kitaqgb/lib/audio.c:1091
void __stackcall Audio_PlaySFXPanned(u8 *sfx, u8 priority, u8 pan) {
Audio_PlaySFXPannedBanked(__rom_bank, sfx, priority, pan);
}Audio_PlaySFXPannedBanked
用明确的存储体、优先级和声像申请脉冲或 CH3 音效槽位。
语法
void __stackcall Audio_PlaySFXPannedBanked(u8 bank, u8 *sfx, u8 priority, u8 pan);参数
bank音频流所在的 ROM 存储体编号,必须与数据的实际位置一致。
sfx音效字节流首地址。脉冲和 CH3 的数据格式见使用说明。
priority优先级 0~255,数值越大越优先;相同数值也可替换当前音效。
pan0=居中,1=左,2=右,3=静音。音效 API 将大于 3 的值(包括 0xFF)解释为沿用基础声像。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:103
Retain a banked effect stream if its priority equals or exceeds the current owner's priority, or that slot is empty. A leading CH3 marker selects the wave slot and is skipped; other streams use the pulse slot. Paused requests are ignored. Keep stream storage valid until completion; acceptance does not trigger a note.
kitaqgb/lib/audio.c:1041
void __stackcall Audio_PlaySFXPannedBanked(u8 bank, u8 *sfx, u8 priority, u8 pan) {
u8 sfx_pan = pan;
u8 use_ch3 = 0;
if (sfx_pan > AUDIO_PAN_MUTE) sfx_pan = AUDIO_PAN_INHERIT;
if (Audio_Paused != 0) return;
if (sfx != 0) {
use_ch3 = Audio_SfxStartsOnCh3Banked(bank, sfx);
if (use_ch3 != 0) {
if (Audio_EffectPointer3 == 0 || priority >= Audio_EffectPriority3) {
Audio_EffectPointer3 = sfx + 1;
Audio_EffectPriority3 = priority;
Audio_EffectBank3 = bank;
Audio_EffectPan3 = sfx_pan;
Audio_UpdateNR51();
}
return;
}
}
if (Audio_EffectPointer == 0 || priority >= Audio_EffectPriority) {
#ifdef AUDIO_VBLANK_SFX_AUTO_CH2
// Choose pulse-channel ownership when accepting the effect, so its later
// updates and deferred recovery agree on which physical channel was borrowed.
Audio_EffectUsesCh2 = (u8)(AudioVBlank_MusicPlaying != 0 &&
AudioVBlank_MusicEnabled != 0);
#else
Audio_EffectUsesCh2 = 0;
#endif
Audio_EffectPointer = sfx;
Audio_EffectPriority = priority;
Audio_EffectBank = bank;
Audio_EffectPan = sfx_pan;
Audio_UpdateNR51();
}
}Audio_SetCh1Duty
缓存下一个 CH1 音符使用的占空比编号。
语法
void __stackcall Audio_SetCh1Duty(u8 duty);参数
duty脉冲占空比编号,仅保存低两位:0=12.5%、1=25%、2=50%、3=75%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
仅调用此函数不会改变正在使用的音源寄存器。需将缓存值传给 NoteOn,或通过相应音频流应用。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:141
Cache the low two duty bits for the next CH1 note; do not change the active register.
kitaqgb/lib/audio.c:644
void __stackcall Audio_SetCh1Duty(u8 duty) {
Audio_Ch1Duty = (u8)(duty & 3);
}Audio_SetCh2Duty
缓存下一个 CH2 音符使用的占空比编号。
语法
void __stackcall Audio_SetCh2Duty(u8 duty);参数
duty脉冲占空比编号,仅保存低两位:0=12.5%、1=25%、2=50%、3=75%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
仅调用此函数不会改变正在使用的音源寄存器。需将缓存值传给 NoteOn,或通过相应音频流应用。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:143
Cache the low two duty bits for the next CH2 note; do not change the active register.
kitaqgb/lib/audio.c:649
void __stackcall Audio_SetCh2Duty(u8 duty) {
Audio_Ch2Duty = (u8)(duty & 3);
}Audio_SetCh3Level
缓存下一个 CH3 音符使用的 NR32 输出电平。
语法
void __stackcall Audio_SetCh3Level(u8 level);参数
levelNR32 编码:0x00=静音、0x20=100%、0x40=50%、0x60=25%。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
仅调用此函数不会改变正在使用的音源寄存器。需将缓存值传给 NoteOn,或通过相应音频流应用。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:145
Cache the raw NR32 level encoding for the next CH3 note without writing hardware.
kitaqgb/lib/audio.c:654
void __stackcall Audio_SetCh3Level(u8 level) {
Audio_Ch3Level = level;
}Audio_SetCh4Param
缓存下一个 CH4 音符使用的 NR43 噪声参数。
语法
void __stackcall Audio_SetCh4Param(u8 param);参数
paramNR43 原始值:第 7~4 位为时钟移位量,第 3 位选择短 LFSR,第 2~0 位为分频比编号。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
仅调用此函数不会改变正在使用的音源寄存器。需将缓存值传给 NoteOn,或通过相应音频流应用。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:147
Cache the raw noise polynomial encoding for the next CH4 note without writing hardware.
kitaqgb/lib/audio.c:659
void __stackcall Audio_SetCh4Param(u8 param) {
Audio_Ch4Param = param;
}Audio_SetMasterVolume
将左右总音量限制在 0~7,立即写入硬件,并取消正在进行的音量渐变。
语法
void __stackcall Audio_SetMasterVolume(u8 left, u8 right);参数
left左输出总音量 0~7,超过 7 时限制为 7。零也不是完全静音。
right右输出总音量 0~7,超过 7 时限制为 7。
返回值
无返回值。修改音源寄存器或驱动状态。
使用示例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}预期结果
A4 依次从左侧、右侧和中央发声。居中时,音量从 7 渐降至 1,再升至 4 并保持。音量 0 仍有微弱声音,停止声道后才静音。改变输出路由可能产生逐渐衰减的滤波瞬态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:120
Clamp both output levels to 0..7, cancel any fade, and write NR50 immediately.
kitaqgb/lib/audio.c:515
void __stackcall Audio_SetMasterVolume(u8 left, u8 right) {
Audio_MasterLeft = Audio_ClampVolume(left);
Audio_MasterRight = Audio_ClampVolume(right);
Audio_FadeTargetLeft = Audio_MasterLeft;
Audio_FadeTargetRight = Audio_MasterRight;
Audio_FadeStepFrames = 0;
Audio_FadeCounter = 0;
Audio_FadeActive = 0;
Audio_UpdateNR50();
}Audio_SetMusicEnabled
控制普通音乐解码器是否向硬件写入新的音符。
语法
void __stackcall Audio_SetMusicEnabled(u8 on);参数
on零表示禁用,非零表示启用。具体是静音还是只停止更新,请查阅当前函数的说明。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
禁用后仍会推进音乐流,并处理乐器和声像控制,不会使当前音符静音。若要暂停播放,应使用 Audio_SetPaused。
使用示例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}预期结果
依次听到一个音符、暂停静音、继续播放、禁用音乐更新后保留的音、显式停止声道、重新启用后的下一个音符,最后停止。RAM 检查还确认:暂停期间的 30 次更新保持等待计数不变,而禁用音乐写入时计数仍推进到 30。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:114
Set the music-processing gate. This does not silence notes already playing or stop the legacy stream cursor from consuming commands.
kitaqgb/lib/audio.c:498
void __stackcall Audio_SetMusicEnabled(u8 on) {
if (on != 0) Audio_MusicEnabled = 1;
else Audio_MusicEnabled = 0;
}Audio_SetPan
更改一个物理声道的基础声像,再重新应用当前音效的声像覆盖设置。
语法
void __stackcall Audio_SetPan(u8 ch, u8 pan);参数
ch声道 ID:0=CH1、1=CH2、2=CH3、3=CH4。直接操作 API 和音乐流使用同一套编号。
pan0=居中,1=左,2=右,3=静音。音效 API 将大于 3 的值(包括 0xFF)解释为沿用基础声像。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
未知声道编号会选中 CH4。此处 pan 超出 0~3 时表示静音,不是音效 API 中的沿用基础声像。
使用示例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}预期结果
A4 依次从左侧、右侧和中央发声。居中时,音量从 7 渐降至 1,再升至 4 并保持。音量 0 仍有微弱声音,停止声道后才静音。改变输出路由可能产生逐渐衰减的滤波瞬态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:129
Update the base pan for a physical channel and reapply active effect overrides. Unknown channel IDs select CH4; unsupported pan values mute the selected channel.
kitaqgb/lib/audio.c:558
void __stackcall Audio_SetPan(u8 ch, u8 pan) {
u8 r_bit;
u8 l_bit;
u8 mask;
if (ch == AUDIO_CHANNEL_CH1) {
r_bit = 0x01;
l_bit = 0x10;
} else if (ch == AUDIO_CHANNEL_CH2) {
r_bit = 0x02;
l_bit = 0x20;
} else if (ch == AUDIO_CHANNEL_CH3) {
r_bit = 0x04;
l_bit = 0x40;
} else {
r_bit = 0x08;
l_bit = 0x80;
}
mask = (u8)(l_bit | r_bit);
Audio_Pan = (u8)(Audio_Pan & (u8)~mask);
if (pan == AUDIO_PAN_CENTER) Audio_Pan = (u8)(Audio_Pan | mask);
else if (pan == AUDIO_PAN_LEFT) Audio_Pan = (u8)(Audio_Pan | l_bit);
else if (pan == AUDIO_PAN_RIGHT) Audio_Pan = (u8)(Audio_Pan | r_bit);
Audio_UpdateNR51();
}Audio_SetPanPacked
从一个字节拆出四个基础声像设置,然后一次性更新 NR51。
语法
void __stackcall Audio_SetPanPacked(u8 packed);参数
packed每个物理声道占两位:CH1 使用第 0~1 位,CH2 使用第 2~3 位,CH3 使用第 4~5 位,CH4 使用第 6~7 位。各字段与 pan 使用相同取值。
返回值
无返回值。修改音源寄存器或驱动状态。
使用示例
// A sustained pulse moves left, right and center, then fades to a quieter level.
#include "gb_sound_example.h"
void main() {
sound_begin("PAN AND VOLUME");
Audio_SetMasterVolume(7,7);
Audio_Ch1NoteOn(33,0xC0,2);
Audio_SetPan(AUDIO_CHANNEL_CH1,AUDIO_PAN_LEFT);
sound_result[0]=NR51;
sound_wait(30);
// Packed low two bits are CH1: 2 means right. Other channels remain centered.
Audio_SetPanPacked(2);
sound_result[1]=NR51;
sound_wait(30);
Audio_SetPanPacked(0);
sound_result[2]=NR51;
Audio_FadeToMasterVolume(1,1,5);
sound_wait(30);
sound_result[3]=NR50;
sound_result[4]=Audio_FadeActive;
sound_wait(30);
// Cancel halfway through a rise: retain volume 4 instead of reaching 7.
Audio_FadeToMasterVolume(7,7,5);
sound_wait(15);
Audio_CancelMasterVolumeFade();
sound_result[5]=NR50;
sound_wait(30);
sound_result[6]=NR50;
// A zero fade interval sets the target immediately. Level zero is not mute.
Audio_FadeToMasterVolume(0,0,0);
sound_result[7]=NR50;
sound_wait(30);
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(15);
sound_end();
}预期结果
A4 依次从左侧、右侧和中央发声。居中时,音量从 7 渐降至 1,再升至 4 并保持。音量 0 仍有微弱声音,停止声道后才静音。改变输出路由可能产生逐渐衰减的滤波瞬态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_mix.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-mix.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:132
Two bits per channel (CH1 in bits 0-1): center, left, right, mute. Applies all four positions with one NR51 update for sequencer automation.
kitaqgb/lib/audio.c:589
void __stackcall Audio_SetPanPacked(u8 packed) {
u8 mix = 0;
u8 pan = (u8)(packed & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x11);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x10);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x01);
pan = (u8)((packed >> 2) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x22);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x20);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x02);
pan = (u8)((packed >> 4) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x44);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x40);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x04);
pan = (u8)((packed >> 6) & 3);
if (pan == AUDIO_PAN_CENTER) mix = (u8)(mix | 0x88);
else if (pan == AUDIO_PAN_LEFT) mix = (u8)(mix | 0x80);
else if (pan == AUDIO_PAN_RIGHT) mix = (u8)(mix | 0x08);
Audio_Pan = mix;
Audio_UpdateNR51();
}Audio_SetPaused
暂停时静音并保留音乐和音效的位置;继续时重新触发符合条件的 CH1、CH2 和 CH4 音符。
语法
void __stackcall Audio_SetPaused(u8 on);参数
on零表示禁用,非零表示启用。具体是静音还是只停止更新,请查阅当前函数的说明。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
此函数不会重新触发 CH3。音效会在后续更新中继续。即使处于暂停状态,直接调用 NoteOn 仍能发声。
使用示例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}预期结果
依次听到一个音符、暂停静音、继续播放、禁用音乐更新后保留的音、显式停止声道、重新启用后的下一个音符,最后停止。RAM 检查还确认:暂停期间的 30 次更新保持等待计数不变,而禁用音乐写入时计数仍推进到 30。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:90
On pause entry, silence hardware while retaining cursors. On resume, retrigger eligible legacy CH1, CH2 and CH4 notes; CH3 is not retriggered here. Updates still advance master fades while paused, and effect playback resumes on a later update.
kitaqgb/lib/audio.c:1010
void __stackcall Audio_SetPaused(u8 on) {
if (on != 0) {
if (Audio_Paused == 0) {
Audio_Paused = 1;
Audio_SilenceAll();
}
} else {
if (Audio_Paused != 0) {
Audio_Paused = 0;
if (Audio_PlayingMusic != 0 && Audio_MusicEnabled != 0) {
if ((Audio_LastChMask & AUDIO_RESUME_CH1) != 0 && Audio_EffectPointer == 0) {
Audio_Ch1NoteOn(Audio_LastCh1Note, Audio_Ch1Env, Audio_Ch1Duty);
}
if ((Audio_LastChMask & AUDIO_RESUME_CH2) != 0) {
Audio_Ch2NoteOn(Audio_LastCh2Note, Audio_Ch2Env, Audio_Ch2Duty);
}
if ((Audio_LastChMask & AUDIO_RESUME_CH4) != 0) {
Audio_Ch4NoteOn(Audio_LastCh4Param, Audio_Ch4Env);
}
}
}
}
}Audio_SetSfxEnabled
控制音效的音符写入;禁用时还会释放音效槽位。
语法
void __stackcall Audio_SetSfxEnabled(u8 on);参数
on零表示禁用,非零表示启用。具体是静音还是只停止更新,请查阅当前函数的说明。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
禁用期间仍可登记音效流,但更新时不会写入音符。若希望听到音效,请先启用音效,再提交请求。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:117
Enable effect note writes, or disable them and release both effect streams. Disabling follows Audio_StopSfx recovery rules rather than silencing all channels.
kitaqgb/lib/audio.c:505
void __stackcall Audio_SetSfxEnabled(u8 on) {
if (on != 0) {
Audio_SfxEnabled = 1;
} else {
Audio_SfxEnabled = 0;
Audio_StopSfx();
}
}Audio_StopChannel
停止一个物理声道,并从继续播放掩码中移除该声道的音乐音符。
语法
void __stackcall Audio_StopChannel(u8 ch);参数
ch声道 ID:0=CH1、1=CH2、2=CH3、3=CH4。直接操作 API 和音乐流使用同一套编号。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
停止 CH1/CH3 时也会清除对应音效槽位。0~3 之外的值不执行操作。VBlank 保存的音乐音符另行管理。
使用示例
// Four isolated voices, each lasting 30 frames, separated by 15 silent frames.
#include "gb_sound_example.h"
void main() {
sound_begin("FOUR SOUND CHANNELS");
Audio_SetMasterVolume(3,3);
// CH1: A4, 50% duty, fixed envelope volume 12.
Audio_SetCh1Duty(2);
sound_result[0]=Audio_Ch1Duty;
Audio_Ch1NoteOn(33,0xC0,Audio_Ch1Duty);
sound_result[1]=NR12;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH1); sound_wait(15);
// CH2: A4, 25% duty. Changing the cached duty takes effect on this trigger.
Audio_SetCh2Duty(1);
sound_result[2]=Audio_Ch2Duty;
Audio_Ch2NoteOn(33,0xC0,Audio_Ch2Duty);
sound_result[3]=NR22;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH2); sound_wait(15);
// CH3: the initialized triangle wave, at 50% digital output level.
Audio_SetCh3Level(AUDIO_CH3_LEVEL_50);
sound_result[4]=Audio_Ch3Level;
Audio_Ch3NoteOn(33,Audio_Ch3Level);
sound_result[5]=NR32;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH3); sound_wait(15);
// CH4: a sustained noise burst; param is NR43, not a pitched note number.
Audio_SetCh4Param(0x35);
sound_result[6]=Audio_Ch4Param;
Audio_Ch4NoteOn(Audio_Ch4Param,0xC0);
sound_result[7]=NR43;
sound_wait(30); Audio_StopChannel(AUDIO_CHANNEL_CH4); sound_wait(15);
sound_end();
}预期结果
约半秒静音后,依次播放 CH1 的 A4、CH2 的 A4、CH3 的 A3 和噪声。每段发声持续 30 帧,间隔 15 帧。SEQUENCE COMPLETE 表示程序流程结束,声音效果请通过录音确认。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_channels.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-channels.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:110
Stop a physical channel and clear its legacy resume bit. CH1 also clears the pulse effect stream; CH3 clears the wave effect stream. This is the legacy channel API and does not clear a VBlank driver's independently latched notes.
kitaqgb/lib/audio.c:981
void __stackcall Audio_StopChannel(u8 ch) {
if (ch == AUDIO_CHANNEL_CH1) {
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH1);
Audio_UpdateNR51();
Audio_SilenceCh1();
} else if (ch == AUDIO_CHANNEL_CH2) {
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH2);
Audio_SilenceCh2();
} else if (ch == AUDIO_CHANNEL_CH3) {
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH3);
Audio_UpdateNR51();
Audio_Ch3Off();
} else if (ch == AUDIO_CHANNEL_CH4) {
Audio_LastChMask = (u8)(Audio_LastChMask & (u8)~AUDIO_RESUME_CH4);
Audio_SilenceCh4();
}
}Audio_StopMusic
清除曲目播放状态及继续播放掩码,并立即使四个声道静音。
语法
void Audio_StopMusic();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效指针仍然保留,因此后续 Audio_Update 可能再次播放音效。若要全部停止,还应调用 Audio_StopSfx。
使用示例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}预期结果
依次听到一个音符、暂停静音、继续播放、禁用音乐更新后保留的音、显式停止声道、重新启用后的下一个音符,最后停止。RAM 检查还确认:暂停期间的 30 次更新保持等待计数不变,而禁用音乐写入时计数仍推进到 30。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:86
Stop legacy music and clear its resume mask, then silence all physical channels. Effect pointers remain active and may produce sound on a later update.
kitaqgb/lib/audio.c:952
void Audio_StopMusic() {
Audio_PlayingMusic = 0;
Audio_LastChMask = 0;
Audio_SilenceAll();
}Audio_StopSfx
清除两条音效流及其声像覆盖设置,然后释放借用的声道。
语法
void Audio_StopSfx();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
音效每次更新读取一组 (note, value),遇到 note=0 时结束。脉冲音效的 value 使用 NR12 格式,CH3 使用 NR32 格式;CH3 数据开头需加 0xFF。播放结束前须保持数组有效。优先级在同一个槽位内比较,相同优先级也可替换当前音效。
普通配置下,音效结束会关闭 CH3。若音乐已停止,还会使 CH1 静音;若音乐正在播放,则不会自动重新触发之前的音乐音符。VBlank 配置中的 AUDIO_VBLANK_SFX_RESTORE 会请求 ISR 恢复声道。
使用示例
// Each pair lasts one update. Priority controls replacement within each slot.
#include "gb_sound_example.h"
#pragma bank 2
__prg_rom u8 effect[]={33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,33,0xC0,
40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,40,0xC0,0};
__prg_rom u8 wave_effect[]={AUDIO_SFX_CH3_MARKER,
33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,33,0x20,
40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,40,0x20,0};
#pragma bank 0
void main() {
sound_begin("BANKED SOUND EFFECT");
Audio_SetMasterVolume(3,3);
// Read the marker in bank 2 while restoring the caller's original mapping.
Audio_PlaySFXPannedBanked(2,effect,5,AUDIO_PAN_LEFT);
sound_result[0]=Audio_EffectPriority;
Audio_PlaySFXBanked(2,effect,4);
sound_result[1]=Audio_EffectPriority;
sound_wait(30);
Audio_PlaySFXPannedBanked(2,wave_effect,5,AUDIO_PAN_RIGHT);
sound_result[2]=Audio_EffectPriority3;
sound_wait(30);
// Implicit-bank entry points capture bank 2; the service restores bank 1.
__bankswitch(2);
Audio_PlaySFX(effect,5);
__bankswitch(1);
sound_result[3]=Audio_EffectBank;
sound_wait(30);
__bankswitch(2);
Audio_PlaySFXPanned(effect,5,AUDIO_PAN_RIGHT);
__bankswitch(1);
sound_wait(6);
Audio_StopSfx();
sound_result[4]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
// Disabling effects releases current owners; re-enable before the next request.
Audio_PlaySFXBanked(2,effect,5);
sound_wait(6);
Audio_SetSfxEnabled(0);
sound_result[5]=Audio_SfxEnabled;
sound_result[6]=(u8)(Audio_EffectPointer==0);
sound_wait(24);
Audio_SetSfxEnabled(1);
Audio_PlaySFXBanked(2,effect,5);
sound_wait(30);
sound_result[7]=Audio_SfxEnabled;
sound_end();
}预期结果
存储体 2 中的数据依次播放左侧短促上行脉冲音、右侧波形音效和居中脉冲音。后续步骤提前停止一个右侧脉冲音,禁用音效,再重新启用播放。优先级 4 的请求不能替换优先级 5 的音效。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_sfx.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-sfx.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:106
Clear both effect streams and their pan overrides. When not paused, release borrowed channels through the configured legacy or VBlank recovery path.
kitaqgb/lib/audio.c:960
void Audio_StopSfx() {
Audio_EffectPointer = 0;
Audio_EffectPriority = 0;
Audio_EffectBank = 0;
Audio_EffectPointer3 = 0;
Audio_EffectPriority3 = 0;
Audio_EffectBank3 = 0;
Audio_EffectPan = AUDIO_PAN_INHERIT;
Audio_EffectPan3 = AUDIO_PAN_INHERIT;
Audio_UpdateNR51();
if (Audio_Paused != 0) return;
Audio_ReleaseCh1AfterSfx();
Audio_RestoreCh3FromMusic();
}Audio_Update
推进一次音量渐变;未暂停时,先更新音效,再更新音乐。
语法
void Audio_Update();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
按 audio_hwregs_gb.c、audio.c 的顺序包含源码,并在启动时调用 Audio_Init。每帧只调用一次 Audio_Update,频率约为 59.7 Hz。读取音频流时会临时改变 ROM 映射,因此不要从中断重入此驱动。
暂停期间音量渐变仍会推进。读取 WAIT n 后还会经历 n 次等待调用,因此下一条命令在 n+1 次更新后执行。
使用示例
// Original two-note phrase: pause preserves the cursor; the enable gate does not.
#include "gb_sound_example.h"
__prg_rom u8 song[]={
AUDIO_CMD_SET_INST,AUDIO_STREAM_CHANNEL_CH1,2,0xC0,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,33,AUDIO_CMD_WAIT,59,
AUDIO_CMD_NOTE,AUDIO_STREAM_CHANNEL_CH1,40,AUDIO_CMD_WAIT,59,
AUDIO_CMD_LOOP
};
void main() {
u8 delay;
sound_begin("MUSIC PAUSE GATE");
Audio_SetMasterVolume(3,3);
Audio_PlayMusic(0,song);
sound_wait(30);
delay=Audio_Delay;
Audio_SetPaused(1);
sound_wait(30);
sound_result[0]=(u8)(Audio_Delay==delay);
sound_result[1]=Audio_Paused;
Audio_SetPaused(0);
sound_wait(30);
sound_result[2]=Audio_Paused;
// Disabling suppresses note writes; the next 30 updates still consume data.
Audio_SetMusicEnabled(0);
sound_wait(30);
sound_result[3]=Audio_Delay;
sound_result[4]=Audio_MusicEnabled;
// Explicitly stop the sounding channel while leaving the stream running.
Audio_StopChannel(AUDIO_CHANNEL_CH1);
sound_wait(30);
Audio_SetMusicEnabled(1);
sound_wait(30);
Audio_StopMusic();
sound_result[5]=Audio_PlayingMusic;
sound_wait(30);
sound_end();
}预期结果
依次听到一个音符、暂停静音、继续播放、禁用音乐更新后保留的音、显式停止声道、重新启用后的下一个音符,最后停止。RAM 检查还确认:暂停期间的 30 次更新保持等待计数不变,而禁用音乐写入时计数仍推进到 30。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_music.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-music.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio.h:79
Advance the master fade first. Unless paused, service both effect streams then the optional legacy music decoder. Call once per frame for frame-based timing.
kitaqgb/lib/audio.c:849
void Audio_Update() {
Audio_UpdateFade();
if (Audio_Paused != 0) return;
Audio_ServiceSfx();
#ifndef AUDIO_EXCLUDE_LEGACY_MUSIC_SERVICE
Audio_ServiceMusic();
#endif
}audio_vblank.h — 通过 VBlank IRQ 播放音乐
在 VBlank 中断中更新音乐,协调音效的声道占用,并提供逐帧钩子。不要同时用普通音乐解码器推进同一首曲目。
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
AudioVBlank_DisableIrq
仅清除 IE 的 VBlank 位,其他中断源和声音保持不变。
语法
void AudioVBlank_DisableIrq();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:118
Clear only the VBlank bit in IE. Other interrupt sources and current sound remain unchanged.
kitaqgb/lib/audio_vblank_control.inc:129
void AudioVBlank_DisableIrq()
{
AudioVBlank_IE = (u8)(AudioVBlank_IE & 0xFE);
}AudioVBlank_EnableIrq
设置 IE 的 VBlank 位,并执行 EI,全局启用 CPU 中断。
语法
void AudioVBlank_EnableIrq();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:116
Enable the VBlank source in IE and execute EI, enabling CPU interrupts globally. Install the correct interrupt vector and initialize driver state first.
kitaqgb/lib/audio_vblank_control.inc:122
void AudioVBlank_EnableIrq()
{
AudioVBlank_IE = (u8)(AudioVBlank_IE | 0x01);
__asm { EI }
}AudioVBlank_Init
初始化 VBlank 音乐状态、队列、乐器及钩子。此函数不初始化 APU,也不屏蔽中断。
语法
void AudioVBlank_Init();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:85
Reset software playback state, queue counters, instruments and the frame hook. Call before enabling this ISR; the routine does not mask interrupts or initialize the APU registers and cannot synchronize with an active handler.
kitaqgb/lib/audio_vblank_control.inc:6
void AudioVBlank_Init()
{
AudioVBlank_MusicEnabled = 1;
AudioVBlank_MusicPlaying = 0;
AudioVBlank_MusicDelay = 0;
AudioVBlank_MusicStart = 0;
AudioVBlank_MusicPointer = 0;
AudioVBlank_MusicLoopPoint = 0;
AudioVBlank_Step = 0;
AudioVBlank_Ch2Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch3Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch2LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch1LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch3LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
AudioVBlank_Ch4Param = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Envelope = 0xF1;
AudioVBlank_Ch2Envelope = 0xF2;
AudioVBlank_Ch4Envelope = 0xF1;
AudioVBlank_Ch1Duty = 0x80;
AudioVBlank_Ch2Duty = 0x80;
AudioVBlank_Ch1Sweep = 0x00;
AudioVBlank_Ch3Level = 0x20;
AudioVBlank_ControlCommand = 0;
AudioVBlank_ControlArg0 = 0;
AudioVBlank_ControlArg1 = 0;
AudioVBlank_ControlArg2 = 0;
AudioVBlank_QueueMode = 0;
AudioVBlank_QueueReadIndex = 0;
AudioVBlank_QueueWriteIndex = 0;
AudioVBlank_QueueCount = 0;
AudioVBlank_QueueUnderruns = 0;
AudioVBlank_FrameHook = 0;
}AudioVBlank_Play
从已选曲目的开头重新播放,并退出队列模式。曲目为空指针时停止播放。
语法
void AudioVBlank_Play();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:103
Restart the selected direct song at its beginning, leaving queue mode. A null song invokes Stop; this does not enable the music or IRQ gates.
kitaqgb/lib/audio_vblank_control.inc:58
void AudioVBlank_Play()
{
if (AudioVBlank_MusicStart == 0) {
AudioVBlank_Stop();
return;
}
AudioVBlank_MusicPointer = AudioVBlank_MusicStart;
AudioVBlank_MusicLoopPoint = AudioVBlank_MusicStart;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_QueueMode = 0;
AudioVBlank_MusicPlaying = 1;
}AudioVBlank_PlayMusic
选择直接播放的曲目,并准备从头开始播放。
语法
void __stackcall AudioVBlank_PlayMusic(u8 *song);参数
song曲目数据首地址。驱动不复制数组,整个播放期间须保持数据可读。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:105
Select and restart a directly addressed song. This is not the banked WRAM-queue path.
kitaqgb/lib/audio_vblank_control.inc:73
void __stackcall AudioVBlank_PlayMusic(u8 *song)
{
AudioVBlank_SetMusic(song);
AudioVBlank_Play();
}AudioVBlank_QueuePlay
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
处于队列模式且至少已有一条记录时,启用播放。保留读写位置、等待时间、音乐启用设置和暂停状态。开始新的音频流前,应先重置并预填队列。
语法
u8 AudioVBlank_QueuePlay();返回值
接受启动请求时返回 1;队列为空或不在队列模式时返回 0。只有 ISR 正在运行、音乐已启用且驱动未暂停时,队列才会被继续消耗。
使用条件与限制
调用 AudioVBlank_Init 后,依次调用 QueueReset、QueueRefill 和 QueuePlay。编译普通版本的 audio_vblank.c;定义 AUDIO_VBLANK_CORE_ONLY 时不会包含负责补充数据的 C 函数。队列可容纳十六条记录,每条五字节,由主循环一侧填充、VBlank 中断服务程序消耗。
只允许主循环一侧的一个入口补充数据,不要递归调用,也不要从 ISR 或 FrameHook 调用。提交每条记录时,仅在短暂的临界区内保存 IE 并屏蔽中断源,随后恢复 IE 和 SVBK,不改变 IME。源数据所在的 ROM/RAM 存储体由调用方选择并保持可读。
带时长的记录按 delay、ch1_note、ch2_note、ch3_note、ch4_noise_param 排列。END 也必须占满五字节。LOOP 需由补充数据的代码展开,重新提交需要重复的记录。不能直接传入用于直接播放的可变长命令流。
使用示例
if(AudioVBlank_QueuePlay()==0) sound_result[0]++;
AudioVBlank_EnableIrq();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将一段原创的八音短句播放六遍,然后停止。十六槽队列分批接收共 49 条记录:48 个音符和一条 END。现有验证涵盖全部记录被接收、欠载次数为零、播放结束,以及空队列、满队列、空指针和零数量输入;还检查 CGB WRAM 存储体恢复、IME 保持、播放时有声音及结束后静音。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:96
Start/resume queued playback; return 1 on success, 0 for an empty/nonqueue mode. Does not enable the interrupt, music gate or clear pause. Queue before starting.
kitaqgb/lib/audio_vblank_queue.inc:112
u8 AudioVBlank_QueuePlay()
{
u8 saved_ie;
u8 started;
saved_ie = AudioVBlank_IE;
AudioVBlank_IE = 0;
started = 0;
if (AudioVBlank_QueueMode != 0 && AudioVBlank_QueueCount != 0) {
AudioVBlank_MusicPlaying = 1;
started = 1;
}
AudioVBlank_IE = saved_ie;
return started;
}AudioVBlank_QueueRefill
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
将完整的五字节记录复制到空闲槽位,每条记录复制完毕后才允许 ISR 读取。一次调用最多接收十六条记录。
语法
u8 __stackcall AudioVBlank_QueueRefill(const u8 *records, u8 count);参数
records当前可读存储体中的记录数组。每条可能被接收的记录必须有五个可读字节。传入空指针时返回零。
count尝试复制的记录数,不是字节数。大于十六时按十六处理,为零时不做任何操作。
返回值
返回实际接收的记录数,范围为 0~16。队列已满、输入为空、数量为零或未处于队列模式时返回零。源指针应前移返回值乘以五个字节。调用期间 ISR 可能释放槽位,因此返回值不一定等于调用开始时的空闲数量。
使用条件与限制
调用 AudioVBlank_Init 后,依次调用 QueueReset、QueueRefill 和 QueuePlay。编译普通版本的 audio_vblank.c;定义 AUDIO_VBLANK_CORE_ONLY 时不会包含负责补充数据的 C 函数。队列可容纳十六条记录,每条五字节,由主循环一侧填充、VBlank 中断服务程序消耗。
只允许主循环一侧的一个入口补充数据,不要递归调用,也不要从 ISR 或 FrameHook 调用。提交每条记录时,仅在短暂的临界区内保存 IE 并屏蔽中断源,随后恢复 IE 和 SVBK,不改变 IME。源数据所在的 ROM/RAM 存储体由调用方选择并保持可读。
带时长的记录按 delay、ch1_note、ch2_note、ch3_note、ch4_noise_param 排列。END 也必须占满五字节。LOOP 需由补充数据的代码展开,重新提交需要重复的记录。不能直接传入用于直接播放的可变长命令流。
使用示例
next=AudioVBlank_QueueRefill(queue_song,16);
// next counts records; each record occupies five source bytes.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将一段原创的八音短句播放六遍,然后停止。十六槽队列分批接收共 49 条记录:48 个音符和一条 END。现有验证涵盖全部记录被接收、欠载次数为零、播放结束,以及空队列、满队列、空指针和零数量输入;还检查 CGB WRAM 存储体恢复、IME 保持、播放时有声音及结束后静音。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:93
Copy complete records from the currently mapped source; return records accepted. Each call attempts at most 16 records and stops when full. Null/zero returns 0. Supports ROM and RAM sources, restores SVBK and IE, and never changes IME. Single foreground producer only: do not call from an ISR or frame hook. LOOP must be expanded by the producer. END occupies a full five-byte record.
kitaqgb/lib/audio_vblank_queue.inc:93
u8 __stackcall AudioVBlank_QueueRefill(const u8 *records, u8 count)
{
u8 accepted;
u8 i;
if (records == 0 || count == 0 || AudioVBlank_QueueMode == 0) return 0;
if (count > AUDIO_VBLANK_QUEUE_RECORDS) count = AUDIO_VBLANK_QUEUE_RECORDS;
accepted = 0;
while (accepted < count) {
if (AudioVBlank_QueueCount >= AUDIO_VBLANK_QUEUE_RECORDS) break;
for (i = 0; i < 5; i++) AudioVBlank_QueueStaging[i] = records[i];
if (AudioVBlank_QueuePublish() == 0) break;
records += 5;
accepted++;
}
return accepted;
}AudioVBlank_QueueReset
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
停止播放并消音,选择队列模式,将读写位置、剩余记录数和欠载计数清零。此操作会丢弃尚未播放的记录,为新音频流做准备。
语法
void AudioVBlank_QueueReset();返回值
无返回值。保留音乐启用和暂停设置。
使用条件与限制
调用 AudioVBlank_Init 后,依次调用 QueueReset、QueueRefill 和 QueuePlay。编译普通版本的 audio_vblank.c;定义 AUDIO_VBLANK_CORE_ONLY 时不会包含负责补充数据的 C 函数。队列可容纳十六条记录,每条五字节,由主循环一侧填充、VBlank 中断服务程序消耗。
只允许主循环一侧的一个入口补充数据,不要递归调用,也不要从 ISR 或 FrameHook 调用。提交每条记录时,仅在短暂的临界区内保存 IE 并屏蔽中断源,随后恢复 IE 和 SVBK,不改变 IME。源数据所在的 ROM/RAM 存储体由调用方选择并保持可读。
带时长的记录按 delay、ch1_note、ch2_note、ch3_note、ch4_noise_param 排列。END 也必须占满五字节。LOOP 需由补充数据的代码展开,重新提交需要重复的记录。不能直接传入用于直接播放的可变长命令流。
使用示例
AudioVBlank_QueueReset(); // Silence playback and discard any queued records.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将一段原创的八音短句播放六遍,然后停止。十六槽队列分批接收共 49 条记录:48 个音符和一条 END。现有验证涵盖全部记录被接收、欠载次数为零、播放结束,以及空队列、满队列、空指针和零数量输入;还检查 CGB WRAM 存储体恢复、IME 保持、播放时有声音及结束后静音。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\audio_vblank_queue.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\audio_vblank_queue.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:87
Stop music and select an empty WRAM queue; preserve IE, IME and enable/pause gates.
kitaqgb/lib/audio_vblank_queue.inc:76
void AudioVBlank_QueueReset()
{
u8 saved_ie;
saved_ie = AudioVBlank_IE;
AudioVBlank_IE = 0;
AudioVBlank_Stop();
AudioVBlank_QueueMode = 1;
AudioVBlank_QueueReadIndex = 0;
AudioVBlank_QueueWriteIndex = 0;
AudioVBlank_QueueCount = 0;
AudioVBlank_QueueUnderruns = 0;
AudioVBlank_IE = saved_ie;
}AudioVBlank_RequestRestoreCh1
无挂起事件时设置脉冲声道的仅释放请求 2;若已有恢复请求 1,则保留它。
语法
void AudioVBlank_RequestRestoreCh1();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接调用恢复函数时,调用方须保证音效已释放声道,并在随后清除挂起标志。仅调用请求函数不会写音源寄存器。
使用示例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}预期结果
依次听到脉冲声道恢复、静音、波形声道恢复、静音,每段持续 30 帧。释放请求会保留已挂起的值 1;没有挂起事件时则产生仅释放请求值 2。本例禁用 IRQ,由调用方显式管理声道占用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:129
Request deferred pulse-channel release only when no recovery is pending. Preserve value 1, which records a music event suppressed while an effect owned it.
kitaqgb/lib/audio_vblank.c:908
void AudioVBlank_RequestRestoreCh1()
{
if (AudioVBlank_RestoreCh1Pending == 0)
AudioVBlank_RestoreCh1Pending = 2;
}AudioVBlank_RequestRestoreCh3
无挂起事件时设置波形声道的仅释放请求 2;若已有恢复请求 1,则保留它。
语法
void AudioVBlank_RequestRestoreCh3();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接调用恢复函数时,调用方须保证音效已释放声道,并在随后清除挂起标志。仅调用请求函数不会写音源寄存器。
使用示例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}预期结果
依次听到脉冲声道恢复、静音、波形声道恢复、静音,每段持续 30 帧。释放请求会保留已挂起的值 1;没有挂起事件时则产生仅释放请求值 2。本例禁用 IRQ,由调用方显式管理声道占用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:132
Request a deferred wave-channel release only if no recovery is pending. Preserve value 1, which records a new music event suppressed by the effect.
kitaqgb/lib/audio_vblank.c:916
void AudioVBlank_RequestRestoreCh3()
{
if (AudioVBlank_RestoreCh3Pending == 0)
AudioVBlank_RestoreCh3Pending = 2;
}AudioVBlank_RestoreCh1
将保存的音乐音符恢复到音效借用的脉冲声道(CH1 或 CH2),或仅释放该声道并保持静音。
语法
void AudioVBlank_RestoreCh1();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接调用恢复函数时,调用方须保证音效已释放声道,并在随后清除挂起标志。仅调用请求函数不会写音源寄存器。
使用示例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}预期结果
依次听到脉冲声道恢复、静音、波形声道恢复、静音,每段持续 30 帧。释放请求会保留已挂起的值 1;没有挂起事件时则产生仅释放请求值 2。本例禁用 IRQ,由调用方显式管理声道占用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:122
Recover the borrowed pulse channel (CH1 or CH2) from its latched music event. Pending value 2, disabled music, or a non-note latch silences it instead. The caller must first establish that the effect has released the channel.
kitaqgb/lib/audio_vblank.c:769
void AudioVBlank_RestoreCh1()
{
__asm {
LD_A_MEM Audio_EffectUsesCh2
OR_A
JR_NZ audiovb_restore_ch2
LD_A_MEM AudioVBlank_RestoreCh1Pending
CP_IMM 2
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch1_off
LD_A_MEM AudioVBlank_Ch1LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch1_off
LD_C_A
LD_A_MEM AudioVBlank_Ch1Sweep
LDH_MEM_A 0x10
LD_A_MEM AudioVBlank_Ch1Duty
LDH_MEM_A 0x11
LD_A_MEM AudioVBlank_Ch1Envelope
LDH_MEM_A 0x12
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x13
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x14
JR audiovb_restore_ch1_done
audiovb_restore_ch1_off:
XOR_A
LDH_MEM_A 0x12
audiovb_restore_ch1_done:
JR audiovb_restore_square_done
audiovb_restore_ch2:
LD_A_MEM AudioVBlank_RestoreCh1Pending
CP_IMM 2
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch2_off
LD_A_MEM AudioVBlank_Ch2LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch2_off
LD_C_A
LD_A_MEM AudioVBlank_Ch2Duty
LDH_MEM_A 0x16
LD_A_MEM AudioVBlank_Ch2Envelope
LDH_MEM_A 0x17
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x18
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x19
JR audiovb_restore_square_done
audiovb_restore_ch2_off:
XOR_A
LDH_MEM_A 0x17
audiovb_restore_square_done:
}
}AudioVBlank_RestoreCh3
音效释放后恢复 CH3 保存的音乐音符,或禁用其 DAC。
语法
void AudioVBlank_RestoreCh3();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接调用恢复函数时,调用方须保证音效已释放声道,并在随后清除挂起标志。仅调用请求函数不会写音源寄存器。
使用示例
// Exercise the recovery API with IRQ disabled and explicit effect ownership.
#define SOUND_VBLANK
#include "gb_sound_example.h"
void main() {
sound_begin("EFFECT RECOVERY");
Audio_NoiseEffectActive=0; AudioVBlank_Init();
Audio_SetMasterVolume(3,3);
AudioVBlank_MusicPlaying=1;
AudioVBlank_Ch1Envelope=0xC0; AudioVBlank_Ch1LatchedNote=33;
AudioVBlank_Ch3LatchedNote=33;
// Value 1 represents a suppressed BGM event and survives a release request.
AudioVBlank_RestoreCh1Pending=1;
AudioVBlank_RequestRestoreCh1();
sound_result[0]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
// Without a pending event, RequestRestore creates release-only value 2.
AudioVBlank_RequestRestoreCh1();
sound_result[1]=AudioVBlank_RestoreCh1Pending;
AudioVBlank_RestoreCh1(); AudioVBlank_RestoreCh1Pending=0;
sound_wait(30);
AudioVBlank_RestoreCh3Pending=1;
AudioVBlank_RequestRestoreCh3();
sound_result[2]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_RequestRestoreCh3();
sound_result[3]=AudioVBlank_RestoreCh3Pending;
AudioVBlank_RestoreCh3(); AudioVBlank_RestoreCh3Pending=0;
sound_wait(30);
AudioVBlank_Stop();
sound_end();
}预期结果
依次听到脉冲声道恢复、静音、波形声道恢复、静音,每段持续 30 帧。释放请求会保留已挂起的值 1;没有挂起事件时则产生仅释放请求值 2。本例禁用 IRQ,由调用方显式管理声道占用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_recovery.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-recovery.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:126
Recover a latched wave-channel music event after effect release, or disable the DAC for a release-only request, disabled music, or a non-note latch. The caller owns effect arbitration and clearing the pending flag.
kitaqgb/lib/audio_vblank.c:860
void AudioVBlank_RestoreCh3()
{
__asm {
LD_A_MEM AudioVBlank_RestoreCh3Pending
CP_IMM 2
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_MusicPlaying
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_MusicEnabled
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM Audio_MusicEnabled
OR_A
JR_Z audiovb_restore_ch3_off
LD_A_MEM AudioVBlank_Ch3LatchedNote
CP_IMM 0x44
JR_NC audiovb_restore_ch3_off
LD_C_A
LD_A_IMM 0x80
LDH_MEM_A 0x1A
XOR_A
LDH_MEM_A 0x1B
LD_A_MEM AudioVBlank_Ch3Level
LDH_MEM_A 0x1C
LD_A_C
LD_E_A
LD_D_IMM 0
LD_HL_IMM AudioVBlank_FreqLo
ADD_HL_DE
LD_A_HL
LDH_MEM_A 0x1D
LD_HL_IMM AudioVBlank_FreqHi
ADD_HL_DE
LD_A_HL
OR_IMM 0x80
LDH_MEM_A 0x1E
JR audiovb_restore_ch3_done
audiovb_restore_ch3_off:
XOR_A
LDH_MEM_A 0x1A
audiovb_restore_ch3_done:
}
}AudioVBlank_SetEnabled
控制 VBlank 音乐更新,禁用时清除声道恢复请求。当前声音和 IRQ 状态不变。
语法
void __stackcall AudioVBlank_SetEnabled(u8 on);参数
on零表示禁用,非零表示启用。具体是静音还是只停止更新,请查阅当前函数的说明。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:113
Gate music processing and clear pending CH1/CH3 recovery on disable. No audio register is written here: disabling does not itself silence a note already playing or disable the VBlank interrupt.
kitaqgb/lib/audio_vblank_control.inc:109
void __stackcall AudioVBlank_SetEnabled(u8 on)
{
if (on != 0) AudioVBlank_MusicEnabled = 1;
else {
AudioVBlank_MusicEnabled = 0;
// Do not carry a stale SFX-recovery request into a later song.
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
}
}AudioVBlank_SetMusic
选择直接播放的曲目并重置读取位置,不改变播放标志。
语法
void __stackcall AudioVBlank_SetMusic(u8 *song);参数
song曲目数据首地址。驱动不复制数组,整个播放期间须保持数据可读。
返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:100
Select a directly addressed song and reset its cursors, leaving the playing flag unchanged. Song storage must remain accessible to the fixed-bank ISR; coordinate this multi-field update with interrupt execution.
kitaqgb/lib/audio_vblank_control.inc:46
void __stackcall AudioVBlank_SetMusic(u8 *song)
{
AudioVBlank_MusicStart = song;
AudioVBlank_MusicPointer = song;
AudioVBlank_MusicLoopPoint = song;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_QueueMode = 0;
}AudioVBlank_Stop
清除播放、等待和声道恢复状态,立即使脉冲、噪声及波形声道静音。IRQ 保持启用。
语法
void AudioVBlank_Stop();返回值
无返回值。修改音源寄存器或驱动状态。
使用条件与限制
audio_vblank.c 实现 __kq_vblank_vector,不要同时安装另一个 VBlank 向量处理程序。示例定义 Audio_NoiseEffectActive,排除普通音乐解码器,仅用 Audio_Update 更新音效和音量渐变。更改曲目指针或共享状态时应屏蔽中断。
直接播放的曲目数据应放在固定 ROM 等 ISR 始终能读取的位置。每条记录为五字节:delay、CH1、CH2、CH3、CH4。音符字段中的 0xFF 表示保持,0xFD 表示停止;delay 字段中的 0xFF 表示结束,0xFE 表示循环。这是记录布局,需与声道 ID 的编号区分。WRAM 队列属于另一播放模式,使用 AudioVBlank_QueueRefill 提交完整的五字节记录。
使用示例
// Direct fixed-bank music with an actual VBlank ISR and a counting frame hook.
#define SOUND_VBLANK
#include "gb_sound_example.h"
__wram u8 hook_count;
__prg_rom u8 irq_song[]={30,33,0xFF,0xFF,0xFF,30,40,0xFF,0xFF,0xFF,0xFE};
void frame_hook() { hook_count++; }
void main() {
u8 before;
sound_begin("VBLANK MUSIC GATES");
Audio_NoiseEffectActive=0;
AudioVBlank_Init();
sound_result[0]=AudioVBlank_MusicPlaying;
AudioVBlank_Ch1Envelope=0xC0;
Audio_SetMasterVolume(3,3);
hook_count=0; AudioVBlank_FrameHook=frame_hook;
AudioVBlank_SetMusic(irq_song);
AudioVBlank_Play();
AudioVBlank_EnableIrq();
sound_wait(45);
// Disable music processing while the hook and current note remain active.
__asm { DI }
AudioVBlank_SetEnabled(0); before=hook_count;
__asm { EI }
sound_wait(30);
sound_result[1]=(u8)(hook_count!=before);
__asm { DI }
AudioVBlank_Stop();
__asm { EI }
sound_wait(30);
__asm { DI }
AudioVBlank_SetEnabled(1); AudioVBlank_PlayMusic(irq_song);
__asm { EI }
sound_wait(45);
AudioVBlank_DisableIrq();
before=hook_count;
sound_wait(30);
sound_result[2]=(u8)(hook_count==before);
sound_result[3]=(u8)(AudioVBlank_IE&1);
AudioVBlank_Stop(); sound_wait(30);
sound_end();
}预期结果
ISR 播放一首双音符短曲。示例对比三种行为:禁用音乐更新时保留当前声音;Stop 立即静音;重新开始后禁用 IRQ 也会保留声音。RAM 检查确认音乐禁用时钩子仍被调用,而禁用 IRQ 后不再调用。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sound_vblank.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\gb-vblank.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/audio_vblank.h:109
Stop software playback and silence pulse/noise envelopes and the wave DAC. Song pointers and the IRQ enable remain unchanged; the routine writes the physical channels directly, so coordinate ownership with sound effects.
kitaqgb/lib/audio_vblank_control.inc:82
void AudioVBlank_Stop()
{
AudioVBlank_MusicPlaying = 0;
AudioVBlank_MusicDelay = 0;
AudioVBlank_Step = 0;
AudioVBlank_Ch2Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch1Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch3Note = AUDIO_VBLANK_REST;
AudioVBlank_Ch2LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch1LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_Ch3LatchedNote = AUDIO_VBLANK_STOP;
AudioVBlank_RestoreCh1Pending = 0;
AudioVBlank_RestoreCh3Pending = 0;
AudioVBlank_Ch4Param = AUDIO_VBLANK_REST;
__asm {
XOR_A
LDH_MEM_A 0x12
LDH_MEM_A 0x17
LDH_MEM_A 0x1C
LDH_MEM_A 0x1A
LDH_MEM_A 0x21
}
}bank.h — 考虑 bank 的代码与数据访问
ROM 存储体是映射到 CPU 地址空间的一块卡带存储区域。不同存储体可以出现在同一个 CPU 地址,因此仅靠 16 位指针无法确定跨存储体数据。BankPtr 是紧凑的三字节描述符,包含一字节存储体编号和两字节指针。使用 farptr_make 构造它,再通过 farptr_read8、farptr_read16 或 farptr_read 访问数据。
GB 示例使用 MBC5 卡带:固定存储体 0 的代码始终可通过 $0000–$3FFF 访问,存储体 1、2、3 则映射到 $4000–$7FFF。请将 bank_switch 及调用后继续执行的代码放在固定 ROM 中。查询库保存的编号前,应先用 bank_switch 初始化记录。GB 的 far_call 接受回调指针,而 __farcall 要求函数名。
bank_get_current
返回 bank_switch 最后记录的存储体编号。读取的是软件记录,而不是卡带寄存器。
语法
u8 bank_get_current();返回值
bank_switch 最近保存的 u8 值;初始化前为 0。
使用条件与限制
库记录初值为 0,不会查询卡带启动时的映射。请用 bank_switch 初始化。直接调用切换内置函数或写 mapper 不会更新这份独立记录,因此 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。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:21
Return the last bank recorded by bank_switch; this does not read cartridge hardware.
kitaqgb/lib/bank.c:15
u8 bank_get_current()
{
return kq_bank_current;
}bank_switch
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
选择 ROM 存储体,并在 bank 库和编译器的存储体状态中记录编号。远程读取和调用通过编译器记录恢复原来的映射。
语法
void bank_switch(u8 bank);参数
bank无符号 8 位 ROM 存储体编号。调用时应与函数布局一致,读取时用于选择源映射。请使用目标卡带支持的范围。
返回值
无(void)。
使用条件与限制
持续生效的存储体切换不能使当前代码或返回地址失去映射。请像示例一样,将调用点、所用的 bank_switch 本体及后续代码放在公共或固定存储体 0。
库记录初值为 0,不会查询卡带启动时的映射。请用 bank_switch 初始化。直接调用切换内置函数或写 mapper 不会更新这份独立记录,因此 bank_get_current 无法验证实际硬件映射。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
bank_switch(2); // Select the payload window and update library bookkeeping.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
最初的 bank_switch(2) 记录并选择存储体 2。之后直接切换到存储体 1,从 home1 读到 A1,而 bank_get_current 仍报告 2。切回后,home2 再次读出 B2。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:19
Update both the library bank record and the compiler shadow used by far reads and calls. Invoke this permanent bank change from fixed-bank code.
kitaqgb/lib/bank.c:8
void bank_switch(u8 bank)
{
kq_bank_current = bank;
__bankswitch(bank);
}far_call
将存储体编号和回调指针转交给 __farcall_ptr。此库函数执行无参数回调,并在恢复调用方存储体后返回。
语法
void far_call(u8 bank, const void* func);参数
bank无符号 8 位 ROM 存储体编号。调用时应与函数布局一致,读取时用于选择源映射。请使用目标卡带支持的范围。
func无参数回调的有效可执行地址,可由函数名或运行时指针提供。运行时不会检查空指针或错误的存储体与地址组合。
返回值
无(void)。
使用条件与限制
这些调用不传递用户参数,也不会自动使回调可在中断中重入。请正确管理回调自身的数据、栈和中断使用。恢复存储体的调用路径已测试嵌套和重复调用;此示例不覆盖实机或任意中断交错。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
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。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:30
Invoke a no-argument callback in its ROM bank, then restore the caller bank. The pointer must identify executable code; no arguments are forwarded.
kitaqgb/lib/bank.c:40
void far_call(u8 bank, const void* func)
{
__farcall_ptr(bank, func);
}far_data_read
通过远程内存复制内置函数,将指定 ROM 存储体中的 len 字节复制到调用方提供的 RAM,并在返回前恢复原存储体。
语法
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);参数
bank无符号 8 位 ROM 存储体编号。调用时应与函数布局一致,读取时用于选择源映射。请使用目标卡带支持的范围。
addr指向所选ROM存储体中可读字节的近指针,指针本身不含存储体编号。
dst目标可写内存,容量必须能容纳实际复制的全部字节。除零字节传送外,不会检查null或无效目标地址。
len字节数,范围0–65535。为0时不复制内存,但仍会执行存储体选择和恢复。
返回值
无(void)。
使用条件与限制
整个源范围必须在预期ROM映射中可读,整个目标范围必须可写。复制不会在存储体窗口边界自动切换到下一存储体,不提供memmove式的重叠处理,也不控制VRAM安全写入时机。写VRAM请使用图形传送API。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
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 比较。调用方的源描述符或指针保持不变;完整程序还分别检查零长度复制和存储体恢复。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:27
Copy len bytes from banked ROM into caller-owned writable storage.
kitaqgb/lib/bank.c:33
void far_data_read(u8 bank, const void* addr, void* dst, u16 len)
{
__farmemcpy(dst, bank, addr, len);
}far_data_read16
通过 __farpeek16 读取连续两个字节,组成小端序字。第一个字节对应低 8 位。
语法
u16 far_data_read16(u8 bank, const void* addr);参数
bank无符号 8 位 ROM 存储体编号。调用时应与函数布局一致,读取时用于选择源映射。请使用目标卡带支持的范围。
addr指向所选ROM存储体中可读字节的近指针,指针本身不含存储体编号。
返回值
以 u16(0–65535)返回读到的小端序字。
使用条件与限制
目标字节或字的全部内容必须在选定 CPU 映射中可读。这些读取不会检查指针、裁剪地址,也不会在窗口边界自动切换到下一个 ROM 存储体。读取一个字需要连续两个有效字节。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
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 的映射。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:25
Read a 16-bit value from the supplied bank and address through the far-read intrinsic.
kitaqgb/lib/bank.c:27
u16 far_data_read16(u8 bank, const void* addr)
{
return __farpeek16(bank, addr);
}far_data_read8
通过 __farpeek8 从指定 ROM 存储体和地址读取一个字节,恢复原存储体后返回该值。
语法
u8 far_data_read8(u8 bank, const void* addr);参数
bank无符号 8 位 ROM 存储体编号。调用时应与函数布局一致,读取时用于选择源映射。请使用目标卡带支持的范围。
addr指向所选ROM存储体中可读字节的近指针,指针本身不含存储体编号。
返回值
以 u8(0–255)返回读到的字节。
使用条件与限制
目标字节或字的全部内容必须在选定 CPU 映射中可读。这些读取不会检查指针、裁剪地址,也不会在窗口边界自动切换到下一个 ROM 存储体。读取一个字需要连续两个有效字节。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
byte_value=far_data_read8(3,payload); // One byte: 0xD3.
bank_expect(byte_value==0xD3,5);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此代码片段使用 far_data_read8 读取存储体 3 中的 payload 并检查 D3。完整程序还通过近指针读取 home2,确认已恢复存储体 2 的映射。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:23
Read one byte through the compiler intrinsic that accepts an explicit ROM bank.
kitaqgb/lib/bank.c:21
u8 far_data_read8(u8 bank, const void* addr)
{
return __farpeek8(bank, addr);
}farptr_make
将一个字节的存储体编号和 CPU 可见指针写入调用方的 BankPtr。这里只构造描述符,不切换存储体,也不读取指向的数据。
语法
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);参数
out指向可写入一个三字节 BankPtr 的存储空间。out 为 null 时不执行写入。
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 和三个字节 7A 5C 96。输出为 null 的调用不得改变状态。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:32
Store a bank/address pair without accessing ROM; a null output pointer is ignored.
kitaqgb/lib/bank.c:46
void farptr_make(BankPtr* out, u8 bank, const u8* ptr)
{
if (out == 0) return;
out->bank = bank;
out->ptr = ptr;
}farptr_read
从 BankPtr 指向的位置向可写 RAM 复制 len 字节。描述符按值传递,因此调用方的指针仍指向源数据起点。
语法
void farptr_read(BankPtr ptr, void* dst, u16 len);参数
ptr包含 bank 和 ptr 的 BankPtr 值。传入描述符本身,而不是它的地址。调用方的描述符不会被修改。
dst目标可写内存,容量必须能容纳实际复制的全部字节。除零字节传送外,不会检查null或无效目标地址。
len字节数,范围0–65535。为0时不复制内存,但仍会执行存储体选择和恢复。
返回值
无(void)。
使用条件与限制
整个源范围必须在预期ROM映射中可读,整个目标范围必须可写。复制不会在存储体窗口边界自动切换到下一存储体,不提供memmove式的重叠处理,也不控制VRAM安全写入时机。写VRAM请使用图形传送API。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
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 比较。调用方的源描述符或指针保持不变;完整程序还分别检查零长度复制和存储体恢复。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:38
Copy len bytes from a bank-qualified pointer; dst must have room for the full copy.
kitaqgb/lib/bank.c:66
void farptr_read(BankPtr ptr, void* dst, u16 len)
{
__farmemcpy(dst, ptr.bank, ptr.ptr, len);
}farptr_read16
从 BankPtr 保存的存储体和地址读取小端序字。描述符按值传递,指针不会前移。
语法
u16 farptr_read16(BankPtr ptr);参数
ptr包含 bank 和 ptr 的 BankPtr 值。传入描述符本身,而不是它的地址。调用方的描述符不会被修改。
返回值
以 u16(0–65535)返回读到的小端序字。
使用条件与限制
目标字节或字的全部内容必须在选定 CPU 映射中可读。这些读取不会检查指针、裁剪地址,也不会在窗口边界自动切换到下一个 ROM 存储体。读取一个字需要连续两个有效字节。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
bank_expect(farptr_read16(pointer)==0x5C7A,10);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此代码片段使用 farptr_read16 读取存储体 3 中的 pointer.ptr 并检查 5C7A。完整程序还通过近指针读取 home2,确认已恢复存储体 2 的映射。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:36
Read a 16-bit value using both components of the bank-qualified pointer.
kitaqgb/lib/bank.c:60
u16 farptr_read16(BankPtr ptr)
{
return __farpeek16(ptr.bank, ptr.ptr);
}farptr_read8
使用按值传入的 BankPtr 的两个字段读取一个字节。描述符提供普通近指针不包含的存储体编号。
语法
u8 farptr_read8(BankPtr ptr);参数
ptr包含 bank 和 ptr 的 BankPtr 值。传入描述符本身,而不是它的地址。调用方的描述符不会被修改。
返回值
以 u8(0–255)返回读到的字节。
使用条件与限制
目标字节或字的全部内容必须在选定 CPU 映射中可读。这些读取不会检查指针、裁剪地址,也不会在窗口边界自动切换到下一个 ROM 存储体。读取一个字需要连续两个有效字节。
远程读取和复制是同步操作,会使用编译器共享工作区。操作期间不要让中断执行另一笔存储体传输并覆盖同一工作区。GB 复制辅助代码还使用一个共享 HRAM 位置保存返回存储体。示例从固定代码执行,不设置竞争的存储体传输中断。
这些 GB 存储体接口只携带 8 位编号,不设置 MBC5 的第 9 位。仍须遵守卡带规则,包括 mapper 对编号 0 的处理。示例明确使用 MBC5 存储体 1–3;不要混用未记录的 mapper 直接写入。
使用示例
bank_expect(farptr_read8(pointer)==0x7A,9);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此代码片段使用 farptr_read8 读取存储体 3 中的 pointer.ptr 并检查 7A。完整程序还通过近指针读取 home2,确认已恢复存储体 2 的映射。
完整程序演示如何用存储体编号和近地址确定数据、如何跨存储体调用回调,以及如何区分库记录与实际映射。所有数值均为十六进制。BANK IN 和 BANK OUT 应为 02,BYTE HEX 为 D3,WORD HEX 为 5C7A,FAILED CHECKS 和 FIRST FAILURE 均为 00。CGB 和 FC 显示蓝字,DMG 显示黑字。
CALL COUNT 为 03:far_call、__farcall 和 __farcall_ptr 各执行一次回调。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/bank.h:34
Read one byte using both components of the bank-qualified pointer.
kitaqgb/lib/bank.c:54
u8 farptr_read8(BankPtr ptr)
{
return __farpeek8(ptr.bank, ptr.ptr);
}camera.h — 世界坐标与可见区域
管理 Q8.8 摄像机位置、目标和边界,并进行世界坐标与屏幕坐标之间的转换。可以使用由调用方持有的 Camera8_8 对象,也可以通过小写 API 操作一个共享摄像机。
Camera_Add
将 Q8.8 格式的位移加到当前位置上,然后应用已启用的边界限制。
语法
void Camera_Add(Camera8_8* cam, s16 dfx, s16 dfy);参数
cam调用方持有且已初始化的有效
Camera8_8指针。dfx有符号 Q8.8 位移;256 表示移动一个像素。
dfy有符号 Q8.8 位移;256 表示移动一个像素。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_Add(&cam,256,512);result[13]=cam.fx;result[14]=cam.fy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:34
Add Q8.8 deltas and clamp afterward. Keep the intermediate sums within s16; clamping cannot repair arithmetic that already overflowed.
kitaqgb/lib/camera.c:79
void Camera_Add(Camera8_8* cam, s16 dfx, s16 dfy)
{
cam->fx += dfx;
cam->fy += dfy;
Camera_ApplyBounds(cam);
}camera_apply
立即将共享摄像机的当前位置应用到背景滚动。
语法
void camera_apply();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不等待 VBlank;如果不希望在一帧中途改变画面,请由调用方安排同步。负的小数向下取整:−1/256 会变为 −1 像素,对应寄存器值 255。
使用示例
camera_apply();result[31]=M_SCX;result[32]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:64
Write the shared camera's current integer position to background scroll registers.
kitaqgb/lib/camera.c:179
void camera_apply()
{
Camera_ApplyBg(&camera_default);
}Camera_ApplyBg
将摄像机位置向下取整为整数像素,并立即把低八位写入背景滚动寄存器。
语法
void Camera_ApplyBg(const Camera8_8* cam);参数
cam调用方持有且已初始化的有效
Camera8_8指针。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不等待 VBlank;如果不希望在一帧中途改变画面,请由调用方安排同步。负的小数向下取整:−1/256 会变为 −1 像素,对应寄存器值 255。
使用示例
Camera_ApplyBg(&cam);result[21]=M_SCX;result[22]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:40
Discard fractional bits and write the low eight integer bits to background scroll registers.
kitaqgb/lib/camera.c:109
void Camera_ApplyBg(const Camera8_8* cam)
{
__scroll_bg_set((u8)(cam->fx >> 8), (u8)(cam->fy >> 8));
}Camera_ApplyBgBuffered
将摄像机位置向下取整为整数像素,暂存为待提交的背景滚动坐标。
语法
void Camera_ApplyBgBuffered(const Camera8_8* cam);参数
cam调用方持有且已初始化的有效
Camera8_8指针。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
在调用 Scroll_Flush 或 __scroll_flush 前,硬件和库记录的当前值都不会改变。本调用不等待 VBlank。
使用示例
Camera_Set(&cam,2048,4096);Camera_ApplyBgBuffered(&cam);result[23]=M_SCX;Scroll_Flush();result[24]=M_SCX;result[25]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:42
Convert the Q8.8 position to byte scroll values and stage them for a later flush.
kitaqgb/lib/camera.c:115
void Camera_ApplyBgBuffered(const Camera8_8* cam)
{
__scroll_bg_set_buffered((u8)(cam->fx >> 8), (u8)(cam->fy >> 8));
}camera_clamp
用世界尺寸减去视口尺寸,为共享摄像机设置边界。
语法
void camera_clamp(u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);参数
world_w_px世界尺寸,单位为整数像素;超出视口的正差值应保持在 0–127 范围内。
world_h_px世界尺寸,单位为整数像素;超出视口的正差值应保持在 0–127 范围内。
screen_w_px视口尺寸,单位为整数像素;视口不小于世界时,该轴的可移动距离为零。
screen_h_px视口尺寸,单位为整数像素;视口不小于世界时,该轴的可移动距离为零。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
世界小于视口时,可移动距离为零。正差值达到 128 或更大时,转换结果会超出有符号 Q8.8 的范围。参数类型为 u16,并不表示摄像机可以在任意大的世界中移动。这些调用共用一个摄像机;需要独立摄像机对象时,请使用以大写字母开头的 API。由整数像素转换为 Q8.8 的位置必须在 −128 至 127 范围内。
使用示例
camera_clamp(176,160,160,144);result[29]=camera_world_to_screen_x(100);result[30]=camera_world_to_screen_y(90);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:62
Derive and apply world/screen bounds to the shared convenience camera.
kitaqgb/lib/camera.c:173
void camera_clamp(u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px)
{
Camera_ClampToSize(&camera_default, world_w_px, world_h_px, screen_w_px, screen_h_px);
}Camera_ClampToSize
用世界尺寸减去视口尺寸,以所得的非负值设置摄像机边界。
语法
void Camera_ClampToSize(Camera8_8* cam, u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px);参数
cam调用方持有且已初始化的有效
Camera8_8指针。world_w_px世界尺寸,单位为整数像素;超出视口的正差值应保持在 0–127 范围内。
world_h_px世界尺寸,单位为整数像素;超出视口的正差值应保持在 0–127 范围内。
screen_w_px视口尺寸,单位为整数像素;视口不小于世界时,该轴的可移动距离为零。
screen_h_px视口尺寸,单位为整数像素;视口不小于世界时,该轴的可移动距离为零。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
世界小于视口时,可移动距离为零。正差值达到 128 或更大时,转换结果会超出有符号 Q8.8 的范围。参数类型为 u16,并不表示摄像机可以在任意大的世界中移动。
使用示例
Camera_ClampToSize(&cam,176,160,160,144);result[15]=cam.max_fx;result[16]=cam.max_fy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:46
Derive nonnegative camera extents from world minus screen size, then convert them to Q8.8 bounds. Extents must fit signed Q8.8; the u16 input type does not imply support for arbitrary large worlds.
kitaqgb/lib/camera.c:123
void Camera_ClampToSize(Camera8_8* cam, u16 world_w_px, u16 world_h_px, u8 screen_w_px, u8 screen_h_px)
{
s16 max_x = 0;
s16 max_y = 0;
if (world_w_px > screen_w_px) max_x = (s16)((u16)(world_w_px - screen_w_px) << 8);
if (world_h_px > screen_h_px) max_y = (s16)((u16)(world_h_px - screen_h_px) << 8);
Camera_SetBounds(cam, (s16)0, (s16)0, max_x, max_y);
}Camera_ClearBounds
关闭边界限制,不改变当前位置、目标位置或已保存的边界。
语法
void Camera_ClearBounds(Camera8_8* cam);参数
cam调用方持有且已初始化的有效
Camera8_8指针。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_ClearBounds(&cam);result[12]=cam.use_bounds;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:31
Disable clamping while retaining the stored bounds and current position.
kitaqgb/lib/camera.c:72
void Camera_ClearBounds(Camera8_8* cam)
{
cam->use_bounds = 0;
}Camera_ClearTarget
禁用跟随,保留当前位置和目标。
语法
void Camera_ClearTarget(Camera8_8* cam);参数
cam调用方持有且已初始化的有效
Camera8_8指针。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_ClearTarget(&cam);result[8]=cam.use_target;result[9]=cam.target_fx;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:27
Disable target following without moving the camera or erasing the stored target.
kitaqgb/lib/camera.c:55
void Camera_ClearTarget(Camera8_8* cam)
{
cam->use_target = 0;
}camera_follow_xy
立即移动共享摄像机,使指定世界坐标点出现在所要求的屏幕锚点处。
语法
void camera_follow_xy(s16 x, s16 y, u8 screen_cx, u8 screen_cy);参数
x世界 X 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。y世界 Y 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。screen_cx屏幕锚点坐标,单位为整数像素,范围为 0–255。
screen_cy屏幕锚点坐标,单位为整数像素,范围为 0–255。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标含八位小数。位置、目标以及中间的和与差都必须保持在 s16 范围内。请另行调用 Camera_ApplyBg,或提交缓冲坐标以更新画面。这些调用共用一个摄像机;需要独立摄像机对象时,请使用以大写字母开头的 API。由整数像素转换为 Q8.8 的位置必须在 −128 至 127 范围内。
使用示例
camera_follow_xy(100,90,80,72);result[27]=camera_world_to_screen_x(100);result[28]=camera_world_to_screen_y(90);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:60
Position the shared camera so the world point appears at the requested screen anchor. This is an immediate move, not the smoothed target-following path.
kitaqgb/lib/camera.c:165
void camera_follow_xy(s16 x, s16 y, u8 screen_cx, u8 screen_cy)
{
s16 cx = (s16)(x - screen_cx);
s16 cy = (s16)(y - screen_cy);
Camera_Set(&camera_default, (s16)(cx << 8), (s16)(cy << 8));
}Camera_Init
清零位置、目标和边界,禁用跟随及边界限制。
语法
void Camera_Init(Camera8_8* cam);参数
cam要初始化的可写
Camera8_8,不得为空。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_Init(&cam);result[0]=cam.fx;result[1]=cam.use_target;result[2]=cam.use_bounds;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:21
Zero all position/target/bound fields and disable targeting and bounds.
kitaqgb/lib/camera.c:24
void Camera_Init(Camera8_8* cam)
{
cam->fx = 0;
cam->fy = 0;
cam->target_fx = 0;
cam->target_fy = 0;
cam->min_fx = 0;
cam->min_fy = 0;
cam->max_fx = 0;
cam->max_fy = 0;
cam->use_target = 0;
cam->use_bounds = 0;
}Camera_ScreenToWorldX
将摄像机 X 坐标的整数部分加到整数屏幕 X 坐标上,得到世界 X 坐标。
语法
s16 Camera_ScreenToWorldX(const Camera8_8* cam, s16 screen_x);参数
cam调用方持有且已初始化的有效
Camera8_8指针。screen_x屏幕 X 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
result[19]=Camera_ScreenToWorldX(&cam,27);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:52
Convert integer-pixel screen X back to world X using the camera's integer component.
kitaqgb/lib/camera.c:146
s16 Camera_ScreenToWorldX(const Camera8_8* cam, s16 screen_x)
{
return (s16)(screen_x + (cam->fx >> 8));
}Camera_ScreenToWorldY
将摄像机 Y 坐标的整数部分加到整数屏幕 Y 坐标上,得到世界 Y 坐标。
语法
s16 Camera_ScreenToWorldY(const Camera8_8* cam, s16 screen_y);参数
cam调用方持有且已初始化的有效
Camera8_8指针。screen_y屏幕 Y 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
result[20]=Camera_ScreenToWorldY(&cam,24);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:54
Convert integer-pixel screen Y back to world Y using the camera's integer component.
kitaqgb/lib/camera.c:152
s16 Camera_ScreenToWorldY(const Camera8_8* cam, s16 screen_y)
{
return (s16)(screen_y + (cam->fy >> 8));
}Camera_Set
替换当前相机位置,并立即限制到已启用的边界内。
语法
void Camera_Set(Camera8_8* cam, s16 fx, s16 fy);参数
cam调用方持有且已初始化的有效
Camera8_8指针。fxX 坐标,采用有符号 Q8.8:256 表示一像素,范围 −128~127+255/256 像素。
fyY 坐标,采用有符号 Q8.8:256 表示一像素,范围 −128~127+255/256 像素。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_Set(&cam,2560,5120);result[3]=cam.fx;result[4]=cam.fy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:23
Replace the current Q8.8 position and immediately apply enabled bounds.
kitaqgb/lib/camera.c:39
void Camera_Set(Camera8_8* cam, s16 fx, s16 fy)
{
cam->fx = fx;
cam->fy = fy;
Camera_ApplyBounds(cam);
}camera_set
用整数像素坐标移动共享摄像机,并应用已启用的边界限制。
语法
void camera_set(s16 x, s16 y);参数
x世界 X 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。y世界 Y 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标含八位小数。位置、目标以及中间的和与差都必须保持在 s16 范围内。请另行调用 Camera_ApplyBg,或提交缓冲坐标以更新画面。这些调用共用一个摄像机;需要独立摄像机对象时,请使用以大写字母开头的 API。由整数像素转换为 Q8.8 的位置必须在 −128 至 127 范围内。
使用示例
camera_set(5,6);result[26]=camera_world_to_screen_x(20);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:57
Set the shared camera from integer pixels, converting to the representable Q8.8 range.
kitaqgb/lib/camera.c:158
void camera_set(s16 x, s16 y)
{
Camera_Set(&camera_default, (s16)(x << 8), (s16)(y << 8));
}Camera_SetBounds
设置最小/最大坐标,启用边界限制,并立即限制当前位置。
语法
void Camera_SetBounds(Camera8_8* cam, s16 min_fx, s16 min_fy, s16 max_fx, s16 max_fy);参数
cam调用方持有且已初始化的有效
Camera8_8指针。min_fx最小 X 坐标,使用有符号 Q8.8:256 为一像素,范围 −128~127+255/256 像素。
min_fy最小 Y 坐标,使用有符号 Q8.8:256 为一像素,范围 −128~127+255/256 像素。
max_fx最大 X 坐标,使用有符号 Q8.8:256 为一像素,范围 −128~127+255/256 像素。
max_fy最大 Y 坐标,使用有符号 Q8.8:256 为一像素,范围 −128~127+255/256 像素。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
各轴须满足最小值≤最大值,函数不重排边界。保存的目标不受限制。
使用示例
Camera_SetBounds(&cam,0,0,3072,3584);result[10]=cam.fx;result[11]=cam.fy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:29
Install ordered Q8.8 bounds, enable them and clamp the current position immediately.
kitaqgb/lib/camera.c:61
void Camera_SetBounds(Camera8_8* cam, s16 min_fx, s16 min_fy, s16 max_fx, s16 max_fy)
{
cam->min_fx = min_fx;
cam->min_fy = min_fy;
cam->max_fx = max_fx;
cam->max_fy = max_fy;
cam->use_bounds = 1;
Camera_ApplyBounds(cam);
}Camera_SetTarget
保存目标并启用跟随,不立即移动当前位置。
语法
void Camera_SetTarget(Camera8_8* cam, s16 fx, s16 fy);参数
cam调用方持有且已初始化的有效
Camera8_8指针。fxX 坐标,采用有符号 Q8.8:256 表示一像素,范围 −128~127+255/256 像素。
fyY 坐标,采用有符号 Q8.8:256 表示一像素,范围 −128~127+255/256 像素。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
Camera_SetTarget(&cam,4608,3072);result[5]=cam.use_target;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:25
Store a Q8.8 target and enable subsequent step-toward-target updates.
kitaqgb/lib/camera.c:47
void Camera_SetTarget(Camera8_8* cam, s16 fx, s16 fy)
{
cam->target_fx = fx;
cam->target_fy = fy;
cam->use_target = 1;
}Camera_StepTowardTarget
将剩余距离右移后作为本次位移,逐步靠近目标;未启用跟随时不执行任何操作。
语法
void Camera_StepTowardTarget(Camera8_8* cam, u8 shift);参数
cam调用方持有且已初始化的有效
Camera8_8指针。shift用于除以 2 的幂的右移位数:0 表示使用全部位移,1 表示使用一半。大于 7 的值按 7 处理。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
较小的正位移可能被截断为零;较小的负位移经过算术右移后可能变为 −1。是否到达目标是在限制边界之前判断的。因此,目标位于边界外时,跟随状态可能一直保持,也可能被清除,即使最终受限后的位置并不等于目标位置。
使用示例
Camera_StepTowardTarget(&cam,1);result[6]=cam.fx;result[7]=cam.fy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:38
Move by the signed target delta shifted by 0..7 bits, then apply bounds. Quantization can make a small delta produce no movement; only exact equality clears targeting, and a target outside the bounds may remain active indefinitely.
kitaqgb/lib/camera.c:89
void Camera_StepTowardTarget(Camera8_8* cam, u8 shift)
{
s16 dx;
s16 dy;
if (cam->use_target == 0) return;
if (shift > 7) shift = 7;
// Keep target-minus-position differences within s16 as well as the individual positions.
dx = cam->target_fx - cam->fx;
dy = cam->target_fy - cam->fy;
if (dx != 0) cam->fx += (dx >> shift);
if (dy != 0) cam->fy += (dy >> shift);
if (cam->fx == cam->target_fx && cam->fy == cam->target_fy) cam->use_target = 0;
Camera_ApplyBounds(cam);
}camera_world_to_screen_x
从世界 X 坐标中减去共享摄像机 X 坐标的整数部分,得到屏幕 X 坐标。
语法
s16 camera_world_to_screen_x(s16 world_x);参数
world_x世界 X 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标含八位小数。位置、目标以及中间的和与差都必须保持在 s16 范围内。请另行调用 Camera_ApplyBg,或提交缓冲坐标以更新画面。这些调用共用一个摄像机;需要独立摄像机对象时,请使用以大写字母开头的 API。由整数像素转换为 Q8.8 的位置必须在 −128 至 127 范围内。
使用示例
result[33]=camera_world_to_screen_x(20);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:66
Convert integer-pixel world X using the shared convenience camera.
kitaqgb/lib/camera.c:185
s16 camera_world_to_screen_x(s16 world_x)
{
return Camera_WorldToScreenX(&camera_default, world_x);
}camera_world_to_screen_y
从世界 Y 坐标中减去共享摄像机 Y 坐标的整数部分,得到屏幕 Y 坐标。
语法
s16 camera_world_to_screen_y(s16 world_y);参数
world_y世界 Y 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标含八位小数。位置、目标以及中间的和与差都必须保持在 s16 范围内。请另行调用 Camera_ApplyBg,或提交缓冲坐标以更新画面。这些调用共用一个摄像机;需要独立摄像机对象时,请使用以大写字母开头的 API。由整数像素转换为 Q8.8 的位置必须在 −128 至 127 范围内。
使用示例
result[34]=camera_world_to_screen_y(20);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:68
Convert integer-pixel world Y using the shared convenience camera.
kitaqgb/lib/camera.c:191
s16 camera_world_to_screen_y(s16 world_y)
{
return Camera_WorldToScreenY(&camera_default, world_y);
}Camera_WorldToScreenX
从整数世界 X 坐标中减去摄像机 X 坐标的整数部分,得到屏幕 X 坐标。
语法
s16 Camera_WorldToScreenX(const Camera8_8* cam, s16 world_x);参数
cam调用方持有且已初始化的有效
Camera8_8指针。world_x世界 X 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
result[17]=Camera_WorldToScreenX(&cam,40);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:48
Convert an integer-pixel world X by subtracting the camera's integer X component.
kitaqgb/lib/camera.c:134
s16 Camera_WorldToScreenX(const Camera8_8* cam, s16 world_x)
{
return (s16)(world_x - (cam->fx >> 8));
}Camera_WorldToScreenY
从整数世界 Y 坐标中减去摄像机 Y 坐标的整数部分,得到屏幕 Y 坐标。
语法
s16 Camera_WorldToScreenY(const Camera8_8* cam, s16 world_y);参数
cam调用方持有且已初始化的有效
Camera8_8指针。world_y世界 Y 坐标,单位为整数像素;结果及中间差值都必须能用
s16表示。
返回值
以 s16 返回转换后的整数像素坐标;不判断是否可见,也不进行裁剪。
使用条件与限制
坐标包含八位小数,位置、目标及中间和差须在 s16 范围内。显示通过 Camera_ApplyBg 或缓冲提交另行更新。
使用示例
result[18]=Camera_WorldToScreenY(&cam,40);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习 Q8.8 跟随、边界限制和坐标转换。跟随后 X/Y=3584/4096;应用边界后为 3072/3584。背景 X 在等待提交时仍为 13,提交后变为 8。附图展示摄像机位于 (8,16) 时,如何将世界坐标 (32,32) 处的 32×24 矩形移到屏幕坐标 (24,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/camera.h:50
Convert an integer-pixel world Y by subtracting the camera's integer Y component.
kitaqgb/lib/camera.c:140
s16 Camera_WorldToScreenY(const Camera8_8* cam, s16 world_y)
{
return (s16)(world_y - (cam->fy >> 8));
}cgb_palette.h — CGB 背景与 OBJ 调色板
cgb_palette.h和cgb_palette.c提供RGB15打包及CGB调色板即时更新。背景/窗口用BG函数,精灵用OBJ函数。单色及RGB分量函数修改一色,范围函数写入连续颜色字,整组调色板函数写入4个字。raw范围函数使用绝对颜色槽位,寄存器内置函数使用字节位置。通过地图属性或OAM另行指定调色板归属。请像完整示例一样,只将实现包含到程序中一次。
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
cgb_bg_color
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
立即向背景/窗口调色板写入一个打包的RGB15颜色。用调色板编号和颜色编号选择32个颜色槽位之一;不修改图块数据,也不改变图块所选的调色板。
语法
void cgb_bg_color(u8 palette_index, u8 color_index, u16 rgb15);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。rgb15打包的15位RGB颜色,通常为0~32767。先写入所给
u16的低字节,再写高字节;库不会显式清除位15。可用CGB_RGB15或cgb_rgb15构造有效值。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_bg_color(1,3,31); // Red background color 3 of palette 1.
expect_color(0,7,31);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:22
Set one background color through the indexed palette wrapper.
kitaqgb/lib/cgb_palette.c:110
void cgb_bg_color(u8 palette_index, u8 color_index, u16 rgb15)
{
cgb__write_color_bg_raw(cgb__palette_color_slot(palette_index, color_index), rgb15);
}cgb_bg_colors
从指定的调色板及起始颜色编号开始,向背景/窗口调色板连续写入RGB15颜色。可以跨越每组4色的边界,但会在整个32色存储区的末尾停止。
语法
void cgb_bg_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。start_color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。color_count颜色字数量,而非字节数,以
u8(0~255)传入。0表示不操作。实际数量最多为32 - 归一化后的起始槽位,源数组也只读取这么多个字。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_bg_colors(3,2,tail_green,2); // Two color words; color 3 becomes green.
expect_color(0,14,0); expect_color(0,15,992);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:32
Write background colors starting at the indexed slot. The range may cross palette boundaries and is clamped only at the end of all 32 slots.
kitaqgb/lib/cgb_palette.c:135
void cgb_bg_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count)
{
cgb__write_colors_bg_raw(cgb__palette_color_slot(palette_index, start_color_index), colors, color_count);
}cgb_bg_colors_raw
使用绝对颜色槽位编号,向背景/窗口调色板连续写入RGB15值。起点是0~31的单个编号,而非调色板编号与颜色编号的组合。输入为颜色字数组,不是原始图块字节。
语法
void cgb_bg_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);参数
start_color_slot绝对颜色槽位0~31,先用
& 31归一化,再限制写入数量。槽位=调色板编号×4+颜色编号,不是字节偏移。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。color_count颜色字数量,而非字节数,以
u8(0~255)传入。0表示不操作。实际数量最多为32 - 归一化后的起始槽位,源数组也只读取这么多个字。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_bg_colors_raw(23,blue_word,1); // Absolute slot 23 = palette 5, color 3.
expect_color(0,23,31744);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:42
Expose sequential background writes using an absolute color-slot index.
kitaqgb/lib/cgb_palette.c:159
void cgb_bg_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count)
{
cgb__write_colors_bg_raw(start_color_slot, colors, color_count);
}cgb_bg_palette
用4个RGB15值设置一组背景/窗口调色板的全部4色。相当于从颜色0开始写入4色;不会替图块或精灵选择该调色板。
语法
void cgb_bg_palette(u8 palette_index, const u16* colors);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_bg_palette(4,red_palette); // Exactly four color words for BG palette 4.
expect_color(0,16,32767); expect_color(0,19,31);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:37
Write four consecutive colors for one background palette.
kitaqgb/lib/cgb_palette.c:147
void cgb_bg_palette(u8 palette_index, const u16* colors)
{
cgb_bg_colors(palette_index, 0, colors, 4);
}cgb_bg_rgb
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
用cgb_rgb15打包红、绿、蓝分量,再向背景/窗口调色板写入一个颜色。适用于分别提供三个分量,而非打包颜色值的情况。
语法
void cgb_bg_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。r5, g5, b5依次为红、绿、蓝分量,通常各为0~31。使用掩码,不进行饱和处理;宏展开中每个参数出现一次。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_bg_rgb(2,3,0,0,31); // Blue, supplied as three five-bit components.
expect_color(0,11,31744);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:26
Pack component values and write one background color.
kitaqgb/lib/cgb_palette.c:122
void cgb_bg_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5)
{
cgb_bg_color(palette_index, color_index, cgb_rgb15(r5, g5, b5));
}cgb_obj_color
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
立即向精灵(OBJ)调色板写入一个打包的RGB15颜色。用调色板编号和颜色编号选择32个颜色槽位之一;不修改图块数据,也不改变图块所选的调色板。
语法
void cgb_obj_color(u8 palette_index, u8 color_index, u16 rgb15);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。rgb15打包的15位RGB颜色,通常为0~32767。先写入所给
u16的低字节,再写高字节;库不会显式清除位15。可用CGB_RGB15或cgb_rgb15构造有效值。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_obj_color(1,3,31744); // Blue sprite color at the same palette/color indices.
expect_color(1,7,31744);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:24
Set one object color through the indexed palette wrapper.
kitaqgb/lib/cgb_palette.c:116
void cgb_obj_color(u8 palette_index, u8 color_index, u16 rgb15)
{
cgb__write_color_obj_raw(cgb__palette_color_slot(palette_index, color_index), rgb15);
}cgb_obj_colors
从指定的调色板及起始颜色编号开始,向精灵(OBJ)调色板连续写入RGB15颜色。可以跨越每组4色的边界,但会在整个32色存储区的末尾停止。
语法
void cgb_obj_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。start_color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。color_count颜色字数量,而非字节数,以
u8(0~255)传入。0表示不操作。实际数量最多为32 - 归一化后的起始槽位,源数组也只读取这么多个字。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_obj_colors(3,2,tail_red,2); // Sprite color 3 becomes red.
expect_color(1,14,0); expect_color(1,15,31);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:34
Write object colors from the indexed slot, potentially crossing into later palettes.
kitaqgb/lib/cgb_palette.c:141
void cgb_obj_colors(u8 palette_index, u8 start_color_index, const u16* colors, u8 color_count)
{
cgb__write_colors_obj_raw(cgb__palette_color_slot(palette_index, start_color_index), colors, color_count);
}cgb_obj_colors_raw
使用绝对颜色槽位编号,向精灵(OBJ)调色板连续写入RGB15值。起点是0~31的单个编号,而非调色板编号与颜色编号的组合。输入为颜色字数组,不是原始图块字节。
语法
void cgb_obj_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count);参数
start_color_slot绝对颜色槽位0~31,先用
& 31归一化,再限制写入数量。槽位=调色板编号×4+颜色编号,不是字节偏移。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。color_count颜色字数量,而非字节数,以
u8(0~255)传入。0表示不操作。实际数量最多为32 - 归一化后的起始槽位,源数组也只读取这么多个字。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_obj_colors_raw(23,green_word,1); // Same absolute slot in the separate OBJ store.
expect_color(1,23,992);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:44
Expose sequential object writes using an absolute color-slot index.
kitaqgb/lib/cgb_palette.c:165
void cgb_obj_colors_raw(u8 start_color_slot, const u16* colors, u8 color_count)
{
cgb__write_colors_obj_raw(start_color_slot, colors, color_count);
}cgb_obj_palette
用4个RGB15值设置一组精灵(OBJ)调色板的全部4色。相当于从颜色0开始写入4色;不会替图块或精灵选择该调色板。
语法
void cgb_obj_palette(u8 palette_index, const u16* colors);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。colors指向所需颜色字的可读
const u16*。调用期间立即读取,不加入队列。源数组及其ROM存储体必须保持可访问;不会自动切换存储体。空指针不产生写入。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_obj_palette(4,blue_palette); // Exactly four words; OBJ color zero is transparent on screen.
expect_color(1,16,32767); expect_color(1,19,31744);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:39
Write four consecutive colors for one object palette.
kitaqgb/lib/cgb_palette.c:153
void cgb_obj_palette(u8 palette_index, const u16* colors)
{
cgb_obj_colors(palette_index, 0, colors, 4);
}cgb_obj_rgb
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
用cgb_rgb15打包红、绿、蓝分量,再向精灵(OBJ)调色板写入一个颜色。适用于分别提供三个分量,而非打包颜色值的情况。
语法
void cgb_obj_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5);参数
palette_index调色板编号0~7,只使用低3位(
& 7)。color_index所选调色板内的颜色编号0~3,只使用低2位(
& 3)。r5, g5, b5依次为红、绿、蓝分量,通常各为0~31。使用掩码,不进行饱和处理;宏展开中每个参数出现一次。
返回值
无(void)。
使用条件与限制
调色板操作和带模式保护的寄存器写入在DMG模式下不写入任何内容。这里的safe指CGB模式检查,不代表等待PPU可访问或禁止中断。请在LCD关闭时设置,或在可访问的PPU阶段内完成足够短的更新。函数不等待VBlank,也不保护多字节更新免受中断影响。
背景/窗口和OBJ调色板属于独立存储区。库的颜色写入会启用索引自动递增,并保留修改后的索引寄存器。OBJ颜色编号0虽可存储和读取,但绘制精灵时仍为透明。
使用示例
cgb_obj_rgb(2,3,0,31,0); // Green sprite color.
expect_color(1,11,992);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:28
Pack component values and write one object color.
kitaqgb/lib/cgb_palette.c:128
void cgb_obj_rgb(u8 palette_index, u8 color_index, u8 r5, u8 g5, u8 b5)
{
cgb_obj_color(palette_index, color_index, cgb_rgb15(r5, g5, b5));
}CGB_RGB15
将RGB分量打包为u16:红色占位0~4,绿色占位5~9,蓝色占位10~14,位15为0。每个分量与31进行按位与,超出范围时只保留低5位,而非限制到上限。只计算数值,不写硬件;DMG和CGB均可使用。
语法
#define CGB_RGB15(r5, g5, b5) ((u16)((u16)((r5) & 31) | ((u16)((g5) & 31) << 5) | ((u16)((b5) & 31) << 10)))参数
r5, g5, b5依次为红、绿、蓝分量,通常各为0~31。使用掩码,不进行饱和处理;宏展开中每个参数出现一次。
返回值
打包的RGB15值,类型为u16,范围0~32767。
使用示例
if (CGB_RGB15(component,64,95)!=31775) failures++; // (31,0,31), not saturation.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:16
cgb_rgb15
将RGB分量打包为u16:红色占位0~4,绿色占位5~9,蓝色占位10~14,位15为0。每个分量与31进行按位与,超出范围时只保留低5位,而非限制到上限。只计算数值,不写硬件;DMG和CGB均可使用。
语法
u16 cgb_rgb15(u8 r5, u8 g5, u8 b5);参数
r5, g5, b5依次为红、绿、蓝分量,通常各为0~31。使用掩码,不进行饱和处理;宏展开中每个参数出现一次。
返回值
打包的RGB15值,类型为u16,范围0~32767。
使用示例
if (cgb_rgb15(component,64,95)!=31775) failures++; // Same packed value through a function.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_palette.h:19
Pack the red, green and blue components using the public RGB15 macro.
kitaqgb/lib/cgb_palette.c:104
u16 cgb_rgb15(u8 r5, u8 g5, u8 b5)
{
return CGB_RGB15(r5, g5, b5);
}cgb_tile.h — CGB 图块编号与属性
cgb_tile.h 集中了图块地图内建函数的声明,以及构造 CGB 图块属性的宏。图块编号选择图案;属性选择调色板、图案所在的 VRAM 存储体、翻转方式和背景优先级。设置属性不会改变图案的像素。
__settile 系列访问地图 0x9800;__settileat 使用明确指定的地图基地址;__settilebg 使用 LCDC 选择的背景地图;__settilewin 使用 LCDC 选择的窗口地图。attr 形式只写属性,cgb 形式同时写图块编号和属性。bulk 形式在给定地址传送连续字节。请根据目标位置和要写入的数据选择函数。
使用 KQ_CGB_ATTR_PAL 等宏设置单个属性字段,或用 KQ_CGB_ATTR 组合各字段。bank 字段选择存放图案的 VRAM 存储体,而不是 ROM 存储体。普通图块编号写入使用当前选择的 VRAM 存储体,因此执行图块/属性组合操作前,应先选择存储体零。在 DMG 上,仅属性操作不产生效果;组合操作仍会写入图块编号。
编译器会生成 __ 内建函数的实现,无需链接 cgb_tile.c。普通写入路径会等待 VRAM 可访问,但部分路径也会改变中断启用状态。_unsafe 和 _fast 形式省略等待:仅在调用方保证 VRAM 可写时使用,例如 LCD 关闭期间。请查看每个条目的中断、存储体和传送长度条件。
KQ_CGB_ATTR
用调色板、图块数据存储体、水平翻转、垂直翻转和BG优先级构造完整的CGB地图属性字节。调色板占位0~2,四个布尔字段依次设置位3、5、6、7,位4保持0。
语法
#define KQ_CGB_ATTR(palette, bank, xflip, yflip, priority) ((u8)(((palette) & KQ_CGB_ATTR_PAL_MASK) | ((bank) ? KQ_CGB_ATTR_BANK1 : 0) | ((xflip) ? KQ_CGB_ATTR_XFLIP : 0) | ((yflip) ? KQ_CGB_ATTR_YFLIP : 0) | ((priority) ? KQ_CGB_ATTR_PRIORITY : 0)))参数
palette调色板编号0~7,只使用低3位(
& 7)。bank, xflip, yflip, priority0表示关闭,任意非零值表示启用。特别是2也会启用:这些宏判断是否非零,而非只检查最低位。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
构造CGB地图属性的位3:bank非零时返回0x08,否则为0。选择显示图块图案所用的VRAM存储体,不写VBK,也不改变CPU访问的存储体。
构造水平翻转标志:enabled非零时为0x20,否则为0。用于CGB地图属性时,图块每行的8个像素按相反顺序显示,不修改保存的图案。
构造垂直翻转标志:enabled非零时为0x40,否则为0。用于CGB地图属性时,反向显示图块的8行,不改变保存的图案。
构造BG优先于OBJ的标志:enabled非零时为0x80,否则为0。CGB总优先级控制(LCDC位0)启用时,BG非零颜色编号的像素覆盖精灵。BG颜色编号0仍允许精灵显示,不会使整个图块不透明。
使用示例
attr=KQ_CGB_ATTR(10,0,2,2,0); // Palette 2, bank 0, both reflections: 0x62.
if (attr!=98 || KQ_CGB_ATTR(10,2,3,4,5)!=234) failures++;
cell(10,11,1,attr);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:23
KQ_CGB_ATTR_BANK
构造CGB地图属性的位3:bank非零时返回0x08,否则为0。选择显示图块图案所用的VRAM存储体,不写VBK,也不改变CPU访问的存储体。
语法
#define KQ_CGB_ATTR_BANK(bank) ((u8)((bank) ? KQ_CGB_ATTR_BANK1 : 0))参数
bank0表示关闭,任意非零值表示启用。特别是2也会启用:这些宏判断是否非零,而非只检查最低位。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
使用示例
attr=(u8)(2|KQ_CGB_ATTR_BANK(enabled)); // Any nonzero value selects tile-data bank 1.
if (attr!=10) failures++;
cell(4,11,1,attr); // Blue hollow square, unlike the bank-0 triangle.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:19
KQ_CGB_ATTR_PAL
用n & 7构造CGB地图属性的调色板编号字段,结果为u8。可用按位或与其他属性标志组合。
语法
#define KQ_CGB_ATTR_PAL(n) ((u8)((n) & KQ_CGB_ATTR_PAL_MASK))参数
n调色板编号0~7,只使用低3位(
& 7)。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
使用示例
attr=KQ_CGB_ATTR_PAL(9); // 9 & 7 = palette 1, red in this BG example.
if (attr!=1) failures++;
cell(2,11,1,attr);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:18
KQ_CGB_ATTR_PRIORITY_IF
构造BG优先于OBJ的标志:enabled非零时为0x80,否则为0。CGB总优先级控制(LCDC位0)启用时,BG非零颜色编号的像素覆盖精灵。BG颜色编号0仍允许精灵显示,不会使整个图块不透明。
语法
#define KQ_CGB_ATTR_PRIORITY_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_PRIORITY : 0))参数
enabled0表示关闭,任意非零值表示启用。特别是2也会启用:这些宏判断是否非零,而非只检查最低位。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
使用示例
attr=(u8)(3|KQ_CGB_ATTR_PRIORITY_IF(enabled)); // Keep nonzero BG pixels in front of sprites.
if (attr!=131) failures++;
cell(12,11,1,attr);
sprite(7,96,88,2,3); // Red full tile behind the green priority triangle.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:22
KQ_CGB_ATTR_XFLIP_IF
构造水平翻转标志:enabled非零时为0x20,否则为0。用于CGB地图属性时,图块每行的8个像素按相反顺序显示,不修改保存的图案。
语法
#define KQ_CGB_ATTR_XFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_XFLIP : 0))参数
enabled0表示关闭,任意非零值表示启用。特别是2也会启用:这些宏判断是否非零,而非只检查最低位。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
使用示例
attr=(u8)(3|KQ_CGB_ATTR_XFLIP_IF(enabled));
if (attr!=35) failures++;
cell(6,11,1,attr); // Green triangle reflected horizontally.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:20
KQ_CGB_ATTR_YFLIP_IF
构造垂直翻转标志:enabled非零时为0x40,否则为0。用于CGB地图属性时,反向显示图块的8行,不改变保存的图案。
语法
#define KQ_CGB_ATTR_YFLIP_IF(enabled) ((u8)((enabled) ? KQ_CGB_ATTR_YFLIP : 0))参数
enabled0表示关闭,任意非零值表示启用。特别是2也会启用:这些宏判断是否非零,而非只检查最低位。
返回值
按上述位布局生成的属性字段或组合属性字节,类型为u8。
使用条件与限制
这些宏只计算u8值,不写VRAM或寄存器。每个参数出现一次。通过CGB属性写入API,或明确写入存储体1的地图来应用结果。DMG显示不使用CGB地图属性。
使用示例
attr=(u8)(1|KQ_CGB_ATTR_YFLIP_IF(enabled));
if (attr!=65) failures++;
cell(8,11,1,attr); // Red triangle reflected vertically.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本程序演示同一图块图案如何分别使用BG和OBJ的调色板。每个三角形为8×8像素,从顶行左侧1个像素逐行扩展到底行8个像素。在x=16、32、48、64、80、96、112处,y=24的BG行依次为红、蓝、绿、红、蓝、绿、红;y=64的OBJ行依次为蓝、绿、红、蓝、绿、红、蓝。各列依次使用单色、RGB分量、索引范围、整组调色板、绝对范围、标准寄存器名及寄存器别名写入。
y=88处的7个8×8格依次显示:红色三角形(x=16)、存储体1中线宽1像素的蓝色方框(32)、水平翻转的绿色三角形(48)、垂直翻转的红色三角形(64)、双向翻转的蓝色三角形(80)、位于红色精灵前方且颜色0区域透出红色的绿色三角形(96),以及关闭BG优先级后覆盖三角形的实心红色精灵(112)。坐标以屏幕左上角为原点,单位为像素。空白处为白色,文字为黑色。
FAILED CHECKS 000表示RGB掩码、含输入2的布尔标志、调色板索引回绕、跨调色板写入、在槽位31截断、null/零长度不写入,以及LCD关闭时的颜色读回检查均通过。测试4种组合:双模式ROM分别在DMG和CGB运行、DMG专用ROM在DMG运行、CGB专用ROM在CGB运行。DMG下CGB MODE为0,三角形均为黑色,存储体及翻转属性不影响显示,最下行最后两格为黑色方块。CGB测试中CGB MODE为1。未测试实机及LCD显示期间的调色板访问冲突。




构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/cgb_tile.h:21
chain.h — 位置历史环形缓冲
分节蛇身或绳索使用 ChainBody,位置历史使用 Chain。身体各节保存当前位置及 16 档朝向,弯曲每次更新向后传播一节。支持边界回绕、复制尾节增长及每轴最多两像素的位置修正。
chain_wrap_delta
在首尾相接的坐标轴上,计算从当前位置到目标位置的最短带符号位移。可用于跨越屏幕边缘时的跟随或距离比较。
语法
s16 chain_wrap_delta(u8 target, u8 current, u16 size);参数
target目标坐标,范围为 0~size−1。
current当前坐标,范围为 0~size−1。
size回绕坐标轴的长度,范围为 1~256;X 轴使用宽度,Y 轴使用高度。该参数为 16 位,以便表示 256。
返回值
以 s16 返回最短位移:正值表示坐标增大的方向,负值表示减小的方向,零表示位置相同。有效输入的结果位于 −128~+128。屏幕坐标中,向右和向下的位移为正。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。无需初始化 Chain 或 ChainBody。函数只计算位移,不修改坐标数组、节数或画面。
函数不检查参数范围,也不将输入自动归一化。不要传入 size=0、size>256 或大于等于 size 的坐标。从更大的坐标系取得数值时,先将其回绕到 0~size−1,再调用本函数。
若两个方向的路程相同,保留直接计算的 target−current 的符号。size=160 时,0→80 返回 +80,80→0 返回 −80。size=1 时,唯一有效坐标为零,结果也为零。
使用示例
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。前导零用于补齐两位十进制数。本例不绘制或移动物体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_wrap.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_wrap.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:35
Return target minus current along the shortest wrapped route. Supply size 1..256 and coordinates in 0..size-1; inputs are not normalized. Exactly half a field retains the direct displacement sign.
kitaqgb/lib/chain.c:98
s16 chain_wrap_delta(u8 target, u8 current, u16 size)
{
s16 delta;
s16 half;
delta = (s16)((s16)target - (s16)current);
half = (s16)(size >> 1);
// At exactly half a field, retain the sign of the direct displacement.
if (delta > half) delta = (s16)(delta - (s16)size);
else if (delta < (s16)(0 - half)) delta = (s16)(delta + (s16)size);
return delta;
}chain_body_init
绑定调用方持有的三个数组,将节数设为零;函数本身不分配内存。
语法
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height);参数
body目标
ChainBody;传入空指针时不处理。除初始化函数外,其他函数都要求传入已初始化的状态。x保存各节 X 坐标的数组。
y保存各节 Y 坐标的数组。
heading保存各节朝向的数组。
capacity已分配的数组长度,范围为 1~255。
width坐标回绕区域的宽度,范围为 1~256。
height坐标回绕区域的高度,范围为 1~256。
返回值
成功返回 1,参数无效返回 0。若 body 非空但初始化失败,会将 capacity 和 count 设为零,以禁用后续更新。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。ChainBody 保存各节当前的位置和朝向;它与保存坐标历史环形缓冲的 Chain 是不同的状态类型。
调用方须提供 x、y、heading 三个互不重叠的可写数组,每个数组包含 capacity 字节,且在使用身体数据期间始终有效。索引 0 为头部,计入 count。初始化后不要直接修改存储指针、容量或场地尺寸。
朝向按顺时针分为 16 档:0 向右、4 向下、8 向左、12 向上。输入仅使用低四位。屏幕坐标向右、向下递增,宽和高各为 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.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图中从上到下依次为直线姿态、向下转弯后更新一次的姿态,以及转弯后更新四次的姿态。绘制时将逻辑坐标放大两倍,弯曲从右端的头部向后传递。CGB 和 FC 画面中头部为红色、身体为蓝色、尾部为绿色,DMG 使用黑色。增长时重叠的尾节先经过四次更新分离,再进行比较。图中是同一条八节身体在三个时刻的状态,并非一条二十四节的身体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:53
Attach three non-overlapping capacity-byte arrays. Return 1 on success. Invalid storage, zero capacity or dimensions outside 1..256 disable the body.
kitaqgb/lib/chain.c:119
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height)
{
if (body == 0) return 0;
body->x = x;
body->y = y;
body->heading = heading;
body->count = 0;
body->capacity = 0;
body->width = width;
body->height = height;
if (x == 0 || y == 0 || heading == 0 || capacity == 0) return 0;
if (width == 0 || width > 256 || height == 0 || height > 256) return 0;
body->capacity = capacity;
return 1;
}chain_body_reset
从指定的头部位置向后排列各节,建立直线初始姿态。
语法
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading);参数
body目标
ChainBody;传入空指针时不处理。除初始化函数外,其他函数都要求传入已初始化的状态。count包含头部的节数,范围为 1~capacity。
head_x头部 X 坐标,按区域宽度回绕。
head_y头部 Y 坐标,按区域高度回绕。
heading头部在 16 档方向中的朝向。
返回值
成功返回 1。节数为零、超过容量或 body 为空时返回 0,且不改变姿态。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。ChainBody 保存各节当前的位置和朝向;它与保存坐标历史环形缓冲的 Chain 是不同的状态类型。
调用方须提供 x、y、heading 三个互不重叠的可写数组,每个数组包含 capacity 字节,且在使用身体数据期间始终有效。索引 0 为头部,计入 count。初始化后不要直接修改存储指针、容量或场地尺寸。
朝向按顺时针分为 16 档:0 向右、4 向下、8 向左、12 向上。输入仅使用低四位。屏幕坐标向右、向下递增,宽和高各为 1~256 像素。
水平和竖直方向的间距为四像素。其他方向使用 (4,1)、(3,3)、(1,4) 等整数向量,因此实际直线距离不一定恰好为四像素。初始姿态也可以跨越区域边缘。
使用示例
chain_body_reset(&body,7,48,16,0); // Seven joints, facing right.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图中从上到下依次为直线姿态、向下转弯后更新一次的姿态,以及转弯后更新四次的姿态。绘制时将逻辑坐标放大两倍,弯曲从右端的头部向后传递。CGB 和 FC 画面中头部为红色、身体为蓝色、尾部为绿色,DMG 使用黑色。增长时重叠的尾节先经过四次更新分离,再进行比较。图中是同一条八节身体在三个时刻的状态,并非一条二十四节的身体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:57
Seed count joints in a straight pose behind the head; count includes the head. Return 0 without changing the pose if count is zero or exceeds capacity. Coordinates wrap to the playfield and headings are masked to 0..15.
kitaqgb/lib/chain.c:135
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading)
{
u8 i;
u8 x;
u8 y;
s16 ox;
s16 oy;
if (body == 0) return 0;
if (count == 0 || count > body->capacity) return 0;
heading = (u8)(heading & 15);
x = chain_body_wrap((s16)head_x, body->width);
y = chain_body_wrap((s16)head_y, body->height);
ox = chain_body_offset_x(heading);
oy = chain_body_offset_y(heading);
i = 0;
while (i < count) {
body->x[(__safe_index u8)i] = x;
body->y[(__safe_index u8)i] = y;
body->heading[(__safe_index u8)i] = heading;
x = chain_body_wrap((s16)((s16)x - ox), body->width);
y = chain_body_wrap((s16)((s16)y - oy), body->height);
i = (u8)(i + 1);
}
body->count = count;
return 1;
}chain_body_step
设置头部姿态,并将各节逐步拉向前一节后方的跟随点。朝向变化每次更新向后传递一节。
语法
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading);参数
body目标
ChainBody;传入空指针时不处理。除初始化函数外,其他函数都要求传入已初始化的状态。head_x头部 X 坐标,按区域宽度回绕。
head_y头部 Y 坐标,按区域高度回绕。
heading头部在 16 档方向中的朝向。
返回值
无返回值。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。ChainBody 保存各节当前的位置和朝向;它与保存坐标历史环形缓冲的 Chain 是不同的状态类型。
调用方须提供 x、y、heading 三个互不重叠的可写数组,每个数组包含 capacity 字节,且在使用身体数据期间始终有效。索引 0 为头部,计入 count。初始化后不要直接修改存储指针、容量或场地尺寸。
朝向按顺时针分为 16 档:0 向右、4 向下、8 向左、12 向上。输入仅使用低四位。屏幕坐标向右、向下递增,宽和高各为 1~256 像素。
从前一节更新后的位置求出目标跟随点,再分别计算两轴回绕后的最短位移。位移为零时不动,绝对值为 1~4 时移动一像素,为 5 或以上时移动两像素。恰好相隔半个区域时保留原位移的符号。
先保存本节更新前的朝向,再将其传给下一节,使弯曲逐节沿身体传播。身体为空时不做任何更改。
头部速度、加速度、碰撞、绘制和等待均由调用方处理。通常先移动头部,再于每次模拟更新中调用一次 chain_body_step。本库实现逐节跟随,不是刚体求解器,也不求解精确的长度约束。
使用示例
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.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图中从上到下依次为直线姿态、向下转弯后更新一次的姿态,以及转弯后更新四次的姿态。绘制时将逻辑坐标放大两倍,弯曲从右端的头部向后传递。CGB 和 FC 画面中头部为红色、身体为蓝色、尾部为绿色,DMG 使用黑色。增长时重叠的尾节先经过四次更新分离,再进行比较。图中是同一条八节身体在三个时刻的状态,并非一条二十四节的身体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:62
Install the caller's head pose, then pull joints from front to rear. Each joint aims four pixels behind its updated predecessor and receives that predecessor's OLD heading. Each axis moves 1 pixel, or 2 when error exceeds 4. Call once per simulation tick; this does not advance head physics or render.
kitaqgb/lib/chain.c:162
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading)
{
u8 i;
u8 previous;
u8 pull;
u8 old;
u8 x;
u8 y;
u8 tx;
u8 ty;
s16 sx;
s16 sy;
if (body == 0) return;
if (body->count == 0) return;
heading = (u8)(heading & 15);
body->x[0] = chain_body_wrap((s16)head_x, body->width);
body->y[0] = chain_body_wrap((s16)head_y, body->height);
body->heading[0] = heading;
pull = heading;
i = 1;
while (i < body->count) {
previous = (u8)(i - 1);
old = body->heading[(__safe_index u8)i];
x = body->x[(__safe_index u8)i];
y = body->y[(__safe_index u8)i];
tx = chain_body_wrap((s16)((s16)body->x[(__safe_index u8)previous] - chain_body_offset_x(pull)), body->width);
ty = chain_body_wrap((s16)((s16)body->y[(__safe_index u8)previous] - chain_body_offset_y(pull)), body->height);
sx = chain_body_correction(chain_wrap_delta(tx, x, body->width));
sy = chain_body_correction(chain_wrap_delta(ty, y, body->height));
body->heading[(__safe_index u8)i] = pull;
body->x[(__safe_index u8)i] = chain_body_wrap((s16)((s16)x + sx), body->width);
body->y[(__safe_index u8)i] = chain_body_wrap((s16)((s16)y + sy), body->height);
// Save before overwriting: a bend advances by one joint per update.
pull = old;
i = (u8)(i + 1);
}
}chain_body_grow
将尾节当前的位置和朝向复制到下一个空闲槽位,并将节数加一。
语法
u8 chain_body_grow(ChainBody* body);参数
body目标
ChainBody;传入空指针时不处理。除初始化函数外,其他函数都要求传入已初始化的状态。
返回值
成功追加一节时返回 1;身体为空、容量已满、状态已禁用或指针为空时返回 0。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。ChainBody 保存各节当前的位置和朝向;它与保存坐标历史环形缓冲的 Chain 是不同的状态类型。
调用方须提供 x、y、heading 三个互不重叠的可写数组,每个数组包含 capacity 字节,且在使用身体数据期间始终有效。索引 0 为头部,计入 count。初始化后不要直接修改存储指针、容量或场地尺寸。
朝向按顺时针分为 16 档:0 向右、4 向下、8 向左、12 向上。输入仅使用低四位。屏幕坐标向右、向下递增,宽和高各为 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.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图中从上到下依次为直线姿态、向下转弯后更新一次的姿态,以及转弯后更新四次的姿态。绘制时将逻辑坐标放大两倍,弯曲从右端的头部向后传递。CGB 和 FC 画面中头部为红色、身体为蓝色、尾部为绿色,DMG 使用黑色。增长时重叠的尾节先经过四次更新分离,再进行比较。图中是同一条八节身体在三个时刻的状态,并非一条二十四节的身体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:65
Append a copy of the current tail; later steps separate the new joint. Return 1 on growth, or 0 for an empty, disabled or full body.
kitaqgb/lib/chain.c:200
u8 chain_body_grow(ChainBody* body)
{
u8 tail;
u8 next;
if (body == 0) return 0;
next = body->count;
if (next == 0 || next >= body->capacity) return 0;
tail = (u8)(next - 1);
body->x[(__safe_index u8)next] = body->x[(__safe_index u8)tail];
body->y[(__safe_index u8)next] = body->y[(__safe_index u8)tail];
body->heading[(__safe_index u8)next] = body->heading[(__safe_index u8)tail];
body->count = (u8)(next + 1);
return 1;
}chain_body_clear
仅将节数设为零,保留数组和区域设置以供再次使用。
语法
void chain_body_clear(ChainBody* body);参数
body目标
ChainBody;传入空指针时不处理。除初始化函数外,其他函数都要求传入已初始化的状态。
返回值
无返回值。
使用条件与限制
包含 chain.h,并且只编译一次 chain.c。ChainBody 保存各节当前的位置和朝向;它与保存坐标历史环形缓冲的 Chain 是不同的状态类型。
调用方须提供 x、y、heading 三个互不重叠的可写数组,每个数组包含 capacity 字节,且在使用身体数据期间始终有效。索引 0 为头部,计入 count。初始化后不要直接修改存储指针、容量或场地尺寸。
朝向按顺时针分为 16 档:0 向右、4 向下、8 向左、12 向上。输入仅使用低四位。屏幕坐标向右、向下递增,宽和高各为 1~256 像素。
不会擦除数组中的字节。需要重新开始时调用 chain_body_reset;清空后立即调用 chain_body_grow 不会追加身体节。
使用示例
chain_body_clear(&body); // Forget the pose; the three arrays remain owned by us.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
图中从上到下依次为直线姿态、向下转弯后更新一次的姿态,以及转弯后更新四次的姿态。绘制时将逻辑坐标放大两倍,弯曲从右端的头部向后传递。CGB 和 FC 画面中头部为红色、身体为蓝色、尾部为绿色,DMG 使用黑色。增长时重叠的尾节先经过四次更新分离,再进行比较。图中是同一条八节身体在三个时刻的状态,并非一条二十四节的身体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\chain_body.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\chain_body.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/chain.h:67
Remove all joints while retaining caller-owned storage and playfield settings.
kitaqgb/lib/chain.c:215
void chain_body_clear(ChainBody* body)
{
if (body == 0) return;
body->count = 0;
}chain_clear
丢弃已记录历史,以便复用同一数组。
语法
void chain_clear(Chain* chain);参数
chain已初始化的
Chain指针。
返回值
无返回值。
使用条件与限制
将 count 和 head 归零,不擦除或释放数组。调用方须保持 Chain 和点数组有效。容量最多为 128,以避免读取器八位索引加法溢出。
GB 版本要求 chain 指针有效。
使用示例
chain_clear(&trail);result[11]=chain_get_count(&trail);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例清空四点历史,确认数量归零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/chain.h:21
Forget the recorded points without clearing or freeing the backing storage.
kitaqgb/lib/chain.c:14
void chain_clear(Chain* chain)
{
chain->count = 0;
chain->head = 0;
}chain_get_count
读取历史中当前可用的位置数量。
语法
u8 chain_get_count(const Chain* chain);参数
chain已初始化的
Chain指针。
返回值
返回零到容量之间的数量,链指针为空时返回零。
使用条件与限制
调用方持有 Chain 和点数组,并须保持其有效。容量最多使用 128,避免读取器的八位索引加法溢出。
使用示例
result[4]=chain_get_count(&trail);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
向四槽历史写入五次后数量仍为四,清空后为零,容量为零的历史始终为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/chain.h:30
Return the number of stored points, or zero for a null chain.
kitaqgb/lib/chain.c:63
u8 chain_get_count(const Chain* chain)
{
if (chain == 0) return 0;
return chain->count;
}chain_get_segment
按记录的新旧次序读取位置。
语法
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out);参数
chain已初始化的
Chain指针。index历史年龄索引:0 为最新,
count - 1为最旧的保留记录。out独立可写的结果
ChainPoint,空指针导致失败。
返回值
成功返回 1,请求无效返回 0。验证之前先将非空 out 清零为 (0,0)。
使用条件与限制
out 不得与底层数组或 Chain 重叠,否则清除输出会破坏源数据。调用方须保持 Chain 及点数组有效,容量最多为 128,以避免读取器八位索引加法溢出。
使用示例
result[1]=chain_get_segment(&trail,0,&point);result[2]=point.x;result[3]=point.y;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
索引 0 读取最新点 (40,24),索引 3 读取最旧点 (16,8),无效索引 4 返回失败且输出为 (0,0)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/chain.h:28
Read a point by age, with index zero denoting the newest point. Return zero for an invalid request and clear a non-null output first. Keep head + index within the 8-bit range: addition is narrowed before the capacity wrap.
kitaqgb/lib/chain.c:40
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out)
{
u8 pos;
if (out != 0) {
out->x = 0;
out->y = 0;
}
if (out == 0) return 0;
if (chain == 0) return 0;
if (chain->capacity == 0) return 0;
if (index >= chain->count) return 0;
pos = (u8)(chain->head + index);
while (pos >= chain->capacity) {
pos = (u8)(pos - chain->capacity);
}
out->x = chain->points[pos].x;
out->y = chain->points[pos].y;
return 1;
}chain_init
为环形位置历史绑定存储,从没有记录点的状态开始。
语法
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity);参数
chain要初始化的可写
Chain。storage能够容纳
capacity个ChainPoint的数组。capacity点槽位数。有效历史容量为 1~128,零表示禁用记录。
返回值
无返回值。
使用条件与限制
调用方持有 Chain 和点数组,并须保持其有效。容量最多使用 128,避免读取器的八位索引加法溢出。
GB 版本要求 chain 指针有效。
使用示例
chain_init(&trail,storage,4);result[0]=chain_get_count(&trail);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例绑定四个点槽位,检查初始数量为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/chain.h:19
Attach caller-owned ring storage and start with no recorded points. The storage must remain valid for the lifetime of the chain. Use a valid chain pointer and capacity entries of caller-owned storage. Capacity at most 128 keeps every head+age sum in the byte range used by the reader.
kitaqgb/lib/chain.c:5
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity)
{
chain->points = storage;
chain->capacity = capacity;
chain->count = 0;
chain->head = 0;
}chain_push_head
将最新位置加入历史头部,满时替换最旧的点。
语法
void chain_push_head(Chain* chain, s16 x, s16 y);参数
chain已初始化的
Chain指针。x新点的有符号 X 坐标。
y新点的有符号 Y 坐标。
返回值
无返回值。
使用条件与限制
链指针为空或容量为零时不处理。调用方须保持 Chain 和点数组有效,容量最多为 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);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
向四槽历史写入五次后,最新点为 (40,24),保留的最旧点为 (16,8),最初的 (8,8) 被替换。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_chain.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_chain.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/chain.h:24
Move the ring head backward and store the newest point. Once full, each push replaces the oldest point; zero capacity is a no-op.
kitaqgb/lib/chain.c:22
void chain_push_head(Chain* chain, s16 x, s16 y)
{
if (chain == 0) return;
if (chain->capacity == 0) return;
if (chain->head == 0) chain->head = (u8)(chain->capacity - 1);
else chain->head--;
chain->points[chain->head].x = x;
chain->points[chain->head].y = y;
if (chain->count < chain->capacity) chain->count++;
}danmaku.h — 弹池、扇形发射、命中与擦弹
无需动态分配的弹幕系统,在 160×128 像素区域内最多管理 96 颗子弹。支持十六分之一像素移动、32 方向扇形弹幕、命中/擦弹事件及四象限图块合成。模拟可在 GB 上运行;present_now 上传仅适用于 CGB。
danmaku_clear
停用全部子弹,清零活动数量、当前命中/擦弹事件和分配游标。
语法
void danmaku_clear();返回值
无返回值。
使用条件与限制
保留峰值及累计计数。要从画面移除子弹,还需清空并上传 RAM 地图。
使用示例
danmaku_clear();result[6]=dm_count;result[7]=dm_spawned;result[8]=dm_peak;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:35
Deactivate the pool while retaining peak/spawn/rejection counters and the RAM map.
kitaqgb/lib/danmaku.c:28
void danmaku_clear() {
u8 i;
for (i=0; i<DANMAKU_MAX; i++) dm_bullets[i].active=0;
dm_count=0; dm_hit=0; dm_graze=0; dm_cursor=0;
}danmaku_clear_map
清空 32×18 RAM 图块地图的全部 576 字节。
语法
void danmaku_clear_map();返回值
无返回值。
使用条件与限制
不改变子弹状态或 VRAM。每帧应依次调用 clear_map、写入装饰、调用 step,再上传地图。
使用示例
danmaku_clear_map();result[11]=dm_map[0];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:42
32 directions, 0=right, 8=down, 16=left, 24=up. speed in 1/16 px. Clear the 32x18 RAM map before drawing the next frame; no transfer is performed.
kitaqgb/lib/danmaku.c:78
void danmaku_clear_map() {
__asm {
LD_HL_IMM dm_map
LD_B_IMM 72
XOR_A
dm_clm_loop:
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
LDI_HL_A
DEC_B
JR_NZ dm_clm_loop
RET
}
}danmaku_fan
使用 32 方向速度表和固定角度步长生成扇形弹幕。
语法
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);参数
x初始像素 X 坐标,范围为 0..159。
y初始像素 Y 坐标,范围为 0..127。
direction起始角度,仅保留低五位:0=右、8=下、16=左、24=上。
step每颗子弹的角度增量,在 32 个方向内循环。
count尝试生成的数量,最大按 96 处理。
speed以十六分之一像素为单位的速度,最大按 64 处理,即每次更新四个像素。
返回值
无返回值。
使用条件与限制
对象池已满时仍会继续尝试其余生成请求。整数 sin/cos 表的值会使斜向移动产生舍入误差。
使用示例
danmaku_fan(80,64,0,4,8,64);result[9]=dm_count;result[10]=dm_spawned;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:39
Attempt a fan using wrapping 32-step angles; count is capped at 96 and speed at 64.
kitaqgb/lib/danmaku.c:62
void danmaku_fan(u8 x,u8 y,u8 direction,u8 step,u8 count,u8 speed) {
u8 n;
u8 a;
s8 vx;
s8 vy;
if(count>DANMAKU_MAX) count=DANMAKU_MAX;
if(speed>64) speed=64;
a=direction&31;
for(n=0;n<count;n++) {
vx=(s8)(((s16)dm_cos[a]*(s16)speed)>>6);
vy=(s8)(((s16)dm_sin[a]*(s16)speed)>>6);
danmaku_spawn(x,y,vx,vy);
a=(u8)((a+step)&31);
}
}danmaku_present_now
使用 CGB GDMA,将 dm_map 的全部 576 字节同步上传到背景地图 $9800。
语法
void danmaku_present_now();返回值
无返回值。
使用条件与限制
仅适用于 CGB;不要在 DMG 上调用。请在 LCD 关闭时或 VBlank 开始时使用。会选择 VRAM 存储体零,不恢复先前的 VBK。应用程序须在图块 0..15 中提供子弹图形。
使用示例
// CGB general DMA; the LCD is stopped for this complete-map upload.
danmaku_present_now();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:48
Requires CGB and LCD off or the beginning of VBlank. VBK restored to 0. Transfers 18 rows (32-byte stride) to BG map 9800 with 36-block GDMA.
kitaqgb/lib/danmaku.c:301
void danmaku_present_now() {
__asm {
XOR_A
LDH_MEM_A 0x4F
LD_HL_IMM dm_map
LD_A_H
LDH_MEM_A 0x51
LD_A_L
LDH_MEM_A 0x52
LD_A_IMM 0x18
LDH_MEM_A 0x53
XOR_A
LDH_MEM_A 0x54
LD_A_IMM 35
LDH_MEM_A 0x55
RET
}
}danmaku_reset
清空含 96 个子弹槽位的对象池,并重置峰值、累计生成数和因池满而拒绝生成的计数。
语法
void danmaku_reset();返回值
无返回值。
使用条件与限制
不改变玩家坐标、无敌状态或 RAM 图块地图;请在游戏开始时另行设置。
使用示例
danmaku_reset();result[0]=dm_count;result[1]=dm_peak;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:33
Reset pool, peak and lifetime counters; player fields and map contents remain caller-owned state.
kitaqgb/lib/danmaku.c:34
void danmaku_reset() {
danmaku_clear(); dm_peak=0; dm_spawned=0; dm_rejected=0;
}danmaku_spawn
循环查找空闲槽位,并按指定位置和速度创建一颗子弹。
语法
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);参数
x初始像素 X 坐标,范围为 0..159。
y初始像素 Y 坐标,范围为 0..127。
vx有符号 Q4.4 速度,范围为 −128..127;16 表示每次更新向右移动一个像素。
vy有符号 Q4.4 速度;正值表示向下移动。
返回值
分配成功返回 1;坐标越界或对象池已满时返回 0。越界请求不会增加 dm_rejected。
使用条件与限制
位置以十六分之一像素为单位保存。u16 类型的累计生成数和拒绝数超过 65535 后会回绕。不使用动态内存分配。
使用示例
result[2]=danmaku_spawn(40,40,16,0);result[3]=danmaku_spawn(160,40,0,0);result[4]=dm_count;result[5]=dm_rejected;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:37
Use pixel origins inside 160x128 and signed sixteenth-pixel velocities; return one if a slot was allocated.
kitaqgb/lib/danmaku.c:39
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy) {
u8 n;
u8 i;
if (x>=160 || y>=128) return 0;
for(n=0;n<DANMAKU_MAX;n++) {
i=dm_cursor;
dm_cursor++;
if(dm_cursor>=DANMAKU_MAX) dm_cursor=0;
if(dm_bullets[i].active==0) {
dm_bullets[i].x=(s16)((u16)x<<4);
dm_bullets[i].y=(s16)((u16)y<<4);
dm_bullets[i].vx=vx; dm_bullets[i].vy=vy;
dm_bullets[i].active=1; dm_bullets[i].grazed=0;
dm_count++; dm_spawned++;
if(dm_count>dm_peak) dm_peak=dm_count;
return 1;
}
}
dm_rejected++;
return 0;
}danmaku_step
更新每颗子弹一次,移除越界子弹,合成子弹图块,并检测命中与擦弹。
语法
void danmaku_step();返回值
无返回值。
使用条件与限制
绘制中心对齐到四像素格子。两个轴上的距离都不超过八时,触发首次擦弹;都不超过三时,判定命中。无敌状态只抑制命中。dm_hit/dm_graze 每次调用都会重置;每颗子弹只触发一次擦弹。图块 ID 0..15 用四个象限位编码;被子弹占据的装饰图块若 ID 为 16 或更大,则会被替换。
使用示例
dm_player_x=0;dm_player_y=0;dm_invulnerable=0;for(i=0;i<8;i++){danmaku_clear_map();danmaku_step();}result[12]=dm_count;result[13]=dm_hit;result[14]=dm_graze;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
仅适用于 CGB 的绘制示例。在 (80,64) 沿八个方向生成子弹,更新八次后,显示放射状排列的八个红色 3×3 点。上下左右方向的子弹移动 32 像素;斜向采用整数近似。同时检查对象池清空及累计生成数的保留行为。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_danmaku.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_danmaku.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/danmaku.h:45
Caller fills decoration after clear_map, then calls step to overlay bullets. hit and graze are events for this step. Collision uses rendered centers.
kitaqgb/lib/danmaku.c:99
void danmaku_step() {
// One linear pass. No per-bullet bank calls, multiplication or division.
// The 8-byte packed public layout is x16,y16,vx8,vy8,active,graze.
__asm {
XOR_A
LD_MEM_A dm_hit
LD_MEM_A dm_graze
LD_A_IMM 96
LD_MEM_A dm_remaining
LD_HL_IMM dm_bullets
dm_tick_loop:
PUSH_HL
LD_BC_IMM 6
ADD_HL_BC
LD_A_HL
OR_A
JP_Z dm_tick_next
DEC_HL
LD_A_HL
LD_MEM_A dm_vy
DEC_HL
LD_A_HL
LD_C_A
LD_B_IMM 0
AND_IMM 128
JR_Z dm_tick_xsign
DEC_B
// B:C is the sign-extended X velocity. Add to Q12.4 position; high byte >=10 also rejects negative X.
dm_tick_xsign:
POP_HL
PUSH_HL
LD_A_HL
ADD_C
LDI_HL_A
LD_E_A
LD_A_HL
ADC_B
LDI_HL_A
CP_IMM 10
JP_NC dm_tick_kill
RRCA
RRCA
RRCA
RRCA
LD_D_A
LD_A_E
RRCA
RRCA
RRCA
RRCA
AND_IMM 15
OR_D
LD_MEM_A dm_px
LD_A_MEM dm_vy
LD_C_A
LD_B_IMM 0
AND_IMM 128
JR_Z dm_tick_ysign
DEC_B
// Apply signed Y velocity similarly; high byte >=8 rejects negative Y and positions at or below 128 pixels.
dm_tick_ysign:
LD_A_HL
ADD_C
LDI_HL_A
LD_E_A
LD_A_HL
ADC_B
LDI_HL_A
CP_IMM 8
JP_NC dm_tick_kill
RRCA
RRCA
RRCA
RRCA
LD_D_A
LD_A_E
RRCA
RRCA
RRCA
RRCA
AND_IMM 15
OR_D
LD_MEM_A dm_py
// Signed differences shifted into 0..16 for a wrap-safe range test.
// Rendered centers are (pixel & FC)+2. Test a square of radius eight around the player for graze eligibility.
LD_A_MEM dm_player_x
LD_C_A
LD_A_MEM dm_px
AND_IMM 252
ADD_A_IMM 10
SUB_C
CP_IMM 17
JR_NC dm_tick_plot
LD_B_A
LD_A_MEM dm_player_y
LD_C_A
LD_A_MEM dm_py
AND_IMM 252
ADD_A_IMM 10
SUB_C
CP_IMM 17
JR_NC dm_tick_plot
LD_C_A
INC_HL
INC_HL
INC_HL
LD_A_HL
OR_A
JR_NZ dm_tick_check_hit
LD_A_IMM 1
LD_HL_A
LD_A_MEM dm_graze
INC_A
LD_MEM_A dm_graze
// A bullet grazes at most once during its lifetime. Invulnerability suppresses hits, but not first-graze events.
// The inner hit test uses a square of radius three around the same quantized center.
dm_tick_check_hit:
LD_A_MEM dm_invulnerable
OR_A
JR_NZ dm_tick_plot
LD_A_B
SUB_IMM 5
CP_IMM 7
JR_NC dm_tick_plot
LD_A_C
SUB_IMM 5
CP_IMM 7
JR_NC dm_tick_plot
LD_A_IMM 1
LD_MEM_A dm_hit
// Select one of four quadrant bits from pixel bit 2, then address map[(y>>3)*32+(x>>3)].
// Decoration IDs >=16 are replaced at occupied cells; bullet IDs 0..15 merge with OR.
dm_tick_plot:
LD_A_MEM dm_py
AND_IMM 4
AND_IMM 254
RRCA
LD_B_A
LD_A_MEM dm_px
AND_IMM 4
AND_IMM 254
RRCA
AND_IMM 254
RRCA
OR_B
LD_E_A
LD_D_IMM 0
LD_HL_IMM dm_bits
ADD_HL_DE
LD_A_HL
LD_C_A
LD_A_MEM dm_py
AND_IMM 120
LD_L_A
LD_H_IMM 0
ADD_HL_HL
ADD_HL_HL
LD_A_MEM dm_px
AND_IMM 254
RRCA
AND_IMM 254
RRCA
AND_IMM 254
RRCA
LD_E_A
LD_D_IMM 0
ADD_HL_DE
LD_DE_IMM dm_map
ADD_HL_DE
LD_A_HL
CP_IMM 16
JR_C dm_tick_merge
XOR_A
dm_tick_merge:
OR_C
LD_HL_A
JR dm_tick_next
// Restore the saved record base, clear active at offset six and decrement the live count exactly once.
dm_tick_kill:
POP_HL
PUSH_HL
LD_BC_IMM 6
ADD_HL_BC
XOR_A
LD_HL_A
LD_A_MEM dm_count
DEC_A
LD_MEM_A dm_count
// Restore the saved base even for inactive or culled bullets, then advance by the fixed eight-byte record size.
dm_tick_next:
POP_HL
LD_BC_IMM 8
ADD_HL_BC
LD_A_MEM dm_remaining
DEC_A
LD_MEM_A dm_remaining
JP_NZ dm_tick_loop
RET
}
}debug.h — RAM 记录与断言
在固定容量的 RAM 日志中记录名称、数值和帧标签。断言保留诊断信息,并允许继续执行。
debug_assert_fail
保留断言失败代码,并尝试追加 ASSERT 条目。
语法
void debug_assert_fail(u16 code);参数
code要保留的 16 位错误标识符。
返回值
无返回值。
使用条件与限制
执行会继续。即使日志已满,最后断言代码也会更新。日志默认容纳 32 条,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时各编译单元须一致使用 1~255。名称只保留指针,检查完成前须可读;调用方协调中断和主循环写入。
使用示例
debug_assert_fail(42);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例先记录代码 42,填满日志后记录 999。条目数保持 32,最后代码变为 999。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:33
Record the assertion code and attempt to append an ASSERT entry. This helper does not halt execution, and the code is retained even if the log is full.
kitaqgb/lib/debug.c:56
void debug_assert_fail(u16 code)
{
kq_debug_last_assert = code;
debug_push((const u8*)"ASSERT", code);
}debug_get_last_assert
不受日志剩余空间影响,读取最近一次断言代码。
语法
u16 debug_get_last_assert();返回值
返回最近的 16 位代码,初始化后为零。
使用条件与限制
零也是初始值,因此无法据此区分是否记录过代码零的失败。读取不会清除代码。
使用示例
result[22]=debug_get_last_assert();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例检查断言宏记录的 77、日志满后的 999,以及重新初始化后的零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:39
Read the last recorded assertion code, or the zero value installed by initialization.
kitaqgb/lib/debug.c:75
u16 debug_get_last_assert()
{
return kq_debug_last_assert;
}debug_get_trace_count
读取跟踪数组中的有效条目数。
语法
u8 debug_get_trace_count();返回值
返回零到 DEBUG_TRACE_MAX 之间的数量。
使用条件与限制
日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称保存指针而不复制,检查结束前须保持可读;调用方须协调中断与主循环的写入。
使用示例
result[1]=debug_get_trace_count();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例检查最初的十条记录及继续写入后的 32 条上限,满时计数不会回绕。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:35
Return the number of valid entries in the fixed trace buffer.
kitaqgb/lib/debug.c:63
u8 debug_get_trace_count()
{
return kq_debug_trace_count;
}debug_get_trace_log
取得内部数组引用,用于查看记录的名称、数值和帧标签。
语法
const DebugTraceEntry* debug_get_trace_log();返回值
返回内部 DebugTraceEntry 数组的只读指针。
使用条件与限制
返回的是引用而非副本,只读取 debug_get_trace_count() 报告的条目数。初始化后遗留的字节不是有效记录。日志默认容量 32,满时丢弃新条目;覆盖 DEBUG_TRACE_MAX 时各编译单元一致使用 1~255。名称只保存指针,检查完成前须可读,中断和主循环写入须自行协调。
使用示例
entries=debug_get_trace_log();for(i=0;i<10;i++){result[2+i]=entries[i].value;result[12+i]=entries[i].frame;}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例读取全部十条的数值和帧字段,并检查 BEGIN 标签的首字母 B。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:37
Expose the internal trace array for inspection; use the count to bound reads.
kitaqgb/lib/debug.c:69
const DebugTraceEntry* debug_get_trace_log()
{
return kq_debug_trace_log;
}debug_init
初始化有效日志长度、帧标签和最后断言代码。
语法
void debug_init();返回值
无返回值。
使用条件与限制
不擦除已有条目字节,首次记录前应调用。日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时须在所有编译单元一致使用 1~255。名称保存为指针而非副本,检查结束前须保持可读。中断与主循环的并发写入由调用方协调。
使用示例
debug_init();result[0]=debug_get_trace_count();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例检查初始数量为零,再重置已有内容的日志,确认数量和断言代码清零、旧数组值仍保留。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:22
Reset log length, frame tag and last assertion code; existing entry bytes are not erased.
kitaqgb/lib/debug.c:10
void debug_init()
{
kq_debug_trace_count = 0;
kq_debug_frame = 0;
kq_debug_last_assert = 0;
}debug_mark_frame
记录带名称的帧标记,以识别流程中的某个位置。
语法
void debug_mark_frame(const u8* label);参数
label以零结尾的标签,所在存储须在检查完成前保持可读。
返回值
无返回值。
使用条件与限制
日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称保存指针而不复制,检查结束前须保持可读;调用方须协调中断与主循环的写入。
使用示例
debug_mark_frame("BEGIN");这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
添加 BEGIN 标记时,记录数值 65535(0xFFFF)及帧标签 513。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:26
Append a named frame marker using 0xFFFF as its value.
kitaqgb/lib/debug.c:37
void debug_mark_frame(const u8* label)
{
debug_push(label, 0xFFFF);
}debug_set_frame
设置后续日志条目复制使用的帧标签。
语法
void debug_set_frame(u16 frame);参数
frame后续条目使用的 16 位帧编号。
返回值
无返回值。
使用条件与限制
不等待帧,也不自动递增标签,须显式传入游戏帧计数。日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称仅保存指针,检查结束前须可读;中断和主循环写入须自行串行化。
使用示例
debug_set_frame(513);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例设置标签 513,记录十条后读回全部帧字段进行检查。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:24
Set the frame tag copied into subsequently recorded entries.
kitaqgb/lib/debug.c:18
void debug_set_frame(u16 frame)
{
kq_debug_frame = frame;
}debug_trace_u16
记录带名称的 16 位分数或计数器。
语法
void debug_trace_u16(const u8* name, u16 value);参数
name以零结尾的标签,所在存储须在检查完成前保持可读。
value要记录的 0~65535 数值。
返回值
无返回值。
使用条件与限制
日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称保存指针而不复制,检查结束前须保持可读;调用方须协调中断与主循环的写入。
使用示例
debug_trace_u16("WORD",4660);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
WORD 条目保存 4660(0x1234),示例读回并检查两个字节。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:30
Append a 16-bit value with the current frame tag.
kitaqgb/lib/debug.c:49
void debug_trace_u16(const u8* name, u16 value)
{
debug_push(name, value);
}debug_trace_u8
记录八位状态值、名称及当前帧标签。
语法
void debug_trace_u8(const u8* name, u8 value);参数
name以零结尾的标签,所在存储须在检查完成前保持可读。
value0~255 的数值,零扩展后存入日志的 16 位字段。
返回值
无返回值。
使用条件与限制
日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称保存指针而不复制,检查结束前须保持可读;调用方须协调中断与主循环的写入。
使用示例
debug_trace_u8("BYTE",255);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE 条目记录 255,并确认日志的 16 位值字段为 255。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:28
Append an unsigned byte value, widened to the log's 16-bit value field.
kitaqgb/lib/debug.c:43
void debug_trace_u8(const u8* name, u8 value)
{
debug_push(name, (u16)value);
}KITAQGB_ASSERT
语句式宏,条件为假时记录断言代码 1。
语法
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)参数
cond预期成立的条件,零表示失败。
返回值
无返回值。
使用条件与限制
条件只求值一次。为真不记录,为假记录失败但不停止执行。do…while (0) 形式使其可作为 if…else 中的一条语句使用。
使用示例
KITAQGB_ASSERT(1);KITAQGB_ASSERT(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
一次为真和一次为假的断言只产生一条记录,代码为 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:45
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)KITAQGB_ASSERT_CODE
仅在条件为假时记录指定断言代码。
语法
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)参数
cond预期成立的条件。
code失败时求值并转换为 16 位的代码表达式。
返回值
无返回值。
使用条件与限制
条件求值一次,代码表达式仅在失败时求值。记录后继续执行。
使用示例
KITAQGB_ASSERT_CODE(1,++side_effect);KITAQGB_ASSERT_CODE(0,77);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
条件为真时不会求值 ++side_effect,变量保持零;条件为假时记录代码 77。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:46
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)KITAQGB_MARK_FRAME
将标签转换为字符串指针,追加值为 0xFFFF 的帧标记。
语法
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))参数
label以零结尾的标签,所在存储须在检查完成前保持可读。
返回值
无返回值。
使用条件与限制
日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时所有编译单元须一致使用 1~255。名称保存指针而不复制,检查结束前须保持可读;调用方须协调中断与主循环的写入。
使用示例
KITAQGB_MARK_FRAME("END");这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查 END 标记的值为 65535,帧标签为 513。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:44
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))KITAQGB_TRACE
先显式转换为 16 位,再记录带名称的数值。
语法
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))参数
name以零结尾的标签,所在存储须在检查完成前保持可读。
value记录前转换为 16 位的表达式。
返回值
无返回值。
使用条件与限制
宏始终启用,不由构建设置自动禁用,16 位记录行为与 KITAQGB_TRACE_U16 相同。日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时各编译单元须一致使用 1~255。名称仅存指针,检查完成前须可读;中断与主循环写入须自行协调。
使用示例
KITAQGB_TRACE("SCORE",600);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例记录 600,并从日志读回验证。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:43
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))KITAQGB_TRACE_U16
先显式转换为 16 位,再记录带名称的数值。
语法
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))参数
name以零结尾的标签,所在存储须在检查完成前保持可读。
value记录前转换为 16 位的表达式。
返回值
无返回值。
使用条件与限制
宏始终启用,构建设置不会自动禁用。日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时各编译单元须一致使用 1~255。名称仅保存指针,检查结束前须可读;中断和主循环写入须自行协调。
使用示例
KITAQGB_TRACE_U16("WORD2",500);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例记录 500,并从日志读回验证。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:42
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))KITAQGB_TRACE_U8
先显式转换为八位,再记录带名称的数值。
语法
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))参数
name以零结尾的标签,所在存储须在检查完成前保持可读。
value记录前转换为八位的表达式。
返回值
无返回值。
使用条件与限制
宏始终启用,构建设置不会自动禁用。日志默认容量 32,满时丢弃新条目。覆盖 DEBUG_TRACE_MAX 时各编译单元须一致使用 1~255。名称仅保存指针,检查结束前须可读;中断和主循环写入须自行协调。
使用示例
KITAQGB_TRACE_U8("CAST",0x1234);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例记录 52,只保留 0x1234 的低字节 0x34。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_debug.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_debug.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/debug.h:41
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))dmg3d.h — DMG 的 128×120 单色线框
单色三维线条绘制。提供模型顶点、边和面,以及物体位置、旋转及缩放,按初始化、开始帧、绘制、上传的顺序使用。物理模拟另行处理。角度每周 16 档,模型比例 256 为原比例。
Vec3 保存 x/y/z,Edge 保存顶点索引 a/b,Face 保存三角形索引 a/b/c。Model 的 vertices、edges、faces 指向这些数组,各计数表示数组长度。edge_faces 保存邻接面索引 f0/f1,缺少的面用 FACE_NONE。flags 中的 MODEL_HIDDEN_LINES 请求按面选择边。投影后的有符号面积决定面朝向,因此顶点顺序有意义。
Object 保存模型引用、位置 x/y/z、角度 rx/ry/rz、scale_q8 和 visible。多个物体可共享模型,但绘制不复制数据表,也不自动切换其 ROM 存储体。绘制期间引用数据须保持可读。CGB Object 还包含颜色索引。
DMG3D_BeginFrame
开始绘制一帧并禁用遮挡检测。
语法
void DMG3D_BeginFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:只禁用遮挡过滤。暂存区通常在传输时清空,Init 提供初始干净区域。不会丢弃尚未提交的帧、清空遮罩,或重置背景待写项目及待更新标志。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:17
WIRE3D_DMG_HEIGHT = 120. Disable occlusion filtering only. Stage clearing normally happens during transfer, and Init supplies the first clean stage. This does not discard an unsubmitted frame, clear the mask, or reset pending BG writes/dirty flags.
kitaqgb/lib/wire3d_dmg.c:2641
void Wire3DDMG_BeginFrame()
{
/* The consuming stage transfer clears D000 as it uploads, so BeginFrame only
resets draw state. Wire3DDMG_Init supplies the first clean buffer. */
w3ddmg_occlusion_active = 0;
}DMG3D_DrawIndexedEdges
通过顶点和两字节边索引对,绘制整数缩放的线框模型。
语法
void DMG3D_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale);参数
vertices三维顶点数组。
vertex_count顶点数组的元素数。
edges每条边的顶点索引,格式为声明的结构体或字节对。
edge_count边数。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
scale整数缩放比例,零按一处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:整数缩放、平移顶点后绘制字节对索引边,比例零按一,不进行物体旋转或面过滤。最多投影 24 个顶点,最多提供 128 对边索引,因为字节偏移超过 255 会回绕。跳过无效顶点编号或深度不合格的端点,忽略空数组。坐标乘积及和须能用 s16 表示。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}预期结果
投影水平线和辅助 8×8 方块展示直接传输,源字节被消耗后从 255 变为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:20
WIRE3D_DMG_HEIGHT = 120. Draw byte-pair edge indices after integer scaling and translation; scale zero means one, and there is no object rotation or face filtering. Project at most 24 vertices. Supply at most 128 edge pairs because byte offsets wrap after 255. Invalid vertex IDs/depth-rejected endpoints are skipped; null arrays are ignored. Keep coordinate products/sums representable as s16.
kitaqgb/lib/wire3d_dmg.c:3824
void Wire3DDMG_DrawIndexedEdges(const Wire3DDMG_Vec3* vertices, w3ddmg_u8 vertex_count, const w3ddmg_u8* edges, w3ddmg_u8 edge_count, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 scale)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
if (vertices == 0) return;
if (edges == 0) return;
count = vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
if (scale == 0) scale = 1;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)(x + (w3ddmg_i16)(v->x * scale));
vy = (w3ddmg_i16)(y + (w3ddmg_i16)(v->y * scale));
vz = (w3ddmg_i16)(z + (w3ddmg_i16)(v->z * scale));
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < edge_count)
{
w3ddmg_u8 a;
w3ddmg_u8 b;
a = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)(i << 1))];
b = edges[(__safe_index w3ddmg_u8)((w3ddmg_u8)((i << 1) + 1))];
if ((a < count) && (b < count) &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)b])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)b]);
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_DrawLine2D
向暂存区绘制含端点的四连通屏幕线段。
语法
void DMG3D_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:启用待更新传输时先标记包围图块矩形,再绘制连通暂存线。场景绘制可能用五个遮罩采样点拒绝线条。端点须在 128×120 内,底层像素边界检查不是几何线段裁剪。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:21
WIRE3D_DMG_HEIGHT = 120. Mark the bounding tile rectangle when dirty transfer is enabled, then draw a connected staged line. Scene drawing may reject it using five mask samples. Supply 128x120 viewport endpoints; low-level pixel bounds checks do not clip the segment geometrically.
kitaqgb/lib/wire3d_dmg.c:2685
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by)
{
w3ddmg_mark_line_dirty(ax, ay, bx, by);
if (w3ddmg_occlusion_active)
{
w3ddmg_line_stage_masked_c(ax, ay, bx, by);
return;
}
w3ddmg_line_x0 = ax;
w3ddmg_line_y0 = ay;
w3ddmg_line_x1 = bx;
w3ddmg_line_y1 = by;
if (ax < 128 && bx < 128 && ay < WIRE3D_DMG_SCREEN_H && by < WIRE3D_DMG_SCREEN_H)
w3ddmg_line_inside_asm();
else
w3ddmg_line_stage_asm();
}DMG3D_DrawLine3D
投影两个世界坐标端点并连接成线。
语法
void DMG3D_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);参数
ax起点的世界坐标 X 分量。
ay起点的世界坐标 Y 分量。
az起点的世界坐标 Z 分量。
bx终点的世界坐标 X 分量。
by终点的世界坐标 Y 分量。
bz终点的世界坐标 Z 分量。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:投影两个世界端点,仅在两者都通过深度测试时绘制。不求跨界线段与近/远裁剪面的交点,投影端点各自限制在屏幕边界内。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:22
WIRE3D_DMG_HEIGHT = 120. Project both world endpoints and draw only when both pass the depth range. Do not intersect a crossing segment with near/far planes; projected endpoints are independently clamped to the screen edges.
kitaqgb/lib/wire3d_dmg.c:2808
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz)
{
w3ddmg_u8 sx0;
w3ddmg_u8 sy0;
w3ddmg_u8 sx1;
w3ddmg_u8 sy1;
if (w3ddmg_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3ddmg_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DDMG_DrawLine2D(sx0, sy0, sx1, sy1);
}DMG3D_DrawModel
以指定位置、旋转和原比例绘制顶点/边模型。
语法
void DMG3D_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:用比例 256 调用 DrawModelScaled,绘制未缩放模型。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:23
WIRE3D_DMG_HEIGHT = 120. Draw an unscaled model by delegating to DrawModelScaled with scale 256.
kitaqgb/lib/wire3d_dmg.c:2827
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}DMG3D_DrawModelScaled
缩放模型,按 Y/X/Z 旋转,再平移、投影顶点并绘制边。
语法
void DMG3D_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:缩放最多 24 个顶点,按 Y/X/Z 旋转、平移和投影,再遍历全部 edge_count 项,该计数为字节类型,可选邻接面过滤。面临时区最多 16 项,边表须含全部声明项。比例非正按 256,忽略空模型/顶点/边指针。乘积和平移结果须在 s16 范围内,丢弃跨越深度边界的边。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:24
WIRE3D_DMG_HEIGHT = 120. Scale up to 24 vertices, rotate Y/X/Z, translate and project, then traverse all edge_count entries (a byte count) with optional adjacent-face filtering. Face scratch is capped at 16; edge tables must contain all declared entries. Nonpositive scale means 256. Null model/vertex/edge pointers are ignored. Keep products/translations in signed 16-bit range; depth-crossing edges are dropped.
kitaqgb/lib/wire3d_dmg.c:2972
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_is_edge_visible(model, i, face_count))
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_DrawScene
从近到远绘制最多八个物体,用较近的面隐藏后续边。
语法
void DMG3D_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);参数
objects包含位置、旋转、缩放和可见标志的物体数组。
count提供的数组元素数;具体处理上限见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:将最多八个物体原点按相机深度从近到远排序,绘制可见有效模型,标记正面包围矩形供后续物体使用。首个排序位置跳过遮罩检测,最后一个无需增加遮挡。这是粗略线条拒绝,不是深度缓冲。忽略空指针或空输入,遍历时调用方数据和当前投影缓存须有效。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:25
WIRE3D_DMG_HEIGHT = 120. Sort at most eight object origins by camera depth, near to far. Draw visible valid models and mark front-face bounding rectangles for later objects. The first sorted slot bypasses mask tests and the last needs no new occluder. This is coarse line rejection, not a depth buffer. Null/empty input is ignored; caller data and the current projection cache must remain valid during traversal.
kitaqgb/lib/wire3d_dmg.c:2723
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count)
{
w3ddmg_u8 scene_count;
w3ddmg_u8 i;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3D_DMG_SCENE_OBJECT_LIMIT) scene_count = WIRE3D_DMG_SCENE_OBJECT_LIMIT;
if (scene_count == 0) return;
i = 0;
while (i < scene_count)
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = i;
w3ddmg_scene_depth[(__safe_index w3ddmg_u8)i] = w3ddmg_scene_object_depth(&objects[(__safe_index w3ddmg_u8)i]);
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3ddmg_u8 j;
j = (w3ddmg_u8)(i + 1);
while (j < scene_count)
{
w3ddmg_u8 oi;
w3ddmg_u8 oj;
oi = w3ddmg_scene_order[(__safe_index w3ddmg_u8)i];
oj = w3ddmg_scene_order[(__safe_index w3ddmg_u8)j];
if (w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oj] < w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oi])
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = oj;
w3ddmg_scene_order[(__safe_index w3ddmg_u8)j] = oi;
}
j = (w3ddmg_u8)(j + 1);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 0;
i = 0;
while (i < scene_count)
{
Wire3DDMG_Object* obj;
obj = &objects[(__safe_index w3ddmg_u8)w3ddmg_scene_order[(__safe_index w3ddmg_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
w3ddmg_occlusion_active = i == 0 ? 0 : 1;
Wire3DDMG_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, obj->rx, obj->ry, obj->rz, obj->scale_q8);
if ((w3ddmg_u8)(i + 1) < scene_count)
{
w3ddmg_mark_model_occluder(obj->model);
}
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_occlusion_active = 0;
}DMG3D_EndFrame
向 VRAM 提交背景待写项目及暂存图形。
语法
void DMG3D_EndFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:等待下一次 VBlank 并提交背景写入,再按设置先消耗辅助条带,随后上传待更新图块或完整主暂存区。辅助和主源区域重叠,因此顺序会影响结果。STAT 轮询跨访问窗口继续,不要求一次 VBlank 内完成。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:28
WIRE3D_DMG_HEIGHT = 120. Wait for the next VBlank and flush BG writes, then optionally consume the auxiliary strips before uploading either dirty tiles or the entire main stage. Auxiliary/main source overlap makes that ordering significant. STAT polling continues across access windows; the update need not fit one VBlank.
kitaqgb/lib/wire3d_dmg.c:3081
void Wire3DDMG_EndFrame()
{
w3ddmg_wait_vblank_start();
w3ddmg_flush_bg_queue();
if (w3ddmg_aux_transfer_enabled)
{
w3ddmg_transfer_stage_aux_asm();
}
if (w3ddmg_dirty_transfer_enabled)
{
w3ddmg_transfer_dirty_stage();
}
else
{
w3ddmg_transfer_stage_asm();
}
}DMG3D_EraseModelFaces
投影模型正面三角形,并擦除内部暂存像素。
语法
void DMG3D_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:对有隐藏线标志及面数据的模型,投影最多 24 个顶点,擦除最多 16 个正面三角形内部的暂存像素。比例非正按原比例 256,跳过跨越深度边界的面。替换共享投影缓存,不更新 VRAM。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom DMG3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom DMG3D_Face faces[2]={{0,2,1},{0,3,2}};
DMG3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
DMG3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) DMG3D_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) DMG3D_DrawLine2D(40,i,88,i);
DMG3D_EraseTriangle2D(12,12,28,12,12,28);
DMG3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
投影面擦除已填充区域,从而显示保留区域与清除区域的区别。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_faces.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:29
WIRE3D_DMG_HEIGHT = 120. Project up to 24 vertices and erase front-facing triangle interiors from the stage for a model with hidden-line flags and face data. Nonpositive scale means 256 (unity). Up to 16 faces are considered; depth-crossing faces are skipped. This also replaces shared projection caches, but does not update VRAM.
kitaqgb/lib/wire3d_dmg.c:2843
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, ry);
w3ddmg_rotate_x(&vy, &vz, rx);
w3ddmg_rotate_z(&vx, &vy, rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}DMG3D_EraseSpan2D
擦除一行暂存像素中包含端点的水平区间。
语法
void DMG3D_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);参数
y待擦除行的像素 Y 坐标。
x0视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
x1视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:排列 X 端点,擦除包含端点的暂存水平区间。两个 X 均须在 0~127 内,无效 Y 被忽略,不自动裁剪 X。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:30
WIRE3D_DMG_HEIGHT = 120. Order the X endpoints and erase the inclusive staged span. Both endpoints must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
kitaqgb/lib/wire3d_dmg.c:2943
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1)
{
w3ddmg_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
w3ddmg_stage_clear_span(y, x0, x1);
}DMG3D_EraseTriangle2D
使用水平扫描线区间,从暂存区擦除屏幕三角形。
语法
void DMG3D_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
cx顶点 C 的视口像素 X 坐标。
cy顶点 C 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:通过整数扫描线求交擦除暂存三角形。提供视口坐标,忽略退化三角形,遮挡遮罩和 VRAM 不变。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom DMG3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom DMG3D_Face faces[2]={{0,2,1},{0,3,2}};
DMG3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;
DMG3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) DMG3D_DrawLine2D(8,i,32,i);
for(i=36;i<=84;i++) DMG3D_DrawLine2D(40,i,88,i);
DMG3D_EraseTriangle2D(12,12,28,12,12,28);
DMG3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
投影面擦除已填充区域,从而显示保留区域与清除区域的区别。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_faces.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:31
WIRE3D_DMG_HEIGHT = 120. Erase a triangle from staged pixels using integer scanline intersections. Supply viewport coordinates. Degenerate triangles are ignored; the occlusion mask and VRAM are unchanged.
kitaqgb/lib/wire3d_dmg.c:2933
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy)
{
w3ddmg_clear_triangle(ax, ay, bx, by, cx, cy);
}DMG3D_Init
为单色线框视口初始化 LCD、图块和背景图。
语法
void DMG3D_Init();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:接管 LCD 状态,清空图块 VRAM 和背景图,在图块 (3,1) 建立 16×15 视口。设置滚动 (8,0)、BGP=0xB4 并开启背景。重置相机、队列、传输模式、两份待更新表及暂存区。预留 0xD000~0xDFFF 并保持 WRAM 映射,辅助存储与其重叠。绘制前调用 BeginFrame 建立遮挡状态。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:35
WIRE3D_DMG_HEIGHT = 120. Take over LCD state, clear tile VRAM and the BG map, and install a 16x15 viewport beginning at map tile (3,1). Set scroll (8,0), BGP=0xB4 and BG display. Reset camera, queues, transfer modes, both dirty tables and stage. Reserve 0xD000..0xDFFF and retain its WRAM mapping; auxiliary storage aliases it. Call BeginFrame before drawing to establish occlusion state.
kitaqgb/lib/wire3d_dmg.c:2572
void Wire3DDMG_Init()
{
w3ddmg_u8 row;
w3ddmg_u8 col;
w3ddmg_u8 tid;
w3ddmg_u16 off;
w3ddmg_wait_vblank_start();
w3ddmg_reg_lcdc = 0;
w3ddmg_clear_vram_asm();
w3ddmg_fill_bg_map_asm();
row = 0;
tid = 0x90;
while (row < WIRE3D_DMG_TILE_H)
{
off = (w3ddmg_u16)(0x23 + ((w3ddmg_u16)row << 5));
col = 0;
while (col < WIRE3D_DMG_TILE_W)
{
w3ddmg_bg_map_9800[(w3ddmg_u16)(off + (w3ddmg_u16)col)] = tid;
tid = (w3ddmg_u8)(tid + WIRE3D_DMG_TILE_H);
col = (w3ddmg_u8)(col + 1);
}
row = (w3ddmg_u8)(row + 1);
tid = (w3ddmg_u8)(0x90 + row);
}
w3ddmg_reg_scx = 8;
w3ddmg_reg_scy = 0;
w3ddmg_reg_bgp = 0xB4;
w3ddmg_reg_lcdc = 0x81;
w3ddmg_bgq_count = 0;
w3ddmg_aux_transfer_enabled = 0;
w3ddmg_dirty_transfer_enabled = 0;
Wire3DDMG_SetCamera(0, 0, 0, 0, 0, 0);
w3ddmg_clear_stage_asm();
w3ddmg_clear_stage_aux_asm();
w3ddmg_clear_dirty_tables();
}DMG3D_ProjectPoint
通过相机将世界坐标点投影为视口坐标。
语法
w3ddmg_u8 DMG3D_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);参数
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
sx接收投影 X 坐标的有效可写 u8 指针。
sy接收投影 Y 坐标的有效可写 u8 指针。
返回值
投影成功返回 1,深度被拒绝返回 0。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:通过当前相机投影世界点。深度无效返回零且输出不变;成功时写入限制在 128×120 内的坐标。两个输出指针须有效,坐标差须能用 s16 表示。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:43
WIRE3D_DMG_HEIGHT = 120. Project a world point with the current camera. Return zero for invalid depth without changing outputs; success writes clamped 128x120 coordinates. Both output pointers must be valid. Coordinate differences must fit s16.
kitaqgb/lib/wire3d_dmg.c:549
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy)
{
return w3ddmg_project_world(x, y, z, sx, sy);
}DMG3D_PutBgTile
为一个背景图格排队一次图块编号写入。
语法
void DMG3D_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);参数
x32×32 背景图中的图块 X 坐标。
y32×32 背景图中的图块 Y 坐标。
tile背景图格的八位图块编号。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:为 0x9800 的 32×32 背景图排队一次写入,交由 EndFrame 提交。无效坐标和超过 48 条待写上限的提交会被静默忽略。此队列独立于通用 vram 库队列。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:44
WIRE3D_DMG_HEIGHT = 120. Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map coordinates and submissions beyond 48 pending writes are silently ignored. This queue is separate from the general vram library queue.
kitaqgb/lib/wire3d_dmg.c:582
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3ddmg_bgq_count >= WIRE3D_DMG_BG_QUEUE_LIMIT) return;
w3ddmg_bgq_x[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = x;
w3ddmg_bgq_y[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = y;
w3ddmg_bgq_tile[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = tile;
w3ddmg_bgq_count = (w3ddmg_u8)(w3ddmg_bgq_count + 1);
}DMG3D_RotatePoint
按给定角度原地旋转一个点。
语法
void DMG3D_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz);参数
xX 分量的独立可写 s16 指针。
yY 分量的独立可写 s16 指针。
zZ 分量的独立可写 s16 指针。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:按 Y/X/Z 原地旋转,每周 16 档。任一分量指针为空则整次调用不处理,须使用不同指针。每次单轴旋转将输入限制在 ±220。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom DMG3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
DMG3D_Model cube;
DMG3D_Object objects[2];
s16 px; s16 py; s16 pz;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
DMG3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
DMG3D_DrawModel(&cube,-32,32,96,0,0,0);
DMG3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
DMG3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
px=16;py=0;pz=0;DMG3D_RotatePoint(&px,&py,&pz,0,4,0);
result[1]=px; result[2]=py; result[3]=pz;
DMG3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_models.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:46
WIRE3D_DMG_HEIGHT = 120. Rotate in place in Y/X/Z order using 16 steps per full turn. A null component pointer makes the whole call a no-op; use distinct pointers. Each axis rotation clamps its inputs to +/-220.
kitaqgb/lib/wire3d_dmg.c:3121
void Wire3DDMG_RotatePoint(w3ddmg_i16* x, w3ddmg_i16* y, w3ddmg_i16* z, w3ddmg_u8 rx, w3ddmg_u8 ry, w3ddmg_u8 rz)
{
if (x == 0) return;
if (y == 0) return;
if (z == 0) return;
w3ddmg_rotate_y(x, z, ry);
w3ddmg_rotate_x(y, z, rx);
w3ddmg_rotate_z(x, y, rz);
}DMG3D_SetAuxTransfer
选择 EndFrame 是否同时传输辅助条带。
语法
void DMG3D_SetAuxTransfer(w3ddmg_u8 flag);参数
flag零禁用传输功能,其他值启用。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:保存 EndFrame 的辅助传输开关,非零为启用。此处不复制或清除数据,辅助源字节与主暂存区重叠。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Use the auxiliary-transfer gate to consume an overlapping stage strip.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetDirtyTransfer(1); DMG3D_SetAuxTransfer(1);
DMG3D_BeginFrame();
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
// Auxiliary upload consumes this source before the dirty main tile is sent.
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
辅助传输消耗主暂存区的一部分,在屏幕 (80,24) 形成黑色 8×8 方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_aux_gate.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_aux_gate.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:50
WIRE3D_DMG_HEIGHT = 120. Store the EndFrame auxiliary-transfer gate; any nonzero value enables it. No data is copied or cleared here. Auxiliary source bytes alias the main stage.
kitaqgb/lib/wire3d_dmg.c:3895
void Wire3DDMG_SetAuxTransfer(w3ddmg_u8 flag)
{
w3ddmg_aux_transfer_enabled = flag;
}DMG3D_SetCamera
保存后续投影使用的相机位置与朝向。
语法
void DMG3D_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll);参数
x相机的世界坐标 X 分量。
y相机的世界坐标 Y 分量。
z相机的世界坐标 Z 分量。
pitch绕 X 轴旋转,一周为 16 档。
yaw绕 Y 轴旋转,一周为 16 档。
roll绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:保存后续绘制使用的相机位置和角度,一周 16 档。不重新投影已有图形,也不检查输入范围。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:51
WIRE3D_DMG_HEIGHT = 120. Store camera position and angles for later draws; a full turn is 16 angle steps. No existing drawing is reprojected and no input range validation occurs.
kitaqgb/lib/wire3d_dmg.c:2661
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8 pitch, w3ddmg_u8 yaw, w3ddmg_u8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}DMG3D_SetDirtyTransfer
为 120 行主视口选择按待更新图块传输。
语法
void DMG3D_SetDirtyTransfer(w3ddmg_u8 flag);参数
flag零禁用传输功能,其他值启用。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:保存待更新传输开关,并清除两份历史和主暂存区,即使值不变也执行。VRAM 保留,在旧画面上直接启用可能留下未标记的旧图块,应先建立干净的已显示基准帧。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Remove the previous frame and upload only the tracked current/previous tiles.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetDirtyTransfer(1); DMG3D_SetAuxTransfer(0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,24,8); DMG3D_DrawLine2D(24,8,24,24);
DMG3D_DrawLine2D(24,24,8,24); DMG3D_DrawLine2D(8,24,8,8);
DMG3D_EndFrame();
DMG3D_BeginFrame();
DMG3D_DrawLine2D(80,80,104,80); DMG3D_DrawLine2D(104,80,104,104);
DMG3D_DrawLine2D(104,104,80,104); DMG3D_DrawLine2D(80,104,80,80);
DMG3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
左上方旧边框消失,仅保留屏幕 (96,88)~(120,112) 的新边框。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_dirty.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_dirty.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:52
WIRE3D_DMG_HEIGHT = 120. Store the dirty-transfer gate and clear both dirty histories and the main stage, even if the value is unchanged. VRAM is retained; enabling this over an old image can leave unmarked old tiles, so arrange a clean displayed baseline.
kitaqgb/lib/wire3d_dmg.c:3955
void Wire3DDMG_SetDirtyTransfer(w3ddmg_u8 flag)
{
w3ddmg_dirty_transfer_enabled = flag;
w3ddmg_clear_dirty_tables();
w3ddmg_clear_stage_asm();
}DMG3D_SetPalette
写入单色背景调色板寄存器 BGP 的四阶灰度映射。
语法
void DMG3D_SetPalette(w3ddmg_u8 bgp);参数
bgp包含四个两位灰度分配字段的 BGP 寄存器值。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:立即写入 DMG BGP 原始调色板编码,不进行帧同步。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_SetCamera(8,0,0,0,0,0);
result[1]=DMG3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=DMG3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
DMG3D_SetCamera(0,0,0,0,0,0);
DMG3D_BeginFrame();
DMG3D_DrawLine2D(8,8,56,8);
DMG3D_DrawLine2D(56,8,56,40);
DMG3D_DrawLine2D(56,40,8,40);
DMG3D_DrawLine2D(8,40,8,8);
DMG3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
DMG3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
DMG3D_PutBgTile(1,1,160);
DMG3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_raster.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:53
WIRE3D_DMG_HEIGHT = 120. Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
kitaqgb/lib/wire3d_dmg.c:597
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp)
{
w3ddmg_reg_bgp = bgp;
}DMG3D_TransferAuxNow
立即上传六条辅助条带,并清空其源字节。
语法
void DMG3D_TransferAuxNow();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:立即上传并消耗辅助源条带,不受辅助开关影响。不等待 VBlank 起始,不更新待更新历史或背景队列。源区与主暂存区重叠,须协调上传顺序。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}预期结果
投影水平线和辅助 8×8 方块展示直接传输,源字节被消耗后从 255 变为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:57
WIRE3D_DMG_HEIGHT = 120. Upload and consume auxiliary source strips immediately, independent of the auxiliary gate. No VBlank-start wait or dirty-history/BG-queue update occurs. The source overlaps the main stage and must be coordinated with its upload.
kitaqgb/lib/wire3d_dmg.c:3983
void Wire3DDMG_TransferAuxNow()
{
w3ddmg_transfer_stage_aux_asm();
}DMG3D_TransferMainNow
立即上传完整主暂存区,并清空已消耗的源像素。
语法
void DMG3D_TransferMainNow();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=120:立即上传并消耗完整主暂存区,轮询 STAT 但不等待下一次 VBlank。忽略传输开关,保持待更新历史及背景队列不变,调用方负责协调帧生命周期。
本例使用 128×120 配置,视口起点为屏幕 (16,8)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Upload a main line and an auxiliary block separately; verify source consumption.
// The selected 120-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 120
#include "dmg3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom DMG3D_Vec3 vertices[2]={{-32,0,0},{32,0,0}};
__prg_rom u8 edge_pairs[2]={0,1};
u8* stage_byte;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
DMG3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
DMG3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
DMG3D_BeginFrame();
DMG3D_DrawIndexedEdges(vertices,2,edge_pairs,1,-64,104,96,1);
DMG3D_TransferMainNow();
// Direct auxiliary transfer writes a separate tile after the main upload.
for(i=24;i<=31;i++) DMG3D_DrawLine2D(40,i,47,i);
stage_byte=(u8*)0xD518;result[1]=*stage_byte;
DMG3D_TransferAuxNow();result[2]=*stage_byte;
// Do not upload the consumed stage again.
result[7]=0xA55A; while(1) {}
}预期结果
投影水平线和辅助 8×8 方块展示直接传输,源字节被消耗后从 255 变为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_dmg3d_direct.c .\kitaqgb\lib\dmg3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_dmg3d_direct.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/dmg3d.h:58
WIRE3D_DMG_HEIGHT = 120. Upload and consume the full main stage immediately, polling STAT but not waiting for the next VBlank. Ignore transfer gates and leave dirty histories and BG tile queue unchanged; the caller coordinates the frame lifecycle.
kitaqgb/lib/wire3d_dmg.c:3970
void Wire3DDMG_TransferMainNow()
{
w3ddmg_transfer_stage_asm();
}entity.h — 固定数组对象池
entity 是基于固定数组的对象池,可保存敌人、子弹和道具等状态。分配空闲槽位后,通过 ID 访问;对象不再需要时释放槽位,供后续重用。不进行堆分配。坐标与速度单位、移动、碰撞处理及计时器更新均由游戏定义。
包含 entity.h,并且只编译一次 entity.c。先调用 entity_init,再检查 entity_create 是否返回 0xFF。ENTITY_MAX 默认为 16。ID 支持 1..255 个槽位,但实际可分配数量取决于可用 RAM。所有编译单元须使用相同的容量定义。
entity_get 返回的指针指向可重用的对象池存储空间。释放后,同一 ID 可能指代另一个对象,因此 ID 和指针都不是永久身份标识。entity_destroy 只停用槽位。调用方须隐藏或释放精灵,并清理其他资源。sprite 字段只是记录信息,不会自动更新 OAM。
entity_update_all 和 entity_draw_all 按 ID 升序访问活动槽位,并调用 fn(id)。请在回调中执行实际更新或绘制。回调为 null 时不执行操作。在回调中创建或释放后续槽位,会影响本次遍历。先执行更新遍历,再执行绘制遍历,可明确分离两类工作。
entity_clear_all
释放所有实体,并重置各字段,包括未分配精灵的标记。
语法
void entity_clear_all();返回值
无(void)。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
将所有槽位的 active,type,x,y,vx,vy,state,timer 清零,sprite 设为 0xFF。不会隐藏或释放硬件精灵,绘图资源需另行清理。
使用示例
entity_clear_all();
after_clear = entity_count_active();
if (after_clear != 0 || entity_get(reused) != 0) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:43
Release every entity and restore deterministic defaults, including the no-sprite sentinel.
kitaqgb/lib/entity.c:14
void entity_clear_all()
{
u8 i = 0;
while (i < ENTITY_MAX) {
entity_pool[i].active = 0;
entity_pool[i].type = 0;
entity_pool[i].x = 0;
entity_pool[i].y = 0;
entity_pool[i].vx = 0;
entity_pool[i].vy = 0;
entity_pool[i].state = 0;
entity_pool[i].timer = 0;
entity_pool[i].sprite = 0xFF;
i++;
}
}entity_count_active
返回已占用实体槽位数,不改变其状态。
语法
u8 entity_count_active();返回值
active 字节非零的槽位数,范围为 0~ENTITY_MAX。表示占用数量,不是最大已分配 ID 或屏幕上的精灵数量。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
使用示例
created = entity_count_active();
if (created != 2) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:41
Count occupied slots without changing entity state.
kitaqgb/lib/entity.c:93
u8 entity_count_active()
{
u8 i = 0;
u8 count = 0;
while (i < ENTITY_MAX) {
if (entity_pool[i].active != 0) count++;
i++;
}
return count;
}entity_create
按指定类型和位置分配实体,返回可复用的 ID;池满时返回 0xFF。
语法
u8 entity_create(u8 type, s16 x, s16 y);参数
type由游戏定义的类型编号,范围 0~255。池只保存此值,不据此选择行为或绘图函数。
x, y有符号 16 位坐标(
-32768~32767)。像素或 Q8.8 等单位由游戏决定;池不转换单位、裁剪位置或自动应用速度。
返回值
返回编号最小的空闲槽位 ID(0~ENTITY_MAX-1),无空位时返回 0xFF。成功时设 active=1,保存类型和位置,清零速度、状态、计时器,并设 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++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:29
Returns a reusable slot ID, or 0xFF if no slot is free.
kitaqgb/lib/entity.c:33
u8 entity_create(u8 type, s16 x, s16 y)
{
u8 i = 0;
while (i < ENTITY_MAX) {
if (entity_pool[i].active == 0) {
entity_pool[i].active = 1;
entity_pool[i].type = type;
entity_pool[i].x = x;
entity_pool[i].y = y;
entity_pool[i].vx = 0;
entity_pool[i].vy = 0;
entity_pool[i].state = 0;
entity_pool[i].timer = 0;
entity_pool[i].sprite = 0xFF;
return i;
}
i++;
}
return 0xFF;
}entity_destroy
释放 id 对应的实体槽位;保留的数据到重新分配时才初始化。
语法
void entity_destroy(u8 id);参数
id可复用的槽位索引,范围为 0~
ENTITY_MAX-1。范围外 ID 无效;释放无效 ID 不产生效果,查询则返回 null。
返回值
无(void)。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
释放操作仅将 active 清零,不擦除其他字段,也不释放精灵或素材。之后可能将相同 ID 和地址复用于另一实体。关联资源由游戏负责清理。
使用示例
entity_destroy(first);
after_free = entity_count_active();
if (entity_get(first) != 0 || after_free != 1) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:32
Release a valid slot without clearing its payload. Reallocation resets the payload; a saved pointer or ID must not be treated as a persistent entity identity.
kitaqgb/lib/entity.c:56
void entity_destroy(u8 id)
{
if (id >= ENTITY_MAX) return;
// Deactivation only releases this pool slot; the caller handles sprite/resource cleanup.
entity_pool[id].active = 0;
}entity_draw_all
遍历活动实体,将每个 ID 传给绘图回调。遍历方式与 entity_update_all 相同;只有回调提交绘图数据时,才会绘制精灵或像素。
语法
void entity_draw_all(EntityFn fn);参数
fn签名为
void fn(u8 id)的函数指针。接收槽位 ID,可用entity_get(id)访问字段。允许 null,此时不调用。
返回值
无(void)。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
遍历到槽位时才检查其活动状态。回调可停用后面的槽位使其被跳过,也可启用后面的槽位使其在本轮被访问。并非遍历开始时活动集合的快照。
使用示例
void demo_draw(u8 id) {
Entity* entity;
entity = entity_get(id);
if (entity != 0) {
m_put((u8)entity->x, (u8)entity->y, (u8)('A' + entity->type - 1));
draw_calls++;
}
}
// From main(), after creating two entities:
draw_calls = 0;
entity_draw_all(demo_draw);
automatic_draws = draw_calls;
if (automatic_draws != 2) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:39
Use the same active-slot traversal for drawing; the callback supplies all rendering.
kitaqgb/lib/entity.c:87
void entity_draw_all(EntityFn fn)
{
entity_update_all(fn);
}entity_get
返回有效活动实体的指针;ID 无效或未使用时返回 null。该池内存会被复用。
语法
Entity* entity_get(u8 id);参数
id可复用的槽位索引,范围为 0~
ENTITY_MAX-1。范围外 ID 无效;释放无效 ID 不产生效果,查询则返回 null。
返回值
返回可写的活动槽位指针;ID 无效或未使用时返回 null。内存归池所有,不要自行释放,也不要把指针当作跨越删除和重新分配的永久身份。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
释放操作仅将 active 清零,不擦除其他字段,也不释放精灵或素材。之后可能将相同 ID 和地址复用于另一实体。关联资源由游戏负责清理。
使用示例
current_entity = entity_get(first);
if (current_entity != 0) { current_entity->vx = 5; }
else { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:34
Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.
kitaqgb/lib/entity.c:65
Entity* entity_get(u8 id)
{
if (id >= ENTITY_MAX) return 0;
if (entity_pool[id].active == 0) return 0;
return &entity_pool[(__safe_index u8)id];
}entity_init
初始化实体池中的全部槽位。
语法
void entity_init();返回值
无(void)。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
将所有槽位的 active,type,x,y,vx,vy,state,timer 清零,sprite 设为 0xFF。不会隐藏或释放硬件精灵,绘图资源需另行清理。
使用示例
entity_init();
if (entity_count_active() != 0) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:27
Reset every slot before the game starts using the pool.
kitaqgb/lib/entity.c:8
void entity_init()
{
entity_clear_all();
}entity_update_all
按 ID 升序遍历活动槽位,对每个调用一次 fn(id)。移动、碰撞或计时器逻辑由回调实现,池本身不提供这些逻辑。回调为 null 时不执行任何操作。
语法
void entity_update_all(EntityFn fn);参数
fn签名为
void fn(u8 id)的函数指针。接收槽位 ID,可用entity_get(id)访问字段。允许 null,此时不调用。
返回值
无(void)。
使用条件与限制
池使用固定数组,不进行堆动态分配。ENTITY_MAX 默认为 16,范围为 1~255,所有编译单元必须使用相同定义。槽位 ID 从 0 开始,0xFF 专门表示失败。
遍历到槽位时才检查其活动状态。回调可停用后面的槽位使其被跳过,也可启用后面的槽位使其在本轮被访问。并非遍历开始时活动集合的快照。
使用示例
void demo_update(u8 id) {
Entity* entity;
entity = entity_get(id);
if (entity != 0) { entity->x = entity->x + 1; update_calls++; }
}
// From main(), after creating two entities:
update_calls = 0;
entity_update_all(demo_update);
automatic_updates = update_calls;
if (automatic_updates != 2) { failures++; }这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序用于理解可复用槽位的生命周期。请结合标签阅读数字:分配两个实体后为 CREATED 002,释放第一个后为 AFTER FREE 001,复用最小空闲 ID 时为 REUSED ID 000,清空池后为 AFTER CLEAR 000。还会检查释放后的查询为 null,以及重新分配后字段被重置。FAILED CHECKS 000 表示这些检查符合预期。
GB 遍历对两个活动实体分别调用回调,显示 UPDATE CALLS 002、DRAW CALLS 002。示例更新回调将两者向右移动一个图块,绘图回调将代表两个对象的 A 放在图块 (3,12)、B 放在 (7,12)。这些是回调绘制的背景字体图块,不是自动分配的精灵。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\entity.c .\kitaq-docs\samples\api-examples\gb\entity_pool.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\entity_pool.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm实现与源码
kitaqgb/lib/entity.h:37
Call fn for each slot that is active when the scan reaches it; null is a no-op. Callbacks may change the pool, so newly activated later slots can run in this pass.
kitaqgb/lib/entity.c:74
void entity_update_all(EntityFn fn)
{
u8 i = 0;
if (fn == 0) return;
while (i < ENTITY_MAX) {
if (entity_pool[i].active != 0) {
fn(i);
}
i++;
}
}fixed.h — Q8.8 定点运算与矩形
Q8.8 运算用整数表示带小数的坐标及速度,同时提供矩形/点碰撞判断。使用时注意各操作的精度和范围限制。
fix_div
先降低操作数精度,再执行 Q8.8 除法。
语法
fix8 fix_div(fix8 a, fix8 b);参数
aQ8.8 左操作数。
bQ8.8 右操作数。
返回值
返回 Q8.8 商;b == 0 时返回零。
使用条件与限制
计算式为 ((a >> 4) * 256) / (b >> 4)。非零除数右移后也必须非零,因此不能传入正原始值 1~15。缩放后的分子也须能用 16 位表示。
使用示例
result[3]=fix_div(768,512);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
3.0 除以 2.0 得到 1.5(原始值 384)。另行检查显式传入零除数时的返回值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:32
Scale the reduced-precision quotient back to 8.8 and return zero for b == 0. Callers must also ensure (b >> 4) is nonzero; the explicit check only tests b. The intermediate product must fit the compiler's integer arithmetic.
kitaqgb/lib/fixed.c:26
fix8 fix_div(fix8 a, fix8 b)
{
if (b == 0) return 0;
return (fix8)(((a >> 4) * 256) / (b >> 4));
}fix_from_int
将整数坐标或速度转换为 Q8.8,使后续运算能够保留小数部分。
语法
fix8 fix_from_int(s16 n);参数
n要转换的整数,范围 −128~127。
返回值
返回原始值 n × 256。
使用条件与限制
fix8 是有符号 Q8.8,原始值 256 表示 1.0。中间值和结果须保持在 16 位范围内。
使用示例
result[0]=fix_from_int(-3);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例转换 −3,显示的 −00768 是 Q8.8 原始结果 −768。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:23
Convert an integer to signed 8.8 fixed point by moving it into the high byte. The caller must keep the result within the representable 16-bit range.
kitaqgb/lib/fixed.c:5
fix8 fix_from_int(s16 n)
{
return (fix8)(n << 8);
}fix_lerp
使用八位权重,在两个 Q8.8 值之间插值。
语法
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8);参数
aQ8.8 左操作数。
bQ8.8 右操作数。
t_q8插值权重,0~255。
返回值
返回近似的 Q8.8 插值结果。
使用条件与限制
差值和权重分别右移四位,实际只有 16 档权重,因此即使权重为 255 也未必达到终点。b - a 及最终结果须在 16 位范围内。
使用示例
result[4]=fix_lerp(0,1024,128);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在 0 和 4.0 之间使用权重 128 得到 2.0(原始值 512);权重 255 得到 3.75(原始值 960),展示无法达到终点的情况。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:35
Interpolate toward b using a quantized Q0.8 weight. Both shifts discard low bits, so small changes may vanish and a weight of 255 need not reach b.
kitaqgb/lib/fixed.c:34
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8)
{
// The endpoint difference must fit s16 before the reduced-precision multiplication.
fix8 d = (fix8)(b - a);
return (fix8)(a + (fix8)((d >> 4) * (s16)(t_q8 >> 4)));
}fix_mul
用低开销近似计算两个 Q8.8 值的乘积。
语法
fix8 fix_mul(fix8 a, fix8 b);参数
aQ8.8 左操作数。
bQ8.8 右操作数。
返回值
返回 Q8.8 乘积。
使用条件与限制
相乘前将两个操作数各右移四位,分别丢弃低四位,精度低于完整的 16×16 位乘法。fix8 是有符号 Q8.8,原始值 256 表示 1.0;中间值和结果须保持在 16 位范围内。
使用示例
result[2]=fix_mul(384,512);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
1.5 乘以 2.0 得到 3.0(原始值 768)。程序还检查量化情况:原始值 271 与 271 相乘得到 256。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:28
Approximate 8.8 multiplication by discarding four low bits from each operand before multiplying. This trades precision for a smaller intermediate product.
kitaqgb/lib/fixed.c:18
fix8 fix_mul(fix8 a, fix8 b)
{
return (fix8)((a >> 4) * (b >> 4));
}fix_to_int
丢弃 Q8.8 的小数位以取得整数坐标。负数向负无穷方向取整。
语法
s16 fix_to_int(fix8 x);参数
x要转换为整数的 Q8.8 值。
返回值
返回有符号整数。
使用条件与限制
fix8 是有符号 Q8.8,原始值 256 表示 1.0。中间值和结果须保持在 16 位范围内。
使用示例
result[1]=fix_to_int(-384);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
输入原始值 −384(−1.5)得到 −2,显示为 −00002。边界检查还确认原始值 −1 转为 −1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:25
Discard the eight fractional bits using a signed right shift.
kitaqgb/lib/fixed.c:11
s16 fix_to_int(fix8 x)
{
return (s16)(x >> 8);
}kq_abs_s16
求有符号 16 位数值的绝对值。
语法
s16 kq_abs_s16(s16 v);参数
v需要求绝对值的有符号 16 位数值。
返回值
返回非负绝对值,但 −32768 的正绝对值无法表示。
使用条件与限制
正值 32768 无法存入 s16。需要非负结果时,应排除 −32768。
使用示例
result[0]=kq_abs_s16(-123);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将 −123 转为 123,可用于求坐标差的大小。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:38
Return the magnitude in s16. The most-negative s16 has no positive s16 representation and must be excluded by callers needing a nonnegative result.
kitaqgb/lib/fixed.c:43
s16 kq_abs_s16(s16 v)
{
if (v < 0) return (s16)(0 - v);
return v;
}kq_clamp_s16
将坐标或速度限制在包含两端的区间内。
语法
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi);参数
v要限制的有符号值。
lo包含在有效区间内的下限。
hi包含在有效区间内的上限。
返回值
低于区间返回 lo,高于区间返回 hi,区间内返回 v。
使用条件与限制
须提供满足 lo <= hi 的有效区间。
使用示例
result[3]=kq_clamp_s16(25,-10,10);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
将 25 限制到 −10~10 得到 10;还检查下界 −11→−10 和区间内 3→3。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:44
Clamp to an inclusive interval; callers must provide lo <= hi.
kitaqgb/lib/fixed.c:64
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi)
{
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}kq_max_s16
选择两个有符号 16 位值中的较大值。
语法
s16 kq_max_s16(s16 a, s16 b);参数
a第一个待比较的有符号值。
b第二个待比较的有符号值。
返回值
返回选中的操作数。
使用条件与限制
使用有符号比较。两操作数相等时返回该值,不修改输入。
使用示例
result[2]=kq_max_s16(-7,4);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
比较 −7 和 4 得到 4,负结果带负号显示。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:42
Select the larger signed value.
kitaqgb/lib/fixed.c:57
s16 kq_max_s16(s16 a, s16 b)
{
if (a > b) return a;
return b;
}kq_min_s16
选择两个有符号 16 位值中的较小值。
语法
s16 kq_min_s16(s16 a, s16 b);参数
a第一个待比较的有符号值。
b第二个待比较的有符号值。
返回值
返回选中的操作数。
使用条件与限制
使用有符号比较。两操作数相等时返回该值,不修改输入。
使用示例
result[1]=kq_min_s16(-7,4);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
比较 −7 和 4 得到 −7,负结果带负号显示。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_math_limits.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:40
Select the smaller signed value.
kitaqgb/lib/fixed.c:50
s16 kq_min_s16(s16 a, s16 b)
{
if (a < b) return a;
return b;
}kq_point_in_rect
检查点是否位于矩形内,包含左边和上边。
语法
u8 kq_point_in_rect(s16 x, s16 y, KQRect r);参数
x与矩形处于同一坐标系的点 X 坐标。
y点的 Y 坐标。
r按值传入的待检测矩形。
返回值
位于内部返回 1,否则返回 0。
使用条件与限制
尺寸须为正,坐标加尺寸的结果须能用 s16 表示。不包含右边和下边,不会显式拒绝零面积矩形。
使用示例
result[1]=kq_point_in_rect(9,1,a);
for(gy=0;gy<11;gy++){for(gx=0;gx<15;gx++){
inside_a=kq_point_in_rect(gx,gy,a);inside_b=kq_point_in_rect(gx,gy,b);
tile=0;if(inside_a){tile=65;result[3]=result[3]+1;}if(inside_b)tile=66;if(inside_a && inside_b){tile=79;result[4]=result[4]+1;}
m_put(1+gx,2+gy,tile);if(tile!=0)vram_example_color(1+gx,2+gy,1,1,tile==65?1:(tile==66?2:3));
}}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
每坐标单位对应一个图块。A 位于 (1,1),B 位于 (5,4),两者均为 8×6。仅 A 覆盖处显示 A,仅 B 覆盖处显示 B,4×3 重叠区显示 12 个 O。A 的右边 x=9 不包含在内。CGB 中 A 红、B 蓝、O 绿。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_geometry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:51
Test a half-open rectangle: left/top edges are included, right/bottom excluded. The coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/fixed.c:85
u8 kq_point_in_rect(s16 x, s16 y, KQRect r)
{
if (x < r.x) return 0;
if (y < r.y) return 0;
if (x >= (s16)(r.x + r.w)) return 0;
if (y >= (s16)(r.y + r.h)) return 0;
return 1;
}kq_rect_intersect
检查两个轴对齐矩形是否以正面积重叠。
语法
u8 kq_rect_intersect(KQRect a, KQRect b);参数
a按值传入的第一个矩形:
x, y为左上角,w, h为宽高。b与 A 处于同一坐标系的第二个矩形。
返回值
重叠返回 1,分离或仅边缘接触返回 0。
使用条件与限制
尺寸须为正,坐标加尺寸的结果须能用 s16 表示。不包含右边和下边,不会显式拒绝零面积矩形。
使用示例
result[0]=kq_rect_intersect(a,b);result[2]=kq_rect_intersect(a,touch);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
每坐标单位对应一个图块。A 位于 (1,1),B 位于 (5,4),两者均为 8×6。仅 A 覆盖处显示 A,仅 B 覆盖处显示 B,4×3 重叠区显示 12 个 O。A 的右边 x=9 不包含在内。CGB 中 A 红、B 蓝、O 绿。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_geometry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/fixed.h:48
Test overlap using exclusive right/bottom edges; touching edges do not collide. Require positive dimensions and edge sums that fit s16; zero-sized rectangles are not explicitly rejected and may be reported as overlapping.
kitaqgb/lib/fixed.c:74
u8 kq_rect_intersect(KQRect a, KQRect b)
{
if ((s16)(a.x + a.w) <= b.x) return 0;
if ((s16)(b.x + b.w) <= a.x) return 0;
if ((s16)(a.y + a.h) <= b.y) return 0;
if ((s16)(b.y + b.h) <= a.y) return 0;
return 1;
}input.h — 按钮状态、按下、松开与重复
连续移动用 input_down,单次动作用 input_pressed,松开时停止用 input_released,菜单重复操作用 input_repeat。初始化一次,每个输入周期更新一次,然后按需查询。btn_down、btn_pressed、btn_released、btn_repeat 分别是这四个查询函数的别名。
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
重复触发按更新次数计时,不是毫秒。新按下时立即触发。默认 INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 时,19 次更新后再次触发,此后每 6 次更新触发一次。各按钮独立计数。修改设置时,编译 input.c 也必须使用相同宏值;只修改调用方的头文件不会改变库的设置。
btn_down
btn_down 是 input_down 的别名。检查所选按键中是否有键当前处于按住状态。
判断 mask 选中的按钮是否至少有一个正被按住。
语法
u8 btn_down(u8 mask);参数
mask一个
BTN_*掩码,或用|组合的多个掩码。任一选中按钮符合条件即为真;BTN_A | BTN_B不要求同时按住两个按钮。掩码 0 始终返回 0。若要检查全部按钮均按住,使用(input_current() & mask) == mask。
返回值
任一选中按钮符合查询条件时返回 1,否则返回 0。返回值不是按钮位掩码。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 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(),第二次调用会清除新按下事件。
使用示例
if (btn_down(BTN_A)!=0) held_ticks++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:48
#define btn_down input_downkitaqgb/lib/input.c:68
u8 input_down(u8 mask)
{
return (u8)((kq_input_keys & mask) != 0);
}btn_pressed
btn_pressed 是 input_pressed 的别名。检查所选按键中是否有键在最近一次输入更新时刚被按下。
判断最近一次输入更新中,掩码选中的按钮是否从松开变为按下。
语法
u8 btn_pressed(u8 mask);参数
mask一个
BTN_*掩码,或用|组合的多个掩码。任一选中按钮符合条件即为真;BTN_A | BTN_B不要求同时按住两个按钮。掩码 0 始终返回 0。若要检查全部按钮均按住,使用(input_current() & mask) == mask。
返回值
任一选中按钮符合查询条件时返回 1,否则返回 0。返回值不是按钮位掩码。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 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(),第二次调用会清除新按下事件。
使用示例
if (btn_pressed(BTN_A)!=0) {
press_count++; seen_press=1;
first_down=btn_down(BTN_A); first_press=btn_pressed(BTN_A);
first_repeat=btn_repeat(BTN_A);
first_current=input_current(); // Only A held: BTN_A, or decimal 16.
first_previous=input_previous(); // No buttons in the preceding sample: 0.
if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
if (first_current!=BTN_A || first_previous!=0) failures++;
if (btn_down(BTN_A|BTN_B)!=1 || btn_down(0)!=0) failures++;
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:49
#define btn_pressed input_pressedkitaqgb/lib/input.c:74
u8 input_pressed(u8 mask)
{
return (u8)((kq_input_press_keys & mask) != 0);
}btn_released
btn_released 是 input_released 的别名。检查所选按键中是否有键在最近一次输入更新时被松开。
判断最近一次输入更新中,掩码选中的按钮是否从按下变为松开。
语法
u8 btn_released(u8 mask);参数
mask一个
BTN_*掩码,或用|组合的多个掩码。任一选中按钮符合条件即为真;BTN_A | BTN_B不要求同时按住两个按钮。掩码 0 始终返回 0。若要检查全部按钮均按住,使用(input_current() & mask) == mask。
返回值
任一选中按钮符合查询条件时返回 1,否则返回 0。返回值不是按钮位掩码。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 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(),第二次调用会清除新按下事件。
使用示例
if (btn_released(BTN_A)!=0) {
release_result=btn_released(BTN_A);
last_current=input_current(); last_previous=input_previous();
if (btn_down(BTN_A)!=0 || btn_pressed(BTN_A)!=0 || btn_repeat(BTN_A)!=0) failures++;
break;
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:50
#define btn_released input_releasedkitaqgb/lib/input.c:80
u8 input_released(u8 mask)
{
return (u8)((kq_input_release_keys & mask) != 0);
}btn_repeat
btn_repeat 是 input_repeat 的别名。检查首次按下或持续按住期间按计划产生的重复脉冲。
判断掩码选中的按钮是否产生初次按下或定时连发脉冲。
语法
u8 btn_repeat(u8 mask);参数
mask一个
BTN_*掩码,或用|组合的多个掩码。任一选中按钮符合条件即为真;BTN_A | BTN_B不要求同时按住两个按钮。掩码 0 始终返回 0。若要检查全部按钮均按住,使用(input_current() & mask) == mask。
返回值
任一选中按钮符合查询条件时返回 1,否则返回 0。返回值不是按钮位掩码。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 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(),第二次调用会清除新按下事件。
重复触发按更新次数计时,不是毫秒。新按下时立即触发。默认 INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 时,19 次更新后再次触发,此后每 6 次更新触发一次。各按钮独立计数。修改设置时,编译 input.c 也必须使用相同宏值;只修改调用方的头文件不会改变库的设置。
使用示例
if (btn_repeat(BTN_A)!=0) {
// The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
previous_pulse=held_ticks; repeat_count++;
if (btn_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_aliases.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:51
#define btn_repeat input_repeatkitaqgb/lib/input.c:86
u8 input_repeat(u8 mask)
{
return (u8)((kq_input_repeat_keys & mask) != 0);
}input_current
返回最近一次输入更新得到的全部按住按钮位掩码。
语法
u8 input_current();返回值
通用 BTN_* 格式的 8 位掩码。多个按钮可以同时置位;0 表示没有按钮。
使用条件与限制
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
查询函数只读取最近一次 input_update() 保存的状态,不重新读取控制器、不消耗事件,也不推进重复计时器。同一输入周期内重复查询结果相同。按钮持续按住时若调用两次 input_update(),第二次调用会清除新按下事件。
使用示例
first_current=input_current(); // Only A held: BTN_A, or decimal 16.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:44
Return the complete held-button mask from the latest update.
kitaqgb/lib/input.c:92
u8 input_current()
{
return kq_input_keys;
}input_down
判断 mask 选中的按钮是否至少有一个正被按住。
语法
u8 input_down(u8 mask);参数
mask一个
BTN_*掩码,或用|组合的多个掩码。任一选中按钮符合条件即为真;BTN_A | BTN_B不要求同时按住两个按钮。掩码 0 始终返回 0。若要检查全部按钮均按住,使用(input_current() & mask) == mask。
返回值
任一选中按钮符合查询条件时返回 1,否则返回 0。返回值不是按钮位掩码。
使用条件与限制
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
查询函数只读取最近一次 input_update() 保存的状态,不重新读取控制器、不消耗事件,也不推进重复计时器。同一输入周期内重复查询结果相同。按钮持续按住时若调用两次 input_update(),第二次调用会清除新按下事件。
使用示例
if (input_down(BTN_A)!=0) held_ticks++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:36
Return whether any masked button is currently held.
kitaqgb/lib/input.c:68
u8 input_down(u8 mask)
{
return (u8)((kq_input_keys & mask) != 0);
}input_init
将当前、上次、按下、松开和重复触发掩码清零,并将八个重复计时器重置为 INPUT_REPEAT_DELAY。本函数初始化输入库,但不读取控制器。随后第一次更新时处于按住状态的按钮会被视为新按下。
语法
void input_init();返回值
无(void)。
使用条件与限制
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
重复触发按更新次数计时,不是毫秒。新按下时立即触发。默认 INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 时,19 次更新后再次触发,此后每 6 次更新触发一次。各按钮独立计数。修改设置时,编译 input.c 也必须使用相同宏值;只修改调用方的头文件不会改变库的设置。
使用示例
input_init();
if (input_current()!=0 || input_previous()!=0) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:31
Clear sampled/edge state and initialize an independent repeat timer for each button.
kitaqgb/lib/input.c:18
void input_init()
{
u8 i;
kq_input_prev_keys = 0;
kq_input_keys = 0;
kq_input_press_keys = 0;
kq_input_release_keys = 0;
kq_input_repeat_keys = 0;
i = 0;
while (i < 8) {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
i++;
}
}input_pressed
判断最近一次输入更新中,掩码选中的按钮是否从松开变为按下。
语法
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(),第二次调用会清除新按下事件。
使用示例
if (input_pressed(BTN_A)!=0) {
press_count++; seen_press=1;
first_down=input_down(BTN_A); first_press=input_pressed(BTN_A);
first_repeat=input_repeat(BTN_A);
first_current=input_current(); // Only A held: BTN_A, or decimal 16.
first_previous=input_previous(); // No buttons in the preceding sample: 0.
if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
if (first_current!=BTN_A || first_previous!=0) failures++;
if (input_down(BTN_A|BTN_B)!=1 || input_down(0)!=0) failures++;
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:38
Return whether any masked button became pressed in the latest sample.
kitaqgb/lib/input.c:74
u8 input_pressed(u8 mask)
{
return (u8)((kq_input_press_keys & mask) != 0);
}input_previous
返回最近一次输入更新之前保存的按住按钮位掩码。
语法
u8 input_previous();返回值
通用 BTN_* 格式的 8 位掩码。多个按钮可以同时置位;0 表示没有按钮。
使用条件与限制
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
查询函数只读取最近一次 input_update() 保存的状态,不重新读取控制器、不消耗事件,也不推进重复计时器。同一输入周期内重复查询结果相同。按钮持续按住时若调用两次 input_update(),第二次调用会清除新按下事件。
使用示例
first_previous=input_previous(); // No buttons in the preceding sample: 0.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:46
Return the held-button mask that preceded the latest update.
kitaqgb/lib/input.c:98
u8 input_previous()
{
return kq_input_prev_keys;
}input_released
判断最近一次输入更新中,掩码选中的按钮是否从按下变为松开。
语法
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(),第二次调用会清除新按下事件。
使用示例
if (input_released(BTN_A)!=0) {
release_result=input_released(BTN_A);
last_current=input_current(); last_previous=input_previous();
if (input_down(BTN_A)!=0 || input_pressed(BTN_A)!=0 || input_repeat(BTN_A)!=0) failures++;
break;
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:40
Return whether any masked button became released in the latest sample.
kitaqgb/lib/input.c:80
u8 input_released(u8 mask)
{
return (u8)((kq_input_release_keys & mask) != 0);
}input_repeat
判断掩码选中的按钮是否产生初次按下或定时连发脉冲。
语法
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(),第二次调用会清除新按下事件。
重复触发按更新次数计时,不是毫秒。新按下时立即触发。默认 INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 时,19 次更新后再次触发,此后每 6 次更新触发一次。各按钮独立计数。修改设置时,编译 input.c 也必须使用相同宏值;只修改调用方的头文件不会改变库的设置。
使用示例
if (input_repeat(BTN_A)!=0) {
// The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
previous_pulse=held_ticks; repeat_count++;
if (input_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:42
Return whether any masked button produced a fresh press or scheduled repeat pulse.
kitaqgb/lib/input.c:86
u8 input_repeat(u8 mask)
{
return (u8)((kq_input_repeat_keys & mask) != 0);
}input_update
将当前掩码保存为上次状态,通过 __readpadex 读取 Game Boy 按钮,计算松开事件,并推进各按钮独立的重复计时器。每个游戏输入周期调用一次,再查询缓存状态。
语法
void input_update();返回值
无(void)。
使用条件与限制
通用掩码为 BTN_RIGHT=0x01、BTN_LEFT=0x02、BTN_UP=0x04、BTN_DOWN=0x08、BTN_A=0x10、BTN_B=0x20、BTN_SELECT=0x40、BTN_START=0x80。位为 1 表示按住。GB 和 FC 的 input_* 均使用此格式;FC 原始手柄函数的位序不同。
重复触发按更新次数计时,不是毫秒。新按下时立即触发。默认 INPUT_REPEAT_DELAY=18、INPUT_REPEAT_RATE=5 时,19 次更新后再次触发,此后每 6 次更新触发一次。各按钮独立计数。修改设置时,编译 input.c 也必须使用相同宏值;只修改调用方的头文件不会改变库的设置。
使用示例
input_update(); // One update per input tick, followed by any number of queries.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
此程序演示持续按下、刚按下、刚松开和自动连发之间的区别。按住A达40次更新后松开。FIRST DOWN、FIRST PRESS、FIRST REPEAT为1,FIRST CURRENT为16。HELD TICKS为40,PRESS COUNT为1,REPEAT COUNT为5。松开后,RELEASE为1,LAST CURRENT为0,LAST PREVIOUS为16。FAILED CHECKS应为0。改变按住的时长会改变计数;程序会根据实际观测到的时长检查连发间隔。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\input.c .\kitaq-docs\samples\api-examples\gb\input_edges.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_edges.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache实现与源码
kitaqgb/lib/input.h:34
Sample input once per game tick and derive press, release and repeat masks. Repeat delays are measured in calls to this function, not elapsed wall-clock time.
kitaqgb/lib/input.c:35
void input_update()
{
u8 i;
u16 kt;
kq_input_prev_keys = kq_input_keys;
// The intrinsic packs held buttons in the low byte and new presses in the high byte.
kt = __readpadex(kq_input_prev_keys);
kq_input_keys = (u8)kt;
kq_input_press_keys = (u8)(kt >> 8);
kq_input_release_keys = (u8)(kq_input_prev_keys & (u8)(kq_input_keys ^ 0xFF));
kq_input_repeat_keys = kq_input_press_keys;
i = 0;
while (i < 8) {
u8 mask = input_mask_for_index(i);
if ((kq_input_keys & mask) != 0) {
if ((kq_input_press_keys & mask) != 0) {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
} else if (input_repeat_timer[i] != 0) {
input_repeat_timer[i]--;
} else {
kq_input_repeat_keys = (u8)(kq_input_repeat_keys | mask);
input_repeat_timer[i] = INPUT_REPEAT_RATE;
}
} else {
input_repeat_timer[i] = INPUT_REPEAT_DELAY;
}
i++;
}
}link.h — 串行通信与逻辑数据包
本库提供三个层次:字节交换、最多 24 字节的数据包,以及按对端划分的队列。先学习 BYTE MASTER/SLAVE,再学习 PACKET 和 LINK4,理解时钟归属和接收就绪状态。程序须恰好包含一次硬件寄存器单元、link.c 和 link_packet.c;gb_link_example.h 提供了正确顺序。
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
两端都要持续轮询。超时按连续 90 次无进展调用计算,不是硬件帧数。仅为提供 ACK 时钟而发送 0xFF 不会延长期限;最多尝试发送三次。重试可能造成重复交付,因此要求命令恰好执行一次时,应添加应用层标识符。
Link4_BeginTransferTo
选择目标,然后启动一次字节交换。
语法
u8 __stackcall Link4_BeginTransferTo(u8 peer_slot, u8 out);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
out要发送的八位值。
返回值
返回选择或启动时遇到的第一个错误;成功返回 0。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[6+slot]=Link4_BeginTransferTo(slot,16+slot);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:129
Select the logical peer and start a byte exchange, returning the first error.
kitaqgb/lib/link.c:584
u8 __stackcall Link4_BeginTransferTo(u8 peer_slot, u8 out) {
u8 err = Link4_SelectPeer(peer_slot);
if (err != LINK_ERR_NONE) return err;
return Link_BeginTransfer(out);
}Link4_GetLastRxPeer
读取最近一个已完成字节所属的对端。
语法
u8 Link4_GetLastRxPeer();返回值
主机返回所选对端,对端返回 0;尚未接收时返回 255。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[19]=Link4_GetLastRxPeer();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:121
Return the peer attributed to the last completed byte transfer.
kitaqgb/lib/link.c:540
u8 Link4_GetLastRxPeer() {
return Link4_LastRxPeer;
}Link4_GetLastTxPeer
读取最近一次字节传送启动时记录的目标。
语法
u8 Link4_GetLastTxPeer();返回值
返回记录的目标;尚未启动过传送时返回 255。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[20]=Link4_GetLastTxPeer();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:123
Return the peer attributed when the last byte transfer was started.
kitaqgb/lib/link.c:545
u8 Link4_GetLastTxPeer() {
return Link4_LastTxPeer;
}Link4_GetLocalSlot
读取本机配置的槽位。
语法
u8 Link4_GetLocalSlot();返回值
主机为 0,对端为 1..3。未配置时也返回 0。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[2]=Link4_GetLocalSlot();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:115
Return the configured zero-based local slot.
kitaqgb/lib/link.c:525
u8 Link4_GetLocalSlot() {
return Link4_LocalSlot;
}Link4_GetMode
读取已配置的逻辑角色。
语法
u8 Link4_GetMode();返回值
0=未配置、1=主机、2=对端。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[1]=Link4_GetMode();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:113
Return the configured cooperative role; this is not a hardware connection probe.
kitaqgb/lib/link.c:520
u8 Link4_GetMode() {
return Link4_ModeState;
}Link4_GetSelectedPeer
读取当前选择的逻辑目标。
语法
u8 Link4_GetSelectedPeer();返回值
主机返回所选对端;对端返回 0;未配置返回 255。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[4]=Link4_GetSelectedPeer();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:119
Return the current logical destination, or the invalid-slot sentinel when unset.
kitaqgb/lib/link.c:535
u8 Link4_GetSelectedPeer() {
return Link4_SelectedPeer;
}Link4_GetSlotCount
读取已配置的总槽位数。
语法
u8 Link4_GetSlotCount();返回值
有效配置返回 2..4,未配置返回 0。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
这些函数读取配置或历史记录。不检测实体连接、不查询所有对端,也不等待传送完成。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[3]=Link4_GetSlotCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:117
Return the configured number of logical slots, not a live connection count.
kitaqgb/lib/link.c:530
u8 Link4_GetSlotCount() {
return Link4_SlotCount;
}Link4_HasByteFrom
检查某个对端的接收环形队列是否有未读数据。
语法
u8 __stackcall Link4_HasByteFrom(u8 peer_slot);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
返回值
有排队数据返回 1,队列为空或对端无效时返回 0。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
此查询不轮询硬件;无效对端不会改变错误锁存值。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[9+slot]=Link4_HasByteFrom(slot);value=0;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:131
Inspect a valid peer ring without polling the hardware.
kitaqgb/lib/link.c:591
u8 __stackcall Link4_HasByteFrom(u8 peer_slot) {
if (Link4_IsValidRemoteSlotInternal(peer_slot) == 0) return 0;
if (Link4_RxHead[(__safe_index u8)peer_slot] == Link4_RxTail[(__safe_index u8)peer_slot]) return 0;
return 1;
}Link4_HasPacketFrom
检查某个远端槽位的完整数据包邮箱。
语法
u8 __stackcall Link4_HasPacketFrom(u8 peer_slot);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
返回值
有未读数据包返回 1,邮箱为空或对端无效时返回 0。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
此查询不轮询硬件;无效对端不会改变错误锁存值。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[9]=Link4_HasPacketFrom(0);result[10]=Link4_ReadPacketFrom(0,&cmd,&len,data);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
作为四槽位配置中的对端 1,预期 HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42。CONSUMED=0 是消费后的主机邮箱标志。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:142
Inspect the one-packet mailbox for a valid remote slot without consuming it.
kitaqgb/lib/link_packet.c:484
u8 __stackcall Link4_HasPacketFrom(u8 peer_slot) {
return Link4_PacketPeerReady(peer_slot);
}Link4_InitHost
初始化拥有两至四个槽位的逻辑主机,并选择对端 1。
语法
u8 __stackcall Link4_InitHost(u8 slot_count);参数
slot_count包括主机在内的槽位数,范围为 2..4。
返回值
配置有效时返回 0,槽位数无效时返回 4。
使用条件与限制
初始化会丢弃字节队列、数据包、对端选择和错误,并关闭传送及串行 IRQ 源。不保留正在进行的交换。
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
先初始化再验证。因此调用失败也会丢弃先前拓扑和未读数据。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[0]=Link4_InitHost(4);result[1]=Link4_GetMode();result[2]=Link4_GetLocalSlot();result[3]=Link4_GetSlotCount();result[4]=Link4_GetSelectedPeer();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:108
Cooperative 4-player adapter helpers. This layer models the common host-selected adapter pattern: - Slot 0 is always the host. - The host selects one active peer slot at a time. - Packet mailboxes stay one-deep per peer. - The original Link_* API remains available for normal 2-player projects. Reset as master, validate slot count and select logical peer one. Invalid configuration still discards old link state.
kitaqgb/lib/link.c:489
u8 __stackcall Link4_InitHost(u8 slot_count) {
u8 err;
Link_InitMaster();
err = Link4_ValidateConfig(LINK4_MODE_HOST, LINK4_HOST_SLOT, slot_count);
if (err != LINK_ERR_NONE) {
Link_SetErrorInternal(err);
return err;
}
Link4_ApplyConfig(LINK4_MODE_HOST, LINK4_HOST_SLOT, slot_count);
return LINK_ERR_NONE;
}Link4_InitPeer
初始化使用外部时钟的对端,并选择主机槽位 0。
语法
u8 __stackcall Link4_InitPeer(u8 local_slot, u8 slot_count);参数
local_slot本地槽位,范围为 1..slot_count−1。
slot_count包含主机在内的总槽位数,范围为 2..4。
返回值
配置有效时返回 0,槽位数或本地槽位无效时返回 4。
使用条件与限制
初始化会丢弃字节队列、数据包、对端选择和错误,并关闭传送及串行 IRQ 源。不保留正在进行的交换。
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
先初始化再验证。因此调用失败也会丢弃先前拓扑和未读数据。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[0]=Link4_InitPeer(1,4);result[1]=Link4_GetMode();result[2]=Link4_GetLocalSlot();result[3]=Link4_GetSlotCount();result[4]=Link4_GetSelectedPeer();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
作为四槽位配置中的对端 1,预期 HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42。CONSUMED=0 是消费后的主机邮箱标志。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:111
Reset as slave and validate the local nonzero slot and total slot count. An invalid configuration still discards the previous link state.
kitaqgb/lib/link.c:505
u8 __stackcall Link4_InitPeer(u8 local_slot, u8 slot_count) {
u8 err;
Link_InitSlave();
err = Link4_ValidateConfig(LINK4_MODE_PEER, local_slot, slot_count);
if (err != LINK_ERR_NONE) {
Link_SetErrorInternal(err);
return err;
}
Link4_ApplyConfig(LINK4_MODE_PEER, local_slot, slot_count);
return LINK_ERR_NONE;
}Link4_PollPacket
针对当前所选对端,将共享数据包引擎推进一次。
语法
void Link4_PollPacket();返回值
无返回值。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
两端都要持续轮询。超时按连续 90 次无进展调用计算,不是硬件帧数。仅为提供 ACK 时钟而发送 0xFF 不会延长期限;最多尝试发送三次。重试可能造成重复交付,因此要求命令恰好执行一次时,应添加应用层标识符。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
不会轮流访问全部槽位。只有交换结束且允许选择时,才能改变对端。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
for(i=0;i<50;i++){Link4_PollPacket();while(Link4_TryReadByteFrom(slot,0)){}m_wait();}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:140
Advance the shared packet engine; this does not poll every logical peer in turn.
kitaqgb/lib/link_packet.c:479
void Link4_PollPacket() {
Link_PollPacket();
}Link4_ReadPacketFrom
从指定对端复制数据包,并消费其邮箱。
语法
u8 __stackcall Link4_ReadPacketFrom(u8 peer_slot, u8 *cmd, u8 *len, u8 *dst);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
cmd可写命令字节;为 null 时不输出。
len可写长度字节,范围为 0..24;为 null 时不输出。
dst可容纳 24 字节的可写有效载荷缓冲区;为 null 时丢弃有效载荷。
返回值
消费成功返回 1,邮箱为空或对端无效时返回 0。
使用条件与限制
没有目标容量参数。使用 dst 时须提供 24 个可写字节。cmd、len 和 dst 都可以分别为 null。邮箱检查与消费必须与其他代码及中断互斥。
只有共享邮箱引用同一对端的数据包时,才清除共享邮箱。其他对端的未读数据包仍可用。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[9]=Link4_HasPacketFrom(0);result[10]=Link4_ReadPacketFrom(0,&cmd,&len,data);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
作为四槽位配置中的对端 1,预期 HOST BYTE=17、COMMAND=65、LENGTH=3、PAYLOAD 2=42。CONSUMED=0 是消费后的主机邮箱标志。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:146
Consume a peer mailbox, copying to optional outputs. A nonnull destination needs room for the entire payload. Clear the shared mailbox only when it refers to this same peer.
kitaqgb/lib/link_packet.c:491
u8 __stackcall Link4_ReadPacketFrom(u8 peer_slot, u8 *cmd, u8 *len, u8 *dst) {
u8 i;
u8 offset;
if (Link4_PacketPeerReady(peer_slot) == 0) return 0;
if (cmd != 0) *cmd = Link4_ReceivedPacketCmd[(__safe_index u8)peer_slot];
if (len != 0) *len = Link4_ReceivedPacketLen[(__safe_index u8)peer_slot];
if (dst != 0) {
for (i = 0; i < Link4_ReceivedPacketLen[(__safe_index u8)peer_slot]; ++i) {
offset = Link4_PacketOffset(peer_slot, i);
dst[(__safe_index u8)i] = Link4_ReceivedPacketBuf[(__safe_index u8)offset];
}
}
Link4_ReceivedPacketReady[(__safe_index u8)peer_slot] = 0;
if (Link4_LastPacketPeer == peer_slot) {
Link_ReceivedPacketReady = 0;
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
}
return 1;
}Link4_SelectPeer
轮询完成状态,仅在空闲时改变逻辑目标。
语法
u8 __stackcall Link4_SelectPeer(u8 peer_slot);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
返回值
选择成功返回 0;字节/数据包/ACK 状态忙时返回 1;未配置或对端无效时返回 4。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[25]=Link4_SelectPeer(1);result[26]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:127
Poll completion, then change the logical destination only while byte and packet state are idle. A peer can select only the host. No adapter-routing command is sent here; the transport must provide the matching physical route.
kitaqgb/lib/link.c:552
u8 __stackcall Link4_SelectPeer(u8 peer_slot) {
Link_Poll();
if (Link4_IsEnabledInternal() == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (Link_Busy != 0 || Link_PacketState != LINK_PKT_STATE_IDLE || Link_PendingAck != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
if (Link4_ModeState == LINK4_MODE_HOST) {
if (Link4_IsValidRemoteSlotInternal(peer_slot) == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
Link4_SelectedPeer = peer_slot;
return LINK_ERR_NONE;
}
if (Link4_ModeState == LINK4_MODE_PEER && peer_slot == LINK4_HOST_SLOT) {
Link4_SelectedPeer = LINK4_HOST_SLOT;
return LINK_ERR_NONE;
}
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}Link4_SendPacketTo
选择逻辑对端,复制数据包并安排发送。
语法
u8 __stackcall Link4_SendPacketTo(u8 peer_slot, const u8 *data, u8 len, u8 cmd);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
data含 len 个字节的可读源数据;只有 len 为零时才可为 null。
len有效载荷长度,范围为 0..24 字节。
cmd应用定义的八位命令。
返回值
返回选择或入队时的第一个错误;0 表示已接受。
使用条件与限制
Link4 管理逻辑主机槽位 0 和对端槽位 1–3。SelectPeer 不会发送切换实体接线的命令。KOKURA 的 link4 拓扑通过编译器元数据跟随所选对端。实体 DMG-07 协议请使用 LinkDmg07。
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
两端都要持续轮询。超时按连续 90 次无进展调用计算,不是硬件帧数。仅为提供 ACK 时钟而发送 0xFF 不会延长期限;最多尝试发送三次。重试可能造成重复交付,因此要求命令恰好执行一次时,应添加应用层标识符。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
先选择目标,再验证有效载荷;因此即使有效载荷被拒绝,新目标仍可能保持选中。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[21+slot]=Link4_SendPacketTo(slot,data,3,64+slot);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:138
Select a logical peer, then copy and schedule a packet; return the first error.
kitaqgb/lib/link_packet.c:472
u8 __stackcall Link4_SendPacketTo(u8 peer_slot, const u8 *data, u8 len, u8 cmd) {
u8 err = Link4_SelectPeer(peer_slot);
if (err != LINK_ERR_NONE) return err;
return Link_SendPacket(data, len, cmd);
}Link4_TryReadByteAny
按槽位升序扫描远端,弹出第一个可用字节。
语法
u8 __stackcall Link4_TryReadByteAny(u8 *peer_slot, u8 *out);参数
peer_slot可写的来源槽位字节;为 null 时不输出来源。
out可写字节目标;为 null 时丢弃字节。
返回值
收到数据返回 1,否则返回 0。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
跳过本地槽位。持续有数据的较小编号槽位优先;若要求轮询公平性,请按对端分别读取。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
else {other=255;Link4_TryReadByteAny(&other,&value);result[12+slot]=other;}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:136
Scan remote rings in increasing slot order and return the first available byte. Null output pointers are allowed; lower slots have polling priority.
kitaqgb/lib/link.c:604
u8 __stackcall Link4_TryReadByteAny(u8 *peer_slot, u8 *out) {
u8 slot;
if (Link4_IsEnabledInternal() == 0) return 0;
for (slot = 0; slot < Link4_SlotCount; ++slot) {
if (slot != Link4_LocalSlot) {
if (Link4_PopRxByte(slot, out) != 0) {
if (peer_slot != 0) *peer_slot = slot;
return 1;
}
}
}
return 0;
}Link4_TryReadByteFrom
从指定对端的接收环形队列弹出一个字节。
语法
u8 __stackcall Link4_TryReadByteFrom(u8 peer_slot, u8 *out);参数
peer_slot已配置的远端槽位:主机可选 1..slot_count−1,对端可选 0。
out可写字节目标;为 null 时丢弃字节。
返回值
弹出成功返回 1;队列为空或对端无效时返回 0,并保留 out。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
if(slot==1)result[13]=Link4_TryReadByteFrom(slot,&value);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
用 KOKURA 的 link4 拓扑连接全部四个 ROM。主机从对端 1/2/3 接收 161/162/163,并显示 LAST PEER=3。随后向各对端发送命令 65/66/67,有效载荷均为 7/42/255;还需在每个对端画面检查这些值。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_host.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_host.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer1.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer1.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer2.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_four_peer3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_four_peer3.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology link4 --run-frames 360 --link-session "name=four_host|slot=0|rom=.\out\link_four_host.gb|save_state=.\out\four_host.kqs" --link-session "name=four_peer1|slot=1|rom=.\out\link_four_peer1.gb|save_state=.\out\four_peer1.kqs" --link-session "name=four_peer2|slot=2|rom=.\out\link_four_peer2.gb|save_state=.\out\four_peer2.kqs" --link-session "name=four_peer3|slot=3|rom=.\out\link_four_peer3.gb|save_state=.\out\four_peer3.kqs"
.\kokura\kokura-cli.exe .\out\link_four_host.gb --hardware cgb --load-state .\out\four_host.kqs --run-frames 2 --png .\out\four_host.png
.\kokura\kokura-cli.exe .\out\link_four_peer1.gb --hardware cgb --load-state .\out\four_peer1.kqs --run-frames 2 --png .\out\four_peer1.png
.\kokura\kokura-cli.exe .\out\link_four_peer2.gb --hardware cgb --load-state .\out\four_peer2.kqs --run-frames 2 --png .\out\four_peer2.png
.\kokura\kokura-cli.exe .\out\link_four_peer3.gb --hardware cgb --load-state .\out\four_peer3.kqs --run-frames 2 --png .\out\four_peer3.png实现与源码
kitaqgb/lib/link.h:133
Pop one byte from the selected peer ring; null output discards it.
kitaqgb/lib/link.c:598
u8 __stackcall Link4_TryReadByteFrom(u8 peer_slot, u8 *out) {
return Link4_PopRxByte(peer_slot, out);
}Link_BeginTransfer
写入 SB,并启动或准备一次全双工字节交换。
语法
u8 __stackcall Link_BeginTransfer(u8 out);参数
out要发送的八位值;即使只关心接收,每次交换也会发送一个字节。
返回值
成功启动/准备时返回 0;已忙时返回 1。
使用条件与限制
每台主机只有一个共享通信状态。字节交换为全双工:一端提供内部时钟,另一端使用外部时钟。普通速度每位需要 512 个时钟周期,CGB 高速需要 16 个。
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
外部时钟端等待另一端提供时钟。忙时拒绝不会替换已经在传送的字节。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
lesson_delay(12);result[0]=Link_BeginTransfer(42);value=0;result[1]=Link_WaitByte(8,&value);result[2]=value;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 BYTE SLAVE。发送 42 并接收 99,然后双向交换零。RECEIVED=1、PEER BYTE=99、ZERO READY=1 和 ZERO BYTE=0 区分有效零数据与无数据。两个 CGB 实例在第二次交换时使用高速时钟。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:66
Start one full-duplex byte exchange, or return BUSY without replacing an active transfer. Slave mode arms reception and waits for external clocks.
kitaqgb/lib/link.c:362
u8 __stackcall Link_BeginTransfer(u8 out) {
u8 sc = LINK_SC_START;
if (Link_Busy != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
LinkHw_WriteSB(out);
Link_LastTx = out;
Link4_LastTxPeer = Link4_CurrentTransferPeerInternal();
Link_Busy = 1;
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
if (Link_Mode == LINK_MODE_MASTER) {
sc = (u8)(sc | LINK_SC_INTERNAL);
if (Link_FastClock != 0) sc = (u8)(sc | LINK_SC_FAST);
}
LinkHw_WriteSC(sc);
return LINK_ERR_NONE;
}Link_Cancel
停止活动中的或等待外部时钟的字节传送。
语法
void Link_Cancel();返回值
无返回值。
使用条件与限制
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
写入 SC=0,清除忙状态及串行请求状态。保留已接收字节、数据包状态和错误。若从头重新启动协议,还应重新初始化。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_Cancel();result[12]=Link_IsBusy();result[13]=(SC&128)!=0;Link_ClearError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:71
Stop the hardware transfer under interrupt protection. Queued receive bytes and packet-layer state remain available; this is not a full protocol reset.
kitaqgb/lib/link.c:396
void Link_Cancel() {
u8 tok = __critical_enter();
Link_Busy = 0;
LinkHw_WriteSC(0x00);
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
__critical_leave(tok);
}Link_ClearError
只清除错误锁存值。
语法
void Link_ClearError();返回值
无返回值。
使用条件与限制
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
传送、队列和数据包状态保持不变。用 Link_Cancel 停止字节传送,或重新初始化以丢弃全部状态。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_ClearError();result[8]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:62
Clear the error latch without resetting queues or an active transfer.
kitaqgb/lib/link.c:356
void Link_ClearError() {
Link_LastErrorCode = LINK_ERR_NONE;
}Link_HasByte
检查普通接收环形队列是否有未读数据。
语法
u8 Link_HasByte();返回值
队列中有数据时返回 1,否则返回 0。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
不轮询硬件。请先通过 Link_Poll 或串行处理程序收取完成结果。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[1]=Link_HasByte();value=77;result[2]=Link_TryReadByte(&value);result[3]=value;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE MASTER 提供时钟。预期 PEER BYTE=42、ZERO READY=1、ZERO BYTE=0。随后 EMPTY READ 也为零,因此必须用就绪状态或 TryRead 返回值区分空队列。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:58
Read the ordinary receive-ready flag without polling hardware.
kitaqgb/lib/link.c:346
u8 Link_HasByte() {
return Link_RxReady;
}Link_HasPacket
检查共享的完整数据包邮箱。
语法
u8 Link_HasPacket();返回值
有未读数据包返回 1,否则返回 0。
使用条件与限制
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
只读取标志,不推进协议。不表示发送方是否已收到 ACK。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[0]=Link_HasPacket();cmd=0;len=0;result[1]=Link_ReadPacket(&cmd,&len,data);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 PACKET MASTER,检查 COMMAND=49、LENGTH=3,以及有效载荷 7/42/255。ReadPacket 后 CONSUMED=0 表示邮箱已空。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png实现与源码
kitaqgb/lib/link.h:94
Inspect the shared completed-packet flag without advancing the protocol.
kitaqgb/lib/link_packet.c:441
u8 Link_HasPacket() {
return Link_ReceivedPacketReady;
}Link_InitCommon
将共享通信状态重置为默认的外部时钟模式。
语法
void Link_InitCommon();返回值
无返回值。
使用条件与限制
每台主机只有一个共享通信状态。字节交换为全双工:一端提供内部时钟,另一端使用外部时钟。普通速度每位需要 512 个时钟周期,CGB 高速需要 16 个。
初始化会丢弃字节队列、数据包、对端选择和错误,并关闭传送及串行 IRQ 源。不保留正在进行的交换。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_InitCommon();result[0]=Link_IsBusy();result[1]=Link_HasByte();result[2]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:43
Reset byte queues, packet mailboxes, cooperative-peer state and errors, then disable serial transfers and serial IRQ delivery. Existing data is discarded.
kitaqgb/lib/link.c:243
void Link_InitCommon() {
u8 i;
Link_Mode = LINK_MODE_SLAVE;
Link_UseInterrupt = 0;
Link_FastClock = LINK_CLOCK_NORMAL;
Link_Busy = 0;
Link_LastRx = 0xFF;
Link_LastTx = 0xFF;
Link_RxReady = 0;
Link_LastErrorCode = LINK_ERR_NONE;
Link_PacketState = LINK_PKT_STATE_IDLE;
Link_TxSeq = 0;
Link_RxSeq = 0;
Link4_ModeState = LINK4_MODE_NONE;
Link4_LocalSlot = LINK4_HOST_SLOT;
Link4_SlotCount = 0;
Link4_SelectedPeer = LINK4_INVALID_SLOT;
Link4_LastRxPeer = LINK4_INVALID_SLOT;
Link4_LastTxPeer = LINK4_INVALID_SLOT;
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
Link_RxHead = 0;
Link_RxTail = 0;
Link_TxHead = 0;
Link_TxTail = 0;
Link_CurrentPacketCmd = 0;
Link_CurrentPacketLen = 0;
Link_CurrentPacketChecksum = 0;
Link_PendingAck = 0;
Link_ReceivedPacketReady = 0;
Link_ReceivedPacketCmd = 0;
Link_ReceivedPacketLen = 0;
Link_ReceivedPacketChecksum = 0;
Link_PacketRetryCount = 0;
Link_PacketTimer = 0;
for (i = 0; i < LINK_RX_QUEUE_SIZE; ++i) Link_RxQueue[(__safe_index u8)i] = 0;
for (i = 0; i < LINK_TX_QUEUE_SIZE; ++i) Link_TxQueue[(__safe_index u8)i] = 0;
for (i = 0; i < LINK4_MAX_PLAYERS; ++i) {
Link4_RxHead[(__safe_index u8)i] = 0;
Link4_RxTail[(__safe_index u8)i] = 0;
Link4_ReceivedPacketReady[(__safe_index u8)i] = 0;
Link4_ReceivedPacketCmd[(__safe_index u8)i] = 0;
Link4_ReceivedPacketLen[(__safe_index u8)i] = 0;
}
for (i = 0; i < (u8)(LINK4_MAX_PLAYERS * LINK_RX_QUEUE_SIZE); ++i) {
Link4_RxQueue[(__safe_index u8)i] = 0;
}
for (i = 0; i < LINK_PKT_MAX_PAYLOAD; ++i) {
Link_CurrentPacketBuf[(__safe_index u8)i] = 0;
Link_ReceivedPacketBuf[(__safe_index u8)i] = 0;
}
for (i = 0; i < (u8)(LINK4_MAX_PLAYERS * LINK_PKT_MAX_PAYLOAD); ++i) {
Link4_ReceivedPacketBuf[(__safe_index u8)i] = 0;
}
LinkHw_WriteSB(0xFF);
LinkHw_WriteSC(0x00);
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
Link_SetSerialInterruptEnabled(0);
}Link_InitMaster
重置状态,并将本机设为时钟源。
语法
void Link_InitMaster();返回值
无返回值。
使用条件与限制
每台主机只有一个共享通信状态。字节交换为全双工:一端提供内部时钟,另一端使用外部时钟。普通速度每位需要 512 个时钟周期,CGB 高速需要 16 个。
初始化会丢弃字节队列、数据包、对端选择和错误,并关闭传送及串行 IRQ 源。不保留正在进行的交换。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_InitMaster();Link_SetUseInterrupt(0);Link_SetFastClock(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 BYTE SLAVE。发送 42 并接收 99,然后双向交换零。RECEIVED=1、PEER BYTE=99、ZERO READY=1 和 ZERO BYTE=0 区分有效零数据与无数据。两个 CGB 实例在第二次交换时使用高速时钟。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:45
Reset link state and select the internal-clock role for subsequent transfers.
kitaqgb/lib/link.c:309
void Link_InitMaster() {
Link_InitCommon();
Link_Mode = LINK_MODE_MASTER;
}Link_InitSlave
重置状态,并选择外部时钟通信。
语法
void Link_InitSlave();返回值
无返回值。
使用条件与限制
每台主机只有一个共享通信状态。字节交换为全双工:一端提供内部时钟,另一端使用外部时钟。普通速度每位需要 512 个时钟周期,CGB 高速需要 16 个。
初始化会丢弃字节队列、数据包、对端选择和错误,并关闭传送及串行 IRQ 源。不保留正在进行的交换。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_InitSlave();result[0]=Link_BeginTransfer(99);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE MASTER 提供时钟。预期 PEER BYTE=42、ZERO READY=1、ZERO BYTE=0。随后 EMPTY READ 也为零,因此必须用就绪状态或 TryRead 返回值区分空队列。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:47
Reset link state and select the external-clock role for subsequent transfers.
kitaqgb/lib/link.c:315
void Link_InitSlave() {
Link_InitCommon();
Link_Mode = LINK_MODE_SLAVE;
}Link_IsBusy
轮询一次完成状态,然后报告交换是否仍在进行。
语法
u8 Link_IsBusy();返回值
忙时返回 1,否则返回 0。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
这不是被动查询:已完成的 SB 字节会入队,可能改变就绪状态和错误状态。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[3]=Link_IsBusy();result[4]=Link_HasByte();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 BYTE SLAVE。发送 42 并接收 99,然后双向交换零。RECEIVED=1、PEER BYTE=99、ZERO READY=1 和 ZERO BYTE=0 区分有效零数据与无数据。两个 CGB 实例在第二次交换时使用高速时钟。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:56
Poll once before returning busy state, so this query may collect a completed byte.
kitaqgb/lib/link.c:340
u8 Link_IsBusy() {
Link_Poll();
return Link_Busy;
}Link_LastError
读取最近一次锁存的通信错误,不清除它。
语法
u8 Link_LastError();返回值
返回当前错误码,范围为 0..5。
使用条件与限制
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[2]=Link_LastError();Link_ClearError();result[3]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
发送命令 49 和有效载荷 7/42/255,然后修改源数组。QUEUED=0 表示接受发送;SECOND SEND=1 表示拒绝第二次启动。该忙错误会保持锁存,直到调用 ClearError。接收端的 PAYLOAD 2=42 证明队列持有有效载荷的独立副本。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png实现与源码
kitaqgb/lib/link.h:60
Return the latest latched error without clearing it.
kitaqgb/lib/link.c:351
u8 Link_LastError() {
return Link_LastErrorCode;
}Link_OnSerialIRQ
由已经确认串行请求的处理程序收取一个完成的字节。
语法
void Link_OnSerialIRQ();返回值
无返回值。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
函数不检查 SC 是否完成。串行传送仍活动时,不要从无关的定时器中断调用。示例在真实 IF.serial 请求出现后、串行 CPU IRQ 源被屏蔽的情况下显式执行它;测试的是主体,不是向量安装。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
lesson_delay(2);result[14]=(IF_REG&8)!=0;Link_OnSerialIRQ();result[15]=Link_HasByte();result[16]=Link_ReadByte();result[17]=(IF_REG&8)!=0;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:74
Acknowledge serial IRQ and finish a software-busy transfer. Call this from the actual serial interrupt handler, not an unrelated interrupt.
kitaqgb/lib/link.c:410
void Link_OnSerialIRQ() {
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & (u8)~LINK_IF_SERIAL));
if (Link_Busy == 0) return;
Link_FinishTransfer();
}Link_Poll
仅在 SC 启动位清零后收取忙传送的结果,并将接收字节入队。
语法
void Link_Poll();返回值
无返回值。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
调用不阻塞。软件状态空闲时不执行操作,也不实现超时。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
for(i=0;i<24;i++){Link_Poll();if(Link_HasByte())break;m_wait();}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE MASTER 提供时钟。预期 PEER BYTE=42、ZERO READY=1、ZERO BYTE=0。随后 EMPTY READ 也为零,因此必须用就绪状态或 TryRead 返回值区分空队列。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:68
Collect a pending transfer only after hardware clears the SC start bit.
kitaqgb/lib/link.c:388
void Link_Poll() {
if (Link_Busy == 0) return;
if ((LinkHw_ReadSC() & LINK_SC_START) != 0) return;
Link_FinishTransfer();
}Link_PollPacket
解析接收字节,启动下一个发送字节或 ACK,并推进超时/重试状态。
语法
void Link_PollPacket();返回值
无返回值。
使用条件与限制
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
两端都要持续轮询。超时按连续 90 次无进展调用计算,不是硬件帧数。仅为提供 ACK 时钟而发送 0xFF 不会延长期限;最多尝试发送三次。重试可能造成重复交付,因此要求命令恰好执行一次时,应添加应用层标识符。
ReadByte 一类原始读取也会消费同一个普通环形队列,可能取走数据包的一部分。不要混用原始读取与数据包接收。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_InitSlave();for(i=0;i<120;i++){Link_PollPacket();m_wait();}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 PACKET MASTER,检查 COMMAND=49、LENGTH=3,以及有效载荷 7/42/255。ReadPacket 后 CONSUMED=0 表示邮箱已空。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png使用示例
Link_InitMaster();result[0]=Link_SendPacket(0,0,7);
// An absent peer cannot acknowledge the packet, despite completed FF exchanges.
for(ticks=0;ticks<300;ticks++){Link_PollPacket();m_wait();}
result[1]=Link_LastError();Link_Cancel();result[2]=Link_IsBusy();Link_ClearError();result[3]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不连接对端进行发送。内部时钟会使字节传送完成,但收不到 ACK;重试后 NO ACK ERROR=5。取消后 CANCEL BUSY=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_deadline.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_deadline.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_deadline.gb --hardware cgb --run-frames 330 --png .\out\deadline.png实现与源码
kitaqgb/lib/link.h:92
Drain received bytes, advance one outgoing exchange and update the retry timer. Timeout ticks count calls without progress, not hardware frames; call with a controlled cadence and avoid mixing raw reads with packet consumption.
kitaqgb/lib/link_packet.c:400
void Link_PollPacket() {
u8 progress = 0;
u8 previous_state;
u8 value = 0;
Link_Poll();
if (Link_PacketState == LINK_PKT_STATE_ERROR) {
Link_PacketGoIdle();
return;
}
while (Link_TryReadByte(&value) != 0) {
if (Link_PacketConsumeByte(value) != 0) progress = 1;
}
if (Link_PacketState == LINK_PKT_STATE_SEND_ACK) {
if (Link_PacketStartAckTransfer() != 0) progress = 1;
} else if (Link_PacketIsSendState() != 0) {
// Clocking an ACK is not protocol progress: a missing peer must time out.
previous_state = Link_PacketState;
if (Link_PacketStartNextTransfer() != 0 && previous_state != LINK_PKT_STATE_WAIT_ACK) progress = 1;
} else {
if (Link_PacketStartReceiveTransfer() != 0) progress = 1;
}
if (Link_PacketState == LINK_PKT_STATE_IDLE) {
Link_PacketTimer = 0;
return;
}
if (progress != 0) {
Link_PacketTimer = LINK_PKT_TIMEOUT_TICKS;
return;
}
if (Link_PacketTimer != 0) Link_PacketTimer--;
if (Link_PacketTimer == 0) Link_PacketHandleTimeout();
}Link_ReadByte
返回从普通接收环形队列弹出的一个字节。
语法
u8 Link_ReadByte();返回值
返回接收字节;空队列也返回零。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
仅凭返回值无法区分收到的零与空队列。请使用 TryReadByte 的成功标志,或先调用 HasByte。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[6]=Link_HasByte();result[7]=Link_ReadByte();result[8]=Link_ReadByte();result[9]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE MASTER 提供时钟。预期 PEER BYTE=42、ZERO READY=1、ZERO BYTE=0。随后 EMPTY READ 也为零,因此必须用就绪状态或 TryRead 返回值区分空队列。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:80
Pop one byte, returning zero when empty. Use Link_TryReadByte when a received zero byte must be distinguished from an empty queue.
kitaqgb/lib/link.c:427
u8 Link_ReadByte() {
u8 value = 0;
if (Link_PopRxByte(&value) != 0) return value;
return 0;
}Link_ReadPacket
复制一个完整数据包,并消费共享邮箱。
语法
u8 __stackcall Link_ReadPacket(u8 *cmd, u8 *len, u8 *dst);参数
cmd可写命令字节;为 null 时不输出。
len可写长度字节,范围为 0..24;为 null 时不输出。
dst可容纳 24 字节的可写有效载荷缓冲区;为 null 时丢弃有效载荷。
返回值
消费成功返回 1,为空时返回 0;空读取不改变输出。
使用条件与限制
没有目标容量参数。使用 dst 时须提供 24 个可写字节。cmd、len 和 dst 都可以分别为 null。邮箱检查与消费必须与其他代码及中断互斥。
Link4 模式下,还会清除与该共享数据包对应的对端邮箱。其他对端邮箱仍可用。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[0]=Link_HasPacket();cmd=0;len=0;result[1]=Link_ReadPacket(&cmd,&len,data);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 PACKET MASTER,检查 COMMAND=49、LENGTH=3,以及有效载荷 7/42/255。ReadPacket 后 CONSUMED=0 表示邮箱已空。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png实现与源码
kitaqgb/lib/link.h:98
Consume the shared packet mailbox and its matching peer flag. Any output may be null; a nonnull destination needs room for the received payload because there is no destination-capacity argument.
kitaqgb/lib/link_packet.c:448
u8 __stackcall Link_ReadPacket(u8 *cmd, u8 *len, u8 *dst) {
u8 i;
u8 peer_slot = Link4_LastPacketPeer;
if (Link_ReceivedPacketReady == 0) return 0;
if (cmd != 0) *cmd = Link_ReceivedPacketCmd;
if (len != 0) *len = Link_ReceivedPacketLen;
if (dst != 0) {
for (i = 0; i < Link_ReceivedPacketLen; ++i) {
dst[(__safe_index u8)i] = Link_ReceivedPacketBuf[(__safe_index u8)i];
}
}
Link_ReceivedPacketReady = 0;
if (Link4_IsValidRemoteSlotInternal(peer_slot) != 0) {
Link4_ReceivedPacketReady[(__safe_index u8)peer_slot] = 0;
}
Link4_LastPacketPeer = LINK4_INVALID_SLOT;
return 1;
}Link_SendPacket
将有效载荷复制到库持有的存储空间,并安排数据包发送。
语法
u8 __stackcall Link_SendPacket(const u8 *data, u8 len, u8 cmd);参数
data含 len 个字节的可读源数据;只有 len 为零时才可为 null。
len有效载荷长度,范围为 0..24 字节。
cmd应用定义的八位命令。
返回值
接受时返回 0,数据包状态忙时返回 1,长度或指针无效时返回 4。
使用条件与限制
错误码:0=无错误、1=忙、2=超时、3=接收溢出、4=配置/协议无效、5=ACK 超时且响应类似断线。启动/配置调用成功返回 0;接收/弹出调用成功返回 1。成功调用不清除已锁存错误。
数据包包含 0xA5、命令、长度、0–24 个有效载荷字节和异或校验和。响应为 0x79(ACK,接受)或 0x1F(NAK,拒绝)。校验和对命令、长度和有效载荷求异或。每台主机有一个共享数据包邮箱;Link4 还为每个对端提供一个邮箱。
两端都要持续轮询。超时按连续 90 次无进展调用计算,不是硬件帧数。仅为提供 ACK 时钟而发送 0xFF 不会延长期限;最多尝试发送三次。重试可能造成重复交付,因此要求命令恰好执行一次时,应添加应用层标识符。
零表示已入队,不表示对端已经接收或确认。接受后可重用源数组。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[0]=Link_SendPacket(data,3,0x31);data[1]=0;result[1]=Link_SendPacket(data,3,0x32);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
发送命令 49 和有效载荷 7/42/255,然后修改源数组。QUEUED=0 表示接受发送;SECOND SEND=1 表示拒绝第二次启动。该忙错误会保持锁存,直到调用 ClearError。接收端的 PAYLOAD 2=42 证明队列持有有效载荷的独立副本。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_packet_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_packet_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=packet_master|slot=0|rom=.\out\link_packet_master.gb|save_state=.\out\packet_master.kqs" --link-session "name=packet_slave|slot=1|rom=.\out\link_packet_slave.gb|save_state=.\out\packet_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_packet_master.gb --hardware cgb --load-state .\out\packet_master.kqs --run-frames 2 --png .\out\packet_master.png
.\kokura\kokura-cli.exe .\out\link_packet_slave.gb --hardware cgb --load-state .\out\packet_slave.kqs --run-frames 2 --png .\out\packet_slave.png实现与源码
kitaqgb/lib/link.h:88
Validate payload size and idle packet state, copy payload bytes into owned storage and schedule transmission. Return success means queued, not acknowledged; keep polling to progress the exchange and inspect the error latch.
kitaqgb/lib/link_packet.c:361
u8 __stackcall Link_SendPacket(const u8 *data, u8 len, u8 cmd) {
u8 i;
u8 checksum = (u8)(cmd ^ len);
if (len > LINK_PKT_MAX_PAYLOAD) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (len != 0 && data == 0) {
Link_SetErrorInternal(LINK_ERR_PROTOCOL);
return LINK_ERR_PROTOCOL;
}
if (Link_PacketState != LINK_PKT_STATE_IDLE || Link_PendingAck != 0) {
Link_SetErrorInternal(LINK_ERR_BUSY);
return LINK_ERR_BUSY;
}
Link_CurrentPacketCmd = cmd;
Link_CurrentPacketLen = len;
for (i = 0; i < len; ++i) {
Link_CurrentPacketBuf[(__safe_index u8)i] = data[(__safe_index u8)i];
checksum = (u8)(checksum ^ data[(__safe_index u8)i]);
}
Link_CurrentPacketChecksum = checksum;
Link_TxSeq = 0;
Link_RxSeq = 0;
Link_PacketRetryCount = 0;
Link_PacketTimer = LINK_PKT_TIMEOUT_TICKS;
Link_PacketState = LINK_PKT_STATE_SEND_SYNC;
return LINK_ERR_NONE;
}Link_SetFastClock
选择后续内部时钟传送是否使用 CGB 高速时钟位。
语法
void __stackcall Link_SetFastClock(u8 on);参数
on零请求普通速度,非零请求高速。
返回值
无返回值。
使用条件与限制
每台主机只有一个共享通信状态。字节交换为全双工:一端提供内部时钟,另一端使用外部时钟。普通速度每位需要 512 个时钟周期,CGB 高速需要 16 个。
不改变正在进行的传送。外部时钟传送忽略此设置。不检查硬件是否支持;示例两端应使用匹配的硬件模式。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
lesson_delay(12);Link_SetFastClock(__cgb_is_cgb());result[5]=Link_BeginTransfer(0);value=77;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
连接 BYTE SLAVE。发送 42 并接收 99,然后双向交换零。RECEIVED=1、PEER BYTE=99、ZERO READY=1 和 ZERO BYTE=0 区分有效零数据与无数据。两个 CGB 实例在第二次交换时使用高速时钟。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:53
Select the fast-clock bit for future master transfers; this does not restart an active transfer or verify that the hardware supports CGB fast mode.
kitaqgb/lib/link.c:334
void __stackcall Link_SetFastClock(u8 on) {
if (on != 0) Link_FastClock = LINK_CLOCK_FAST;
else Link_FastClock = LINK_CLOCK_NORMAL;
}Link_SetUseInterrupt
设置串行中断使能位,并清除待处理的串行请求。
语法
void __stackcall Link_SetUseInterrupt(u8 on);参数
on零表示禁用,任意非零值表示启用。
返回值
无返回值。
使用条件与限制
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
须另行在向量 0058h 安装处理程序;本库不会注册。示例在传送前再次屏蔽该中断源,不测试 CPU 中断入口代码。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
Link_SetUseInterrupt(1);result[3]=(IE_REG&8)!=0;Link_SetUseInterrupt(0);result[4]=(IE_REG&8)!=0;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:50
Enable or disable serial IRQ delivery and clear its pending flag. Enabling does not install the handler. Leaving its outer critical section enables CPU interrupts.
kitaqgb/lib/link.c:322
void __stackcall Link_SetUseInterrupt(u8 on) {
if (on != 0) {
Link_UseInterrupt = 1;
Link_SetSerialInterruptEnabled(1);
} else {
Link_UseInterrupt = 0;
Link_SetSerialInterruptEnabled(0);
}
}Link_TryReadByte
从普通接收环形队列弹出一个字节,不等待。
语法
u8 __stackcall Link_TryReadByte(u8 *out);参数
out可写字节目标;为 null 时丢弃字节。
返回值
弹出成功返回 1,队列为空时返回 0;空读取不改变 out。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
内部临界区维护软件嵌套深度。退出最外层时会启用 CPU 中断,因此不要假定原先禁用的 IME 状态会保留。
Link4 会将每个接收字节同时复制到普通环形队列和按对端划分的队列。读取一个不会清空另一个。数据包轮询消费普通队列;还应定期丢弃未使用的各对端原始字节,以免溢出。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[1]=Link_HasByte();value=77;result[2]=Link_TryReadByte(&value);result[3]=value;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
BYTE MASTER 提供时钟。预期 PEER BYTE=42、ZERO READY=1、ZERO BYTE=0。随后 EMPTY READ 也为零,因此必须用就绪状态或 TryRead 返回值区分空队列。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_master.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_master.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_raw_slave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_raw_slave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology pair --run-frames 360 --link-session "name=raw_master|slot=0|rom=.\out\link_raw_master.gb|save_state=.\out\raw_master.kqs" --link-session "name=raw_slave|slot=1|rom=.\out\link_raw_slave.gb|save_state=.\out\raw_slave.kqs"
.\kokura\kokura-cli.exe .\out\link_raw_master.gb --hardware cgb --load-state .\out\raw_master.kqs --run-frames 2 --png .\out\raw_master.png
.\kokura\kokura-cli.exe .\out\link_raw_slave.gb --hardware cgb --load-state .\out\raw_slave.kqs --run-frames 2 --png .\out\raw_slave.png实现与源码
kitaqgb/lib/link.h:77
Pop the ordinary receive queue without waiting or polling hardware; null discards a byte.
kitaqgb/lib/link.c:417
u8 __stackcall Link_TryReadByte(u8 *out) {
return Link_PopRxByte(out);
}Link_WaitByte
立即轮询一次,再按 VBlank 次数限制等待字节。
语法
u8 __stackcall Link_WaitByte(u16 timeout_frames, u8 *out);参数
timeout_frames最多等待 VBlank 的次数。零仍会执行首次轮询和读取。
out必须提供的可写字节。为 null 时立即返回零,不改变错误锁存值。
返回值
收到数据返回 1;超时或输出为 null 时返回 0。
使用条件与限制
八槽接收环形队列最多容纳七个字节。队列已满时丢弃新字节,并锁存错误 3。接收到的零字节也是有效数据。
请在 LCD 运行时使用。超时会锁存错误 2,out 保持不变。传送本身不会取消;若放弃传送,请调用 Cancel。
画面来自 KOKURA。多端示例在连接的模拟器实例间检查数值,并从最终保存状态截取画面。这些检查不能证明实体通信线或四人适配器的行为。
在包含 kitaqgb、kokura 和 kitaq-docs 的父目录运行这些 PowerShell 命令。启动联机会话前,先构建所有参与的 ROM。Link4 要求生成的符号元数据保留在 ROM 旁。运行 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
value=77;result[9]=Link_WaitByte(2,&value);result[10]=value;result[11]=Link_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查初始化、忙时拒绝、外部时钟超时及环形队列容量。预期 BUSY ERROR=1、TIMEOUT=2、OPEN RX=255、OVERFLOW=3、QUEUE BYTES=7。观测到 IRQ REQUEST=1 后,示例在串行 IRQ 源仍被屏蔽时,显式执行处理程序主体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\link_control.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\link_control.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\link_control.gb --hardware cgb --run-frames 330 --png .\out\control.png实现与源码
kitaqgb/lib/link.h:83
Require an output pointer, poll immediately, then wait at most timeout_frames VBlanks for a byte. Zero timeout still performs the initial poll and read.
kitaqgb/lib/link.c:435
u8 __stackcall Link_WaitByte(u16 timeout_frames, u8 *out) {
u16 waited = 0;
if (out == 0) return 0;
Link_Poll();
if (Link_TryReadByte(out) != 0) return 1;
while (waited < timeout_frames) {
__wait_vblank();
waited++;
Link_Poll();
if (Link_TryReadByte(out) != 0) return 1;
}
Link_SetErrorInternal(LINK_ERR_TIMEOUT);
return 0;
}link_dmg07.h — DMG-07 外部时钟通信
DMG-07 将每台主机提交的一个字节广播给全部四名玩家。本库处理发现、玩家 1 启动、延迟数据包广播及对齐的重启控制。EXCHANGE 演示四机交换,NO ADAPTER 演示超时,OVERFLOW 演示未读数据处理,RESTART 演示恢复。
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
本驱动独占串行端口。不要同时运行普通 Link 或 Link4;link_hwregs_gb.c 和 link_dmg07.c 各包含一次。外部时钟由适配器提供;驱动不会选择 CGB 高速串行模式。
在整帧期间反复调用 Poll,并且每帧恰好调用一次 TickFrame。每个 VBlank 只调用一次 Poll,无法在适配器各字节之间及时重新准备端口。共享头文件中的 d_poll 检测进入 LY >= 144 的时刻,以调用一次 TickFrame。应串行执行两种操作,避免 ISR 与主循环发生竞态。
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
LinkDmg07_ClearError
将最新错误码重置为零。
语法
void LinkDmg07_ClearError();返回值
无返回值。
使用条件与限制
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
保留接收数据、阶段、连接信息和超时锁存标志。仅调用此函数不会重新连接或恢复接收。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
LinkDmg07_ClearError();result[21]=LinkDmg07_LastError();result[22]=LinkDmg07_GetErrorCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保留第一个有效数据包不读,直到下一个到达。预期 SENT SEQ=3、PACKET SEQ=2、ERROR=6;读取返回最新的 33/34/35/36。ClearError 使 CLEAR ERROR 变为零,但 ErrorCount 仍为一。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_overflow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_overflow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_overflow.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_overflow.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_overflow.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_overflow.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:125
Clear only the latest error code, preserving counters and protocol state.
kitaqgb/lib/link_dmg07.c:541
void LinkDmg07_ClearError() {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NONE;
}LinkDmg07_ConsumePingStatus
消费并清除已完成的发现通知。
语法
u8 LinkDmg07_ConsumePingStatus();返回值
成功消费返回 1,否则返回 0。
使用条件与限制
只清除通知,保留槽位、掩码和 LastStatus。再次调用会返回零,直到有新通知到达。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[3]=LinkDmg07_ConsumePingStatus();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:71
Clear and report the completed-discovery flag; return zero if no new status exists.
kitaqgb/lib/link_dmg07.c:418
u8 LinkDmg07_ConsumePingStatus() {
if (LinkDmg07_PingStatusReady == 0) return 0;
LinkDmg07_PingStatusReady = 0;
return 1;
}LinkDmg07_GetConnectedMask
读取最近一次接受的状态中,表示玩家连接情况的四位集合。
语法
u8 LinkDmg07_GetConnectedMask();返回值
位 0..3 表示已连接玩家。尚未发现时为零;四名玩家全部连接时为 15。
使用条件与限制
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
读取缓存的发现状态,不探测实体接线。传送期间反映最近一次接受的通知。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[1]=LinkDmg07_GetConnectedMask();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:65
Normalized mask: bit 0 is player 1, ..., bit 3 is player 4.
kitaqgb/lib/link_dmg07.c:403
u8 LinkDmg07_GetConnectedMask() {
return LinkDmg07_ConnectedMask;
}LinkDmg07_GetDisconnectCount
读取记录的丢失事件数:发现适配器后发生静默,或发现状态中的玩家消失。
语法
u8 LinkDmg07_GetDisconnectCount();返回值
返回 0..255 的事件数,不是丢失的玩家人数。
使用条件与限制
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
从未观测到适配器时发生超时,不会增加此计数。一次状态变化使多名玩家断开时,只计一次事件。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[8]=LinkDmg07_GetDisconnectCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不接适配器运行。预期 PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4。由于从未发现适配器,DISCONNECTS 保持零。空读取保留 EMPTY BUFFER=77;ClearError 使 CLEARED=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png实现与源码
kitaqgb/lib/link_dmg07.h:118
Return the saturating count of removed-player and previously-seen-adapter timeout events.
kitaqgb/lib/link_dmg07.c:525
u8 LinkDmg07_GetDisconnectCount() {
return LinkDmg07_DisconnectCount;
}LinkDmg07_GetErrorCount
读取接收协议错误和未读数据包被覆盖的累计次数。
语法
u8 LinkDmg07_GetErrorCount();返回值
返回 0..255。不包括请求验证错误、状态错误和超时。
使用条件与限制
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
接收邮箱只保留最新数据包。另一个数据包完成时,会覆盖未读数据、锁存错误 6,并增加 ErrorCount。适配器不会等待应用程序消费邮箱。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[19]=LinkDmg07_GetErrorCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保留第一个有效数据包不读,直到下一个到达。预期 SENT SEQ=3、PACKET SEQ=2、ERROR=6;读取返回最新的 33/34/35/36。ClearError 使 CLEAR ERROR 变为零,但 ErrorCount 仍为一。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_overflow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_overflow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_overflow.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_overflow.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_overflow.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_overflow.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_overflow.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:121
Return the saturating protocol/overflow error count. Timeout and request validation paths use separate counters or latches and do not increment this value.
kitaqgb/lib/link_dmg07.c:531
u8 LinkDmg07_GetErrorCount() {
return LinkDmg07_ErrorCount;
}LinkDmg07_GetLastStatus
读取最近一次通过验证的适配器原始状态字节。
语法
u8 LinkDmg07_GetLastStatus();返回值
高半字节为连接掩码,低三位包含本机玩家编号。初始为零。
使用条件与限制
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
若状态的第 3 位为一、玩家编号为零或大于四,或发现后本机编号发生变化,则拒绝该状态。拒绝时锁存错误 5,不会将该值保存为 LastStatus。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[2]=LinkDmg07_GetLastStatus();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:67
Return the last validated raw adapter status byte.
kitaqgb/lib/link_dmg07.c:408
u8 LinkDmg07_GetLastStatus() {
return LinkDmg07_LastStatus;
}LinkDmg07_GetLocalSlot
读取从适配器状态中得知的本机玩家编号。
语法
u8 LinkDmg07_GetLocalSlot();返回值
已发现时返回 1..4;发现前或刚重置发现状态后返回零。
使用条件与限制
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
slot=LinkDmg07_GetLocalSlot();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:62
Return the one-based player slot, or LINK_DMG07_NO_SLOT before discovery.
kitaqgb/lib/link_dmg07.c:398
u8 LinkDmg07_GetLocalSlot() {
return LinkDmg07_LocalSlot;
}LinkDmg07_GetPacketSequence
读取分配给最近一次交付数据包的序列号。
语法
u8 LinkDmg07_GetPacketSequence();返回值
返回八位数据包序列号,不改变就绪状态。
使用条件与限制
序列号从 255 回绕到零。数据包交付时,PacketSequence 等于 SentSequence 减一。后续发送开始会推进 SentSequence,因此稍后分别读取这两个值时,不一定仍相差一。
接收邮箱只保留最新数据包。另一个数据包完成时,会覆盖未读数据、锁存错误 6,并增加 ErrorCount。适配器不会等待应用程序消费邮箱。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[7]=LinkDmg07_GetPacketSequence();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:109
Return the modulo-256 sequence associated with the most recently published packet.
kitaqgb/lib/link_dmg07.c:510
u8 LinkDmg07_GetPacketSequence() {
return LinkDmg07_PacketSequence;
}LinkDmg07_GetPacketSlot
从数据包缓存中读取一名玩家的字节。
语法
u8 __stackcall LinkDmg07_GetPacketSlot(u8 player_slot);参数
player_slot物理玩家编号,范围为 1..4。
返回值
返回玩家 1..4 对应的缓存字节;编号无效时返回零。
使用条件与限制
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
既不检查也不消费就绪状态。刚初始化后返回零;消费后或重置发现状态后,可能仍返回保留的字节。不要用它判断是否有新数据。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[16]=LinkDmg07_GetPacketSlot(4);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:102
Read one cached player byte using a one-based slot. Invalid slots return zero; the function neither requires readiness nor consumes the packet.
kitaqgb/lib/link_dmg07.c:499
u8 __stackcall LinkDmg07_GetPacketSlot(u8 player_slot) {
if ((player_slot == 0) || (player_slot > LINK_DMG07_MAX_PLAYERS)) return 0;
return LinkDmg07_RxPacket[(__safe_index u8)(player_slot - 1)];
}LinkDmg07_GetPhase
读取当前的发现、启动请求、确认或传送阶段。
语法
u8 LinkDmg07_GetPhase();返回值
0=PING、1=REQUEST_AA、2=WAIT_CC、3=TRANSFER。
使用条件与限制
不轮询,也不等待。启动请求被接受还不足以进入 TRANSFER:必须连续收到四个 0xCC 字节。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[8]=LinkDmg07_GetPhase();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:60
Return the discovery, start-handshake or transfer phase without polling.
kitaqgb/lib/link_dmg07.c:393
u8 LinkDmg07_GetPhase() {
return LinkDmg07_Phase;
}LinkDmg07_GetRestartState
读取重启控制当前是无请求、等待执行还是正在发送。
语法
u8 LinkDmg07_GetRestartState();返回值
0=无请求、1=等待边界、2=正在发送控制流量。
使用条件与限制
与通信阶段分开记录。仅凭零不能判断连接已经建立或可用。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[4]=LinkDmg07_GetRestartState();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
玩家 2 请求重启。该主机记录 PENDING=1、SENT FF=1。四台均显示 DATA VALID=1、RESET PHASE=0、SLOT AFTER=0、RESET PRIME=0。驱动会保留正在进行的交换,直到适配器指示使其返回发现阶段。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_restart.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_restart.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_restart.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_restart.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_restart.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_restart.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:85
Return whether aligned recovery is absent, pending or sending control traffic.
kitaqgb/lib/link_dmg07.c:457
u8 LinkDmg07_GetRestartState() {
return LinkDmg07_RestartState;
}LinkDmg07_GetSentSequence
读取实际已由时钟移出的应用提交字节数。
语法
u8 LinkDmg07_GetSentSequence();返回值
返回八位发送序列号。计入初始提交,不计重启控制流量。
使用条件与限制
序列号从 255 回绕到零。数据包交付时,PacketSequence 等于 SentSequence 减一。后续发送开始会推进 SentSequence,因此稍后分别读取这两个值时,不一定仍相差一。
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[6]=LinkDmg07_GetSentSequence();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:107
SentSequence increments when this console's packet byte is clocked out. PacketSequence labels the returned four-byte packet. For a valid packet it is one less than SentSequence, making the DMG-07 one-packet delay explicit.
kitaqgb/lib/link_dmg07.c:505
u8 LinkDmg07_GetSentSequence() {
return LinkDmg07_SentSequence;
}LinkDmg07_GetSilenceFrames
读取自上次处理串行完成事件后累计的帧数。
语法
u8 LinkDmg07_GetSilenceFrames();返回值
范围为 0..255;处理完成事件会清零,每次 TickFrame 会递增。
使用条件与限制
在整帧期间反复调用 Poll,并且每帧恰好调用一次 TickFrame。每个 VBlank 只调用一次 Poll,无法在适配器各字节之间及时重新准备端口。共享头文件中的 d_poll 检测进入 LY >= 144 的时刻,以调用一次 TickFrame。应串行执行两种操作,避免 ISR 与主循环发生竞态。
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[6]=LinkDmg07_GetSilenceFrames();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不接适配器运行。预期 PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4。由于从未发现适配器,DISCONNECTS 保持零。空读取保留 EMPTY BUFFER=77;ClearError 使 CLEARED=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png实现与源码
kitaqgb/lib/link_dmg07.h:114
SilenceFrames is reset by every completed serial byte. TimeoutCount counts distinct serial-silence or AA/CC/restart handshake timeouts. DisconnectCount counts adapter-silence outages and ping-status membership drops. All saturate.
kitaqgb/lib/link_dmg07.c:515
u8 LinkDmg07_GetSilenceFrames() {
return LinkDmg07_SilenceFrames;
}LinkDmg07_GetTimeoutCount
读取静默或握手超时的累计次数。
语法
u8 LinkDmg07_GetTimeoutCount();返回值
返回 0..255 范围内的累计值。
使用条件与限制
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
接收恢复前,不会每帧重复计数同一次静默。ClearError 会保留此抑制锁存标志及累计计数。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[7]=LinkDmg07_GetTimeoutCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不接适配器运行。预期 PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4。由于从未发现适配器,DISCONNECTS 保持零。空读取保留 EMPTY BUFFER=77;ClearError 使 CLEARED=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png实现与源码
kitaqgb/lib/link_dmg07.h:116
Return the saturating lifetime timeout count, preserved across protocol restarts.
kitaqgb/lib/link_dmg07.c:520
u8 LinkDmg07_GetTimeoutCount() {
return LinkDmg07_TimeoutCount;
}LinkDmg07_HasPacket
检查是否有尚未读取的完整四字节数据包。
语法
u8 LinkDmg07_HasPacket();返回值
有未读数据返回 1,否则返回 0。
使用条件与限制
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
接收邮箱只保留最新数据包。另一个数据包完成时,会覆盖未读数据、锁存错误 6,并增加 ErrorCount。适配器不会等待应用程序消费邮箱。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[15]=LinkDmg07_HasPacket();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:96
Inspect the one-packet receive mailbox without consuming it.
kitaqgb/lib/link_dmg07.c:473
u8 LinkDmg07_HasPacket() {
return LinkDmg07_PacketReady;
}LinkDmg07_HasPingStatus
检查是否有已完成的四字节发现交换等待确认。
语法
u8 LinkDmg07_HasPingStatus();返回值
有未消费的通知时返回 1,否则返回 0。
使用条件与限制
只读取一个标志。即使状态字节被拒绝,发现交换仍可能继续;不要仅凭此标志判断数据完全有效或启动完成。还应检查槽位、掩码及 LastError。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[4]=LinkDmg07_HasPingStatus();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:69
Read the completed-discovery status flag without clearing it.
kitaqgb/lib/link_dmg07.c:413
u8 LinkDmg07_HasPingStatus() {
return LinkDmg07_PingStatusReady;
}LinkDmg07_Init
重置发现状态、接收存储和诊断信息,然后准备接收适配器时钟。
语法
void __stackcall LinkDmg07_Init(u8 rate);参数
rate返回给适配器的八位 RATE 响应;零表示使用默认值 0x10。
返回值
无返回值。
使用条件与限制
本驱动独占串行端口。不要同时运行普通 Link 或 Link4;link_hwregs_gb.c 和 link_dmg07.c 各包含一次。外部时钟由适配器提供;驱动不会选择 CGB 高速串行模式。
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
将 SC 设为 0x80、SB 设为 0x88;只清除 IE 和 IF 中的串行位。不改变其他中断使能位或 CPU IME 状态。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
LinkDmg07_Init(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:50
Initializes polling-only external-clock operation. A zero RATE is replaced with LINK_DMG07_DEFAULT_RATE. This driver disables only the serial IRQ bit.
kitaqgb/lib/link_dmg07.c:282
void __stackcall LinkDmg07_Init(u8 rate) {
u8 i;
LinkDmg07_Rate = rate;
if (LinkDmg07_Rate == 0) LinkDmg07_Rate = LINK_DMG07_DEFAULT_RATE;
LinkDmg07_LocalByte = 0;
LinkDmg07_SentSequence = 0;
LinkDmg07_PacketSequence = 0;
LinkDmg07_RestartState = LINK_DMG07_RESTART_NONE;
LinkDmg07_SilenceFrames = 0;
LinkDmg07_TimeoutCount = 0;
LinkDmg07_DisconnectCount = 0;
LinkDmg07_ErrorCount = 0;
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NONE;
LinkDmg07_SeenAdapter = 0;
LinkDmg07_TimeoutLatched = 0;
for (i = 0; i < LINK_DMG07_PACKET_BYTES; ++i) {
LinkDmg07_RxBuild[(__safe_index u8)i] = 0;
LinkDmg07_RxPacket[(__safe_index u8)i] = 0;
}
LinkDmg07_ResetToPingInternal();
LinkHw_WriteIE((u8)(LinkHw_ReadIE() & LINK_DMG07_IRQ_SERIAL_CLEAR));
LinkHw_WriteIF((u8)(LinkHw_ReadIF() & LINK_DMG07_IRQ_SERIAL_CLEAR));
LinkDmg07_ArmInternal(LINK_DMG07_ACK);
}LinkDmg07_IsPipelinePrimed
检查是否已经丢弃内容未定义的初始广播。
语法
u8 LinkDmg07_IsPipelinePrimed();返回值
初始广播之后返回 1;初始化、重置发现状态和启动确认都会将其清零。
使用条件与限制
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
流水线已准备就绪,不代表已经有完整数据包。请用 HasPacket 判断是否可读。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[9]=LinkDmg07_IsPipelinePrimed();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:94
The first incoming adapter data packet is discarded. Thereafter ReadPacket copies four bytes in physical slot order (players 1..4).
kitaqgb/lib/link_dmg07.c:468
u8 LinkDmg07_IsPipelinePrimed() {
return LinkDmg07_PipelinePrimed;
}LinkDmg07_LastError
读取最近一次锁存的错误码。
语法
u8 LinkDmg07_LastError();返回值
返回错误码 0..6。
使用条件与限制
错误码:0=无错误、1=阶段错误、2=不是玩家 1、3=尚未就绪、4=超时、5=协议错误、6=未读数据包被覆盖。请求成功返回零;接收/弹出操作成功返回一。请求成功不会清除先前错误。
读取不会清除错误。只保存最新错误码,不保存全部错误历史。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[9]=LinkDmg07_LastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不接适配器运行。预期 PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4。由于从未发现适配器,DISCONNECTS 保持零。空读取保留 EMPTY BUFFER=77;ClearError 使 CLEARED=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png实现与源码
kitaqgb/lib/link_dmg07.h:123
Return the latest error latch without clearing counters or restarting the protocol.
kitaqgb/lib/link_dmg07.c:536
u8 LinkDmg07_LastError() {
return LinkDmg07_LastErrorCode;
}LinkDmg07_Poll
在当前阶段处理一个已完成的串行字节,并立即准备下一个响应。
语法
void LinkDmg07_Poll();返回值
无返回值。
使用条件与限制
本驱动独占串行端口。不要同时运行普通 Link 或 Link4;link_hwregs_gb.c 和 link_dmg07.c 各包含一次。外部时钟由适配器提供;驱动不会选择 CGB 高速串行模式。
在整帧期间反复调用 Poll,并且每帧恰好调用一次 TickFrame。每个 VBlank 只调用一次 Poll,无法在适配器各字节之间及时重新准备端口。共享头文件中的 d_poll 检测进入 LY >= 144 的时刻,以调用一次 TickFrame。应串行执行两种操作,避免 ISR 与主循环发生竞态。
SC 的启动位仍置位时不执行操作。处理完成事件会重置 SilenceFrames 和超时锁存标志。轮询前必须初始化。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
void d_poll(){LinkDmg07_Poll();if(D_LY>=144){if(d_vblank==0){d_vblank=1;LinkDmg07_TickFrame();}}else d_vblank=0;}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:54
Services at most one completed serial byte and immediately re-arms SC=$80. Call continuously from the main loop, including between VBlanks.
kitaqgb/lib/link_dmg07.c:311
void LinkDmg07_Poll() {
u8 value;
u8 next;
if ((LinkHw_ReadSC() & LINK_DMG07_SC_EXTERNAL_START) != 0) return;
value = LinkHw_ReadSB();
LinkDmg07_SilenceFrames = 0;
LinkDmg07_TimeoutLatched = 0;
LinkDmg07_SeenAdapter = 1;
next = 0;
if (LinkDmg07_Phase == LINK_DMG07_PHASE_PING) {
next = LinkDmg07_ProcessPingInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_REQUEST_AA) {
next = LinkDmg07_ProcessRequestInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_WAIT_CC) {
next = LinkDmg07_ProcessWaitCcInternal(value);
} else if (LinkDmg07_Phase == LINK_DMG07_PHASE_TRANSFER) {
next = LinkDmg07_ProcessTransferInternal(value);
} else {
LinkDmg07_SetRuntimeErrorInternal(LINK_DMG07_ERR_PROTOCOL);
LinkDmg07_ResetToPingInternal();
next = LINK_DMG07_ACK;
}
LinkDmg07_ArmInternal(next);
}LinkDmg07_ReadPacket
复制最新完成的数据包,并清除其未读标志。
语法
u8 __stackcall LinkDmg07_ReadPacket(u8 *dst);参数
dst四个可写字节;为 null 时丢弃数据包。
返回值
成功消费返回 1,为空时返回 0。空读取不会改变 dst。
使用条件与限制
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
接收邮箱只保留最新数据包。另一个数据包完成时,会覆盖未读数据、锁存错误 6,并增加 ErrorCount。适配器不会等待应用程序消费邮箱。
消费后,缓存字节仍可通过 GetPacketSlot 读取。空的 ReadPacket 不会再次复制这些缓存字节。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[10]=LinkDmg07_ReadPacket(packet);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:99
Consume the latest four-byte packet. A nonnull destination must hold four bytes; null discards the packet. Packet storage remains after the flag clears.
kitaqgb/lib/link_dmg07.c:479
u8 __stackcall LinkDmg07_ReadPacket(u8 *dst) {
if (LinkDmg07_PacketReady == 0) return 0;
if (dst != 0) {
// Keep this as a sequential pointer copy. Besides being smaller for a
// fixed four-byte packet, it avoids making a serial hot-path copy
// depend on general variable-index pointer lowering.
*dst = LinkDmg07_RxPacket[0];
dst++;
*dst = LinkDmg07_RxPacket[1];
dst++;
*dst = LinkDmg07_RxPacket[2];
dst++;
*dst = LinkDmg07_RxPacket[3];
}
LinkDmg07_PacketReady = 0;
return 1;
}LinkDmg07_RequestRestart
安排重启控制流量,从传送数据包边界开始。
语法
u8 LinkDmg07_RequestRestart();返回值
在 TRANSFER 阶段返回 0,否则返回 1。请求待执行或正在发送时重复请求,也返回 0。
使用条件与限制
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
错误码:0=无错误、1=阶段错误、2=不是玩家 1、3=尚未就绪、4=超时、5=协议错误、6=未读数据包被覆盖。请求成功返回零;接收/弹出操作成功返回一。请求成功不会清除先前错误。
任何玩家都可请求。保留当前字节位置,发送对齐的 0xFF 控制流量,并在收到适配器全 0xFF 指示后返回发现阶段。仅请求重启不会擦除本机槽位或接收流水线。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[3]=LinkDmg07_RequestRestart();result[4]=LinkDmg07_GetRestartState();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
玩家 2 请求重启。该主机记录 PENDING=1、SENT FF=1。四台均显示 DATA VALID=1、RESET PHASE=0、SLOT AFTER=0、RESET PRIME=0。驱动会保留正在进行的交换,直到适配器指示使其返回发现阶段。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_restart.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_restart.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_restart.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_restart.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_restart.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_restart.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_restart.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:83
Any physical slot may schedule FF FF FF FF at the next four-byte packet boundary while remaining in TRANSFER phase. The driver keeps sending aligned FF bytes until it sees the adapter's complete all-FF indicator packet, then returns to PING. A transfer-phase silence timeout schedules the same recovery automatically and preserves the current byte position for clocks that resume.
kitaqgb/lib/link_dmg07.c:445
u8 LinkDmg07_RequestRestart() {
if (LinkDmg07_Phase != LINK_DMG07_PHASE_TRANSFER) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_STATE;
return LINK_DMG07_ERR_STATE;
}
if (LinkDmg07_RestartState == LINK_DMG07_RESTART_NONE) {
LinkDmg07_RestartState = LINK_DMG07_RESTART_PENDING;
}
return LINK_DMG07_ERR_NONE;
}LinkDmg07_RequestTransmission
由物理玩家 1 安排启动请求,在下一个发现数据包边界开始。
语法
u8 LinkDmg07_RequestTransmission();返回值
成功安排返回 0;阶段错误返回 1;不是玩家 1 返回 2;本机连接位未设置返回 3。
使用条件与限制
库的玩家编号为 1..4;零表示尚未发现。连接掩码的位 0..3 对应玩家 1..4。KOKURA --link-session 的 slot 值为 0..3,与 ROM API 使用不同的编号规则。
错误码:0=无错误、1=阶段错误、2=不是玩家 1、3=尚未就绪、4=超时、5=协议错误、6=未读数据包被覆盖。请求成功返回零;接收/弹出操作成功返回一。请求成功不会清除先前错误。
只能在 PING 阶段安排请求。不会自行等待四名玩家全部连接;示例在应用代码中检查掩码 15。安排是异步的:先发送对齐的 0xAA,再进行 0xCC 确认。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
result[5]=LinkDmg07_RequestTransmission();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:76
Only physical player 1 may request AA AA AA AA. The bytes are aligned to the next FE/status/status/status ping packet. All consoles enter transfer mode only after receiving four consecutive CC bytes.
kitaqgb/lib/link_dmg07.c:426
u8 LinkDmg07_RequestTransmission() {
if (LinkDmg07_Phase != LINK_DMG07_PHASE_PING) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_STATE;
return LINK_DMG07_ERR_STATE;
}
if (LinkDmg07_LocalSlot != 1) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NOT_PLAYER1;
return LINK_DMG07_ERR_NOT_PLAYER1;
}
if ((LinkDmg07_ConnectedMask & (u8)0x01) == 0) {
LinkDmg07_LastErrorCode = LINK_DMG07_ERR_NOT_READY;
return LINK_DMG07_ERR_NOT_READY;
}
LinkDmg07_StartRequested = 1;
return LINK_DMG07_ERR_NONE;
}LinkDmg07_SetLocalByte
设置将来发送的数据包要提交的一个应用字节。
语法
void __stackcall LinkDmg07_SetLocalByte(u8 value);参数
value要提交的八位应用值。
返回值
无返回值。
使用条件与限制
SIZE=1 时,每台主机提交一个字节;四字节广播按玩家 1..4 排列。初始广播会被丢弃。后续广播包含前一个数据包的提交值。四字节全为 0xFF 的广播保留作重新启动指示。
准备数据包的第零字节时采样此值。不会修改已准备好的 SB 字节。请求启动前应先设置首次提交值。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
LinkDmg07_SetLocalByte(32+slot);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在四台主机上运行同一个 ROM,每台发送 32 加自身玩家编号。每台主机都收到 PLAYER 1..4 = 33/34/35/36。首个有效广播记录 SENT SEQ=2、PACKET SEQ=1,展示一个数据包的延迟。画面固定显示这一观测结果;实际游戏应继续轮询。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_exchange.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_exchange.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe --link-topology dmg07 --run-frames 90 --link-session "name=player1|slot=0|rom=.\out\dmg07_exchange.gb|save_state=.\out\player1.kqs" --link-session "name=player2|slot=1|rom=.\out\dmg07_exchange.gb|save_state=.\out\player2.kqs" --link-session "name=player3|slot=2|rom=.\out\dmg07_exchange.gb|save_state=.\out\player3.kqs" --link-session "name=player4|slot=3|rom=.\out\dmg07_exchange.gb|save_state=.\out\player4.kqs"
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player1.kqs --run-frames 2 --png .\out\player1.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player2.kqs --run-frames 2 --png .\out\player2.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player3.kqs --run-frames 2 --png .\out\player3.png
.\kokura\kokura-cli.exe .\out\dmg07_exchange.gb --hardware cgb --load-state .\out\player4.kqs --run-frames 2 --png .\out\player4.png实现与源码
kitaqgb/lib/link_dmg07.h:90
Sets the byte sampled when byte 0 of the next adapter packet is armed. Set it before the current packet completes (and before requesting start for the first submission). During normal transfer, bytes 1..3 are sent as zero.
kitaqgb/lib/link_dmg07.c:463
void __stackcall LinkDmg07_SetLocalByte(u8 value) {
LinkDmg07_LocalByte = value;
}LinkDmg07_TickFrame
将静默及启动/重启看门狗推进一帧,到达期限时请求恢复。
语法
void LinkDmg07_TickFrame();返回值
无返回值。
使用条件与限制
在整帧期间反复调用 Poll,并且每帧恰好调用一次 TickFrame。每个 VBlank 只调用一次 Poll,无法在适配器各字节之间及时重新准备端口。共享头文件中的 d_poll 检测进入 LY >= 144 的时刻,以调用一次 TickFrame。应串行执行两种操作,避免 ISR 与主循环发生竞态。
SilenceFrames 和诊断计数器在 255 饱和。Init 会全部重置;普通协议重启保留诊断计数器。ClearError 只清除错误码。
默认期限为 12 次调用;可在包含文件前将 LINK_DMG07_TIMEOUT_FRAMES 定义为 1..255,以选择其他期限。一次静默只计数一次,直到另一个字节清除锁存标志。传送恢复会保留字节对齐;发现/启动阶段的恢复则返回发现阶段。
画面由 KOKURA 的 DMG-07 模型截取。各台主机独立执行,并比较发送与接收值。尚未验证实体适配器/线缆的电气行为及时序。CGB 画面以蓝色显示数值。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。四主机示例在四个会话中启动同一个 ROM。检查 DMG 时,将所有 --cgb=cgb 改为 --cgb=dmg,将 --hardware cgb 改为 --hardware dmg。
使用示例
void d_poll(){LinkDmg07_Poll();if(D_LY>=144){if(d_vblank==0){d_vblank=1;LinkDmg07_TickFrame();}}else d_vblank=0;}这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
不接适配器运行。预期 PLAYER=0、START ERROR=2、SILENT FRAME=12、TIMEOUTS=1、ERROR=4。由于从未发现适配器,DISCONNECTS 保持零。空读取保留 EMPTY BUFFER=77;ClearError 使 CLEARED=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dmg07_absent.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dmg07_absent.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\dmg07_absent.gb --hardware cgb --run-frames 90 --png .\out\absent.png实现与源码
kitaqgb/lib/link_dmg07.h:57
Advances saturating frame-based silence and handshake timeout counters.
kitaqgb/lib/link_dmg07.c:369
void LinkDmg07_TickFrame() {
LinkDmg07_SilenceFrames = LinkDmg07_IncrementSaturatingInternal(LinkDmg07_SilenceFrames);
if ((LinkDmg07_Phase == LINK_DMG07_PHASE_REQUEST_AA) ||
(LinkDmg07_Phase == LINK_DMG07_PHASE_WAIT_CC) ||
(LinkDmg07_RestartState == LINK_DMG07_RESTART_SENDING)) {
LinkDmg07_HandshakeFrames = LinkDmg07_IncrementSaturatingInternal(LinkDmg07_HandshakeFrames);
} else {
LinkDmg07_HandshakeFrames = 0;
}
if ((LinkDmg07_SilenceFrames >= LINK_DMG07_TIMEOUT_FRAMES) &&
(LinkDmg07_TimeoutLatched == 0)) {
LinkDmg07_TimeoutInternal(1);
return;
}
if ((LinkDmg07_HandshakeFrames >= LINK_DMG07_TIMEOUT_FRAMES) &&
(LinkDmg07_TimeoutLatched == 0)) {
LinkDmg07_TimeoutInternal(0);
}
}physics2d.h — 2D 运动与碰撞
提供矩形积分、轴对齐包围盒接触及可选表面响应求解。刚体使用中心和半尺寸,active=0 排除该槽位,inv_mass_q8<=0 表示静态。KQRect 则使用左上角和宽高。矩形对接触不使用刚体恢复系数字段,表面响应使用 KQSurface2D 系数。
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
kq2d_body_apply_friction
用带符号字节系数除以 256 缩放两个速度分量,不检查位置或活动状态。
语法
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
friction_q8本函数使用的速度倍率,会转换为 s8,因此 128~255 会变成负系数。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
0~127 表示倍率 0~127/256;128~255 转为 s8 后为负,会反转速度。若需要 0~255 范围的正阻尼,应对每个速度分量使用 kq2d_scale_q8。乘积向零截断,空指针被忽略。
使用示例
kq2d_body_apply_friction(&temp,64);
result[11]=temp.vx; result[12]=temp.vy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:66
Scale both velocities through the signed 8-bit coefficient intrinsic. Despite the unsigned parameter, values 128..255 become negative coefficients; this is not a full-range unsigned Q8 damping helper. Null is ignored.
kitaqgb/lib/physics2d.c:654
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8)
{
if (body == 0) return;
body->vx = (s16)__smul16x8(body->vx, (s8)friction_q8);
body->vy = (s16)__smul16x8(body->vy, (s8)friction_q8);
}kq2d_body_apply_gravity
向活动动态刚体施加重力,并将各轴速度限制在 ±max_speed,不更新位置。
语法
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
gravity_x本次调用加到 X 速度上的加速度。
gravity_y本次调用加到 Y 速度上的加速度。
max_speed速度上限;本条说明会指出限制的是各轴还是合成向量。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
矩形积分与碰撞函数共享临时存储,不要从中断重入这些物理函数。
忽略空指针、非活动刚体及逆质量非正的刚体。速度上限应为非负数。
使用示例
kq2d_body_apply_gravity(&temp,5,-10,32);
result[9]=temp.vx; result[10]=temp.vy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:62
Add gravity and clamp each velocity axis separately for an active dynamic body. Null, inactive and static bodies are ignored. Supply a nonnegative limit and avoid overflow in the additions before clamping; position is unchanged.
kitaqgb/lib/physics2d.c:630
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed)
{
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
kq2d_asm_value = body->vx;
kq2d_asm_operand = gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = max_speed;
kq2d_clamp_abs_s16_asm();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = max_speed;
kq2d_clamp_abs_s16_asm();
body->vy = kq2d_asm_value;
}kq2d_body_init
按指定半尺寸初始化中心式矩形,速度为零、逆质量为 256,并设为活动状态。
语法
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
x中心或点的 X 坐标,示例中以像素为单位。
y中心或点的 Y 坐标,示例中向下为正。
half_xX 方向半尺寸,完整宽度为其两倍。
half_yY 方向半尺寸,完整高度为其两倍。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
将 restitution_q8 设为零,忽略空刚体指针。请提供正的半尺寸。
使用示例
kq2d_body_init(&temp,10,20,8,8);
result[3]=temp.inv_mass_q8; result[4]=temp.active;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:54
Initialize an active unit-inverse-mass body at the supplied center and half extents, with zero velocity and reserved restitution set to zero. Null is ignored.
kitaqgb/lib/physics2d.c:597
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y)
{
if (body == 0) return;
body->x = x;
body->y = y;
body->vx = 0;
body->vy = 0;
body->half_x = half_x;
body->half_y = half_y;
body->inv_mass_q8 = 256;
body->restitution_q8 = 0;
body->active = 1;
}kq2d_body_limit_speed
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
保守估计向量长度,并同比例缩放两轴以限制合成速度。
语法
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
max_speed速度上限;本条说明会指出限制的是各轴还是合成向量。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
速度分量绝对值和正 max_speed 须不超过 16383。非正上限会将速度清零。忽略空指针,但活动状态和质量不阻止修改。整数取整可能使结果低于上限,极小倍率可能产生零。
使用示例
kq2d_body_limit_speed(&body,250);
result[2]=body.vx; result[3]=body.vy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色刚体位置不变,绿色速度线从左图的 (8,16) 变为右图的 (2,−8),展示蓝色表面上的反弹和切向摩擦。面板原点为屏幕 (8,48)/(88,48),刚体局部中心为 (24,40)。入射速度为 16,跨越时间示例返回 192/256,限速示例将 (300,400) 缩为 (147,196)。DMG 全部使用黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:92
Conservative circular bound, with a shared Q8 scale for both components. Quantizes toward zero; very small limits relative to input may yield zero. Components and positive max_speed must be <= 16383 in magnitude.
kitaqgb/lib/physics2d.c:260
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed)
{
u16 x;
u16 y;
u16 swap;
u16 ratio;
u16 length;
if (body == 0) return;
if (max_speed <= 0) { body->vx = 0; body->vy = 0; return; }
x = (u16)body->vx;
y = (u16)body->vy;
if (body->vx < 0) x = (u16)(0 - body->vx);
if (body->vy < 0) y = (u16)(0 - body->vy);
if (x + y <= (u16)max_speed) return;
if (x < y) { swap = x; x = y; y = swap; }
// major + minor/2 bounds hypot from above for 0 <= minor <= major.
if (x + (y >> 1) + 1 <= (u16)max_speed) return;
// Round the minor/major ratio upward for the 65-entry upper-bound table.
// The extra unit covers truncation when converting the estimate back to speed.
ratio = (kq2d__fraction_q8(y, x) + 4) >> 2;
if (ratio > 64) ratio = 64;
length = x + (u16)kq2d_scale_q8((s16)x, (s16)kq2d_hypot_extra[(__safe_index u8)ratio]) + 1;
if (length <= (u16)max_speed) return;
ratio = kq2d__fraction_q8((u16)max_speed, length);
body->vx = kq2d_scale_q8(body->vx, (s16)ratio);
body->vy = kq2d_scale_q8(body->vy, (s16)ratio);
}kq2d_body_resolve_surface
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
使用相对移动表面的速度,应用法向反弹、最小反弹速度和切向摩擦。
语法
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
surfaceKQSurface2D 指针,其中包含法线、表面速度、反弹及摩擦设置。
返回值
返回正的入射法向速度;输入为空或正在分离时返回 0。
使用条件与限制
使用长度为 256 的 Q8 单位法线。相对速度分量须在 ±8191 内,阈值和 kick 非负,系数为 0~255。只有严格超过阈值才应用恢复系数,kick 为最小反弹速度。空输入或正在分离时速度不变。接触检测及穿透修正需另行处理,不检查活动状态或质量。
使用示例
result[6]=kq2d_body_resolve_surface(&body,&surface);
result[7]=body.vx; result[8]=body.vy; result[9]=body.y;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色刚体位置不变,绿色速度线从左图的 (8,16) 变为右图的 (2,−8),展示蓝色表面上的反弹和切向摩擦。面板原点为屏幕 (8,48)/(88,48),刚体局部中心为 (24,40)。入射速度为 16,跨越时间示例返回 192/256,限速示例将 (300,400) 缩为 (147,196)。DMG 全部使用黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:95
Kinematic surface response; returns incoming normal speed, or zero if separating. Caller resolves penetration separately. Relative components must fit +/-8191.
kitaqgb/lib/physics2d.c:314
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface)
{
s16 vx;
s16 vy;
s16 speed;
s16 tangent;
s16 bounce = 0;
s16 impulse;
s16 friction;
if (body == 0 || surface == 0) return 0;
vx = (s16)(body->vx - surface->vx);
vy = (s16)(body->vy - surface->vy);
speed = (s16)(kq2d_scale_q8(vx,surface->nx_q8) + kq2d_scale_q8(vy,surface->ny_q8));
if (speed >= 0) return 0;
speed = (s16)(0 - speed);
if (speed > surface->bounce_threshold) bounce = kq2d_scale_q8(speed,(s16)surface->restitution_q8);
if (bounce < surface->kick) bounce = surface->kick;
impulse = (s16)(speed + bounce);
// A resting contact is a tangent constraint, not a tiny rounded bounce.
if (bounce == 0) {
tangent = (s16)(kq2d__rest_component(vy,surface->nx_q8) - kq2d__rest_component(vx,surface->ny_q8));
friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
if (tangent > friction) tangent = (s16)(tangent - friction);
else if (tangent < (s16)(0 - friction)) tangent = (s16)(tangent + friction);
else tangent = 0;
body->vx = (s16)(surface->vx - kq2d__rest_component(tangent,surface->ny_q8));
body->vy = (s16)(surface->vy + kq2d__rest_component(tangent,surface->nx_q8));
return speed;
}
body->vx = (s16)(body->vx + kq2d_scale_q8(impulse,surface->nx_q8));
body->vy = (s16)(body->vy + kq2d_scale_q8(impulse,surface->ny_q8));
if (surface->friction_q8 != 0) {
tangent = (s16)(kq2d_scale_q8(vy,surface->nx_q8) - kq2d_scale_q8(vx,surface->ny_q8));
friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
if (tangent > friction) tangent = friction;
if (tangent < (s16)(0 - friction)) tangent = (s16)(0 - friction);
body->vx = (s16)(body->vx + kq2d_scale_q8(tangent,surface->ny_q8));
body->vy = (s16)(body->vy - kq2d_scale_q8(tangent,surface->nx_q8));
}
return speed;
}kq2d_body_set_pos
仅替换刚体中心,保留速度、尺寸和活动状态。
语法
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
x中心或点的 X 坐标,示例中以像素为单位。
y中心或点的 Y 坐标,示例中向下为正。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
忽略空指针;指针有效时,非活动或静态刚体也会被修改。
使用示例
kq2d_body_set_pos(&temp,-12,20);
result[5]=temp.x; result[6]=temp.y;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:56
Replace the center without altering velocity, extents or activity. Null is ignored.
kitaqgb/lib/physics2d.c:612
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y)
{
if (body == 0) return;
body->x = x;
body->y = y;
}kq2d_body_set_velocity
直接设置速度,不限速也不更新位置。
语法
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
vx每次模拟更新的 X 位移,单位与位置相同。
vy每次模拟更新的 Y 位移,单位与位置相同。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
忽略空指针;指针有效时,非活动或静态刚体也会被修改。
使用示例
kq2d_body_set_velocity(&temp,40,-30);
result[7]=temp.vx; result[8]=temp.vy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:58
Replace velocity without clamping or checking activity/mass. Null is ignored.
kitaqgb/lib/physics2d.c:620
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy)
{
if (body == 0) return;
body->vx = vx;
body->vy = vy;
}kq2d_integrate_body
对一个动态刚体应用 world 重力和各轴限速,再将速度加到位置。
语法
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body);参数
world包含刚体数组及模拟设置的 world 指针。
body要修改的刚体指针,本操作未使用的字段保持不变。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
矩形积分与碰撞函数共享临时存储,不要从中断重入这些物理函数。
忽略空指针、非活动刚体及逆质量非正的刚体。速度上限应为非负数。
本调用不处理与其他刚体的接触,空 world 也会被忽略。
使用示例
kq2d_integrate_body(&world,&temp);
result[18]=temp.x; result[19]=temp.y;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:47
For one active dynamic body, add gravity, clamp each velocity axis and then add velocity to position. Null inputs are ignored; no contacts are solved. Use a nonnegative speed limit and keep additions in signed 16-bit range.
kitaqgb/lib/physics2d.c:664
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body)
{
if (world == 0) return;
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
kq2d_asm_value = body->vx;
kq2d_asm_operand = world->gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
body->vx = kq2d_asm_value;
kq2d_asm_value = body->vy;
kq2d_asm_operand = world->gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
body->vy = kq2d_asm_value;
kq2d_asm_value = body->x;
kq2d_asm_operand = body->vx;
kq2d_add_s16_asm();
body->x = kq2d_asm_value;
kq2d_asm_value = body->y;
kq2d_asm_operand = body->vy;
kq2d_add_s16_asm();
body->y = kq2d_asm_value;
}kq2d_overlap_aabb
检查两个中心式矩形在 X、Y 两轴是否严格重叠。
语法
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b);参数
a有效刚体指针,查询忽略活动状态、质量和标志。
b有效刚体指针,查询忽略活动状态、质量和标志。
返回值
内部重叠返回 1,刚好接触或分离返回 0。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
使用示例
result[20]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);
bodies[1].x=40; result[21]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:51
Test strict overlap of two centered boxes; touching edges return false. Both pointers must be valid. Active/mass flags are ignored, and coordinate differences and extent sums must fit the signed 16-bit calculations.
kitaqgb/lib/physics2d.c:579
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b)
{
s16 dx = (s16)(b->x - a->x);
s16 dy = (s16)(b->y - a->y);
s16 adx = kq2d__abs_s16(dx);
s16 ady = kq2d__abs_s16(dy);
s16 sx = (s16)(a->half_x + b->half_x);
s16 sy = (s16)(a->half_y + b->half_y);
if (adx >= sx) return 0;
if (ady >= sy) return 0;
return 1;
}kq2d_point_in_rect
检查点是否位于矩形内,包含左/上边界,不包含右/下边界。
语法
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r);参数
x中心或点的 X 坐标,示例中以像素为单位。
y中心或点的 Y 坐标,示例中向下为正。
rKQRect 值,包含左上角 x/y 及宽高 w/h。
返回值
位于内部返回 1,否则返回 0。
使用条件与限制
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
使用示例
result[16]=kq2d_point_in_rect(0,0,r1);
result[17]=kq2d_point_in_rect(16,0,r1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:72
Delegate the half-open point test: include left/top and exclude right/bottom. Coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/physics2d.c:705
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r)
{
return kq_point_in_rect(x, y, r);
}kq2d_rect_intersect
检查由左上角和宽高表示的两个矩形内部是否重叠。
语法
u8 kq2d_rect_intersect(KQRect a, KQRect b);参数
a以左上角坐标及正宽高表示的 KQRect 值。
b以左上角坐标及正宽高表示的 KQRect 值。
返回值
内部重叠返回 1,刚好接触或分离返回 0。
使用条件与限制
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
不会显式拒绝非正尺寸。不要把刚体中心和半尺寸直接复制到 KQRect。
使用示例
result[14]=kq2d_rect_intersect(r1,r2);
r2.x=16; result[15]=kq2d_rect_intersect(r1,r2);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:69
Delegate strict rectangle overlap to the fixed-point utility; edge-only contact is false. Coordinate-plus-size sums must remain representable as s16.
kitaqgb/lib/physics2d.c:698
u8 kq2d_rect_intersect(KQRect a, KQRect b)
{
return kq_rect_intersect(a, b);
}kq2d_scale_q8
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
乘以带符号系数并除以 256,正负结果均向零截断。
语法
s16 kq2d_scale_q8(s16 value, s16 coefficient);参数
value待缩放的有符号 16 位数值。
coefficient范围 −256~256 的带符号系数,256 表示 1.0。
返回值
返回向零截断的有符号 16 位结果。
使用条件与限制
系数只应位于 −256~256,其中 ±256 表示 ±1。value=−32768 且系数为 −256 时,数学结果 32768 超出 s16。
使用示例
result[0]=kq2d_scale_q8(-257,128);
result[1]=kq2d_scale_q8(300,-256);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色刚体位置不变,绿色速度线从左图的 (8,16) 变为右图的 (2,−8),展示蓝色表面上的反弹和切向摩擦。面板原点为屏幕 (8,48)/(88,48),刚体局部中心为 (24,40)。入射速度为 16,跨越时间示例返回 192/256,限速示例将 (300,400) 缩为 (147,196)。DMG 全部使用黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:88
Truncates toward zero, including negative values; coefficient is -256..256.
kitaqgb/lib/physics2d.c:8
s16 kq2d_scale_q8(s16 value, s16 coefficient)
{
// Eight-bit shift/add product in HL:C, with sign applied after truncation.
__asm {
LD_HL_IMM value
LD_A_HL
LD_E_A
INC_HL
LD_A_HL
LD_D_A
OR_E
JP_Z kqscale_zero
LD_HL_IMM coefficient
LD_A_HL
LD_C_A
INC_HL
LD_A_HL
LD_B_A
LD_A_C
OR_A
JR_NZ kqscale_fraction
LD_A_B
OR_A
JP_Z kqscale_zero
LD_H_D
LD_L_E
AND_IMM 128
RET_Z
JP kqscale_negate
kqscale_fraction:
LD_A_D
XOR_B
AND_IMM 128
PUSH_AF
LD_A_D
AND_IMM 128
JR_Z kqscale_value_positive
XOR_A
SUB_E
LD_E_A
LD_A_IMM 0
SBC_D
LD_D_A
kqscale_value_positive:
LD_A_B
AND_IMM 128
JR_Z kqscale_coefficient_positive
XOR_A
SUB_C
LD_C_A
kqscale_coefficient_positive:
LD_HL_IMM 0
// Unroll eight bits: avoid a loop branch on every fixed-point product.
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_0
ADD_HL_DE
kqscale_shift_0:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_1
ADD_HL_DE
kqscale_shift_1:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_2
ADD_HL_DE
kqscale_shift_2:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_3
ADD_HL_DE
kqscale_shift_3:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_4
ADD_HL_DE
kqscale_shift_4:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_5
ADD_HL_DE
kqscale_shift_5:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_6
ADD_HL_DE
kqscale_shift_6:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
LD_A_C
AND_IMM 1
JR_Z kqscale_shift_7
ADD_HL_DE
kqscale_shift_7:
LD_A_H
RRA
LD_H_A
LD_A_L
RRA
LD_L_A
LD_A_C
RRA
LD_C_A
POP_AF
OR_A
RET_Z
kqscale_negate:
XOR_A
SUB_L
LD_L_A
LD_A_IMM 0
SBC_H
LD_H_A
RET
kqscale_zero:
LD_HL_IMM 0
RET
}
}kq2d_step
积分活动动态刚体,再处理数组中活动元素之间的接触。
语法
void kq2d_step(KQWorld2D* world);参数
world包含刚体数组及模拟设置的 world 指针。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
沿穿透较浅的轴解决接触,深度相同时选择 Y。位置修正使用逆质量,但两个动态刚体的该轴速度直接取简单平均;与静态刚体碰撞则将该分量清零。此求解器不使用 restitution_q8,属于离散碰撞处理,可能漏掉高速穿越。
矩形积分与碰撞函数共享临时存储,不要从中断重入这些物理函数。
忽略空 world 或空数组。solver_iterations 为零时仍执行一次接触处理。
使用示例
kq2d_step(&world);
result[22]=bodies[0].x; result[23]=bodies[0].y;
result[24]=bodies[0].vx; result[25]=bodies[0].vy;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:43
Advance active dynamic bodies, then visit each active unordered pair for the configured contact passes (zero means one). Static bodies still participate as obstacles. The world borrows its array and shares non-reentrant ASM scratch. This is discrete integration; fast bodies can pass through thin obstacles.
kitaqgb/lib/physics2d.c:794
void kq2d_step(KQWorld2D* world)
{
if (world == 0) return;
if (world->bodies == 0) return;
KQBody2D* bodies = world->bodies;
u8 count = world->body_count;
// Integrate dynamic bodies.
u8 i;
for (i = 0; i < count; i++)
{
KQBody2D* b = &bodies[(__safe_index u8)i];
if (b->active == 0) continue;
if (b->inv_mass_q8 <= 0) continue;
kq2d_asm_value = b->vx;
kq2d_asm_operand = world->gravity_x;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
b->vx = kq2d_asm_value;
kq2d_asm_value = b->vy;
kq2d_asm_operand = world->gravity_y;
kq2d_add_s16_asm();
kq2d_asm_limit = world->max_speed;
kq2d_clamp_abs_s16_asm();
b->vy = kq2d_asm_value;
kq2d_asm_value = b->x;
kq2d_asm_operand = b->vx;
kq2d_add_s16_asm();
b->x = kq2d_asm_value;
kq2d_asm_value = b->y;
kq2d_asm_operand = b->vy;
kq2d_add_s16_asm();
b->y = kq2d_asm_value;
}
// Solve contacts with a small fixed iteration count.
u8 iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
u8 it;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQBody2D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody2D* b = &bodies[(__safe_index u8)j];
if (b->active == 0) continue;
kq2d__resolve_pair(a, b);
}
}
}
}kq2d_surface_toi_q8
根据起止带符号间距,在线性运动假设下估算本次更新内的跨越时刻。
语法
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap);参数
start_gap更新开始时的带符号间距,正值表示位于表面外侧。
end_gap更新结束时的带符号间距,负值表示已越过表面。
返回值
返回 0~256 的 Q8 时间,192 表示本次更新的 75%。
使用条件与限制
间距须在 ±8191 内。起始间距非正返回 0,结束间距非负返回 256。初始重叠与时刻零共用返回值;没有跨越与恰在终点接触也共用返回值。不改变位置。
使用示例
result[4]=kq2d_surface_toi_q8(6,-2);
result[5]=kq2d_surface_toi_q8(6,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色刚体位置不变,绿色速度线从左图的 (8,16) 变为右图的 (2,−8),展示蓝色表面上的反弹和切向摩擦。面板原点为屏幕 (8,48)/(88,48),刚体局部中心为 (24,40)。入射速度为 16,跨越时间示例返回 192/256,限速示例将 (300,400) 缩为 (147,196)。DMG 全部使用黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_surface.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_surface.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:98
Linear crossing time in Q8 (0..256); positive separation is outside. Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.
kitaqgb/lib/physics2d.c:291
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap)
{
if (start_gap <= 0) return 0;
if (end_gap >= 0) return 256;
return kq2d__fraction_q8((u16)start_gap, (u16)(start_gap - end_gap));
}kq2d_world_init
为二维 world 绑定数组,并设置重力 (0,32)、四次接触迭代及各轴速度上限 512。
语法
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count);参数
world包含刚体数组及模拟设置的 world 指针。
bodies调用方持有的刚体数组首地址,包含 body_count 个元素。
body_count数组槽位数 0~255,包括非活动元素。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
使用示例
kq2d_world_init(&world,bodies,2);
result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为初始状态,右侧为一次更新后。红色 16×16 矩形中心从 (24,24) 以速度 (4,16) 到达 (28,36),碰到蓝色地板后 Y 速度为零。面板原点为屏幕 (8,48) 和 (88,48),DMG 中所有标记为黑色。RAM 检查还覆盖初始化、设置函数、积分、矩形/点查询和带符号缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d.h:38
Retain the caller-owned body array without initializing its elements. Set gravity to (0,32), four contact iterations and a per-axis speed limit of 512. A null world is ignored; the array must outlive subsequent world operations.
kitaqgb/lib/physics2d.c:564
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 32;
world->solver_iterations = 4;
world->max_speed = 512;
}physics2d_circle.h — 圆形物体物理
支持圆半径、逆质量、恢复系数、切向摩擦、world 阻尼和边界。调用方须初始化所有刚体字段。friction_q8 和 wall_friction_q8 表示去掉的切向运动比例,linear_damping_q8 表示保留的速度比例。字节系数均除以 256,因此 255 不恰好等于 1.0。
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
圆形距离和法线使用整数近似。即使每个分量都缩至不超过 255,其平方和也不保证小于等于 65535。中心差、半径和及逆质量和须可表示;计算权重时 inv_mass_q8×255 须不超过 32767,示例使用 64。
kq2dc_overlap_circle
用缩减后的中心距离和半径之和近似判断内部重叠。
语法
u8 kq2dc_overlap_circle(const KQCircleBody2D* a, const KQCircleBody2D* b);参数
a第一个圆的有效指针。
b第二个圆的有效指针。
返回值
内部重叠返回 1,刚好接触或分离返回 0。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
圆形距离和法线使用整数近似。即使每个分量都缩至不超过 255,其平方和也不保证小于等于 65535。中心差、半径和及逆质量和须可表示;计算权重时 inv_mass_q8×255 须不超过 32767,示例使用 64。
需要两个有效指针和正半径,忽略活动状态与质量。缩减计算及整数平方根可能使结果不同于精确几何距离检测。
使用示例
result[4]=kq2dc_overlap_circle(&balls[0],&balls[1]);
balls[1].x=18; result[5]=kq2dc_overlap_circle(&balls[0],&balls[1]);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色半径 6 的圆从左图中心 (24,24) 移到右图 (54,24)。局部边界为 (4,4)~(60,60)。右墙将中心限制为 54,X 速度从 32 变为 −15:先应用墙面恢复系数 128/256,再应用阻尼 255/256。面板原点为屏幕 (8,48)/(88,48),DMG 的圆与边框为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d_circle.h:55
Compare a reduced integer center distance with the reduced radius sum. Valid pointers and positive radii are required; activity and mass are ignored. This approximate test requires representable differences, radius sums and squared-distance sum. Touching at the computed distance returns false.
kitaqgb/lib/physics2d_circle.c:397
u8 kq2dc_overlap_circle(const KQCircleBody2D* a, const KQCircleBody2D* b)
{
s16 dx;
s16 dy;
s16 sdx;
s16 sdy;
u8 shift;
u16 adx;
u16 ady;
u16 dist_sq;
u16 dist_scaled;
s16 radius_sum;
u16 radius_sum_scaled;
dx = (s16)(b->x - a->x);
dy = (s16)(b->y - a->y);
sdx = dx;
sdy = dy;
shift = kq2dc__reduce_vector(&sdx, &sdy);
adx = (u16)kq2dc__abs_s16(sdx);
ady = (u16)kq2dc__abs_s16(sdy);
dist_sq = (u16)((adx * adx) + (ady * ady));
dist_scaled = kq2dc__isqrt_u16(dist_sq);
radius_sum = (s16)(a->radius + b->radius);
radius_sum_scaled = kq2dc__scale_down_positive(radius_sum, shift);
if (dist_scaled >= radius_sum_scaled) return 0;
return 1;
}kq2dc_set_bounds
保存并启用圆形 world 边界,不立即移动刚体。
语法
void kq2dc_set_bounds(KQCircleWorld2D* world, s16 min_x, s16 min_y, s16 max_x, s16 max_y);参数
world包含刚体数组及模拟设置的 world 指针。
min_x左边界,圆心至少为 min_x+radius。
min_y上边界,圆心至少为 min_y+radius。
max_x右边界,圆心至多为 max_x−radius。
max_y下边界,圆心至多为 max_y−radius。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
忽略空指针,不验证边界顺序或能否容纳圆,因此宽高至少应等于圆直径。实际位置修正由 step 执行。
使用示例
kq2dc_set_bounds(&world,4,4,60,60);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色半径 6 的圆从左图中心 (24,24) 移到右图 (54,24)。局部边界为 (4,4)~(60,60)。右墙将中心限制为 54,X 速度从 32 变为 −15:先应用墙面恢复系数 128/256,再应用阻尼 255/256。面板原点为屏幕 (8,48)/(88,48),DMG 的圆与边框为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d_circle.h:45
Store and enable bounds without validating their order or fitting bodies immediately. Supply extents large enough for body diameters. Null is ignored.
kitaqgb/lib/physics2d_circle.c:382
void kq2dc_set_bounds(KQCircleWorld2D* world, s16 min_x, s16 min_y, s16 max_x, s16 max_y)
{
if (world == 0) return;
world->use_bounds = 1;
world->min_x = min_x;
world->min_y = min_y;
world->max_x = max_x;
world->max_y = max_y;
}kq2dc_step
积分圆形刚体,处理边界和圆间接触,再应用线性阻尼。
语法
void kq2dc_step(KQCircleWorld2D* world);参数
world包含刚体数组及模拟设置的 world 指针。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
圆形距离和法线使用整数近似。即使每个分量都缩至不超过 255,其平方和也不保证小于等于 65535。中心差、半径和及逆质量和须可表示;计算权重时 inv_mass_q8×255 须不超过 32767,示例使用 64。
最后将 −1~1 的速度分量归零。即使阻尼为 255 也会损失少量速度。静止圆之间的重叠可能保留,静态圆也会被移回已启用的边界内。迭代次数零按一轮处理,忽略空 world/数组。此离散、量化求解器可能随速度改变而以不同顺序处理接触。
使用示例
kq2dc_step(&world);
result[6]=balls[0].x; result[7]=balls[0].y; result[8]=balls[0].vx;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色半径 6 的圆从左图中心 (24,24) 移到右图 (54,24)。局部边界为 (4,4)~(60,60)。右墙将中心限制为 54,X 速度从 32 变为 −15:先应用墙面恢复系数 128/256,再应用阻尼 255/256。面板原点为屏幕 (8,48)/(88,48),DMG 的圆与边框为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d_circle.h:50
Integrate active dynamic bodies, apply bounds, solve contacts, then damp velocity and snap components in -1..1 to zero. Zero solver iterations means one. Pairs with both velocities zero are skipped, so resting overlaps can remain. Bounds can reposition static bodies; null world/array inputs are ignored.
kitaqgb/lib/physics2d_circle.c:433
void kq2dc_step(KQCircleWorld2D* world)
{
KQCircleBody2D* bodies;
u8 count;
u8 i;
u8 iterations;
u8 it;
if (world == 0) return;
if (world->bodies == 0) return;
bodies = world->bodies;
count = world->body_count;
for (i = 0; i < count; i++)
{
KQCircleBody2D* body = &bodies[(__safe_index u8)i];
if (body->active == 0) continue;
if (body->inv_mass_q8 <= 0)
{
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
continue;
}
body->vx = (s16)(body->vx + world->gravity_x);
body->vy = (s16)(body->vy + world->gravity_y);
body->vx = kq2dc__clamp_abs(body->vx, world->max_speed);
body->vy = kq2dc__clamp_abs(body->vy, world->max_speed);
body->x = (s16)(body->x + body->vx);
body->y = (s16)(body->y + body->vy);
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
}
iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQCircleBody2D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
if (a->vx == 0 && a->vy == 0) continue;
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, a);
{
u8 j;
for (j = 0; j < count; j++)
{
KQCircleBody2D* b = &bodies[(__safe_index u8)j];
s16 radius_sum;
if (j == i) continue;
if (b->active == 0) continue;
// Avoid revisiting an earlier moving body, but still allow a moving body
// to resolve against a stationary slot with a lower index. Velocities are live
// solver state, so contact ordering can change which later pairs are visited.
if (j < i && (b->vx != 0 || b->vy != 0)) continue;
radius_sum = (s16)(a->radius + b->radius);
if (kq2dc__abs_s16((s16)(b->x - a->x)) >= radius_sum) continue;
if (kq2dc__abs_s16((s16)(b->y - a->y)) >= radius_sum) continue;
kq2dc__resolve_pair(a, b);
}
}
}
}
for (i = 0; i < count; i++)
{
KQCircleBody2D* body = &bodies[(__safe_index u8)i];
if (body->active == 0) continue;
if (body->inv_mass_q8 <= 0) continue;
body->vx = kq2dc__mul_q8(body->vx, world->linear_damping_q8);
body->vy = kq2dc__mul_q8(body->vy, world->linear_damping_q8);
if (body->vx >= -1 && body->vx <= 1) body->vx = 0;
if (body->vy >= -1 && body->vy <= 1) body->vy = 0;
if (world->use_bounds != 0) kq2dc__resolve_bounds(world, body);
}
}kq2dc_world_init
绑定圆形数组,设置零重力、四轮处理、各轴限速 768 和阻尼 252/256。
语法
void kq2dc_world_init(KQCircleWorld2D* world, KQCircleBody2D* bodies, u8 body_count);参数
world包含刚体数组及模拟设置的 world 指针。
bodies调用方持有的刚体数组首地址,包含 body_count 个元素。
body_count数组槽位数 0~255,包括非活动元素。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
边界初始禁用,数值为 (0,0)~(159,143),墙面恢复系数 240、摩擦 16。没有圆形刚体初始化函数,调用方须设置每个字段。空 world 被忽略。
使用示例
kq2dc_world_init(&world,balls,1);
result[0]=world.gravity_y; result[1]=world.solver_iterations;
result[2]=world.max_speed; result[3]=world.linear_damping_q8;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
红色半径 6 的圆从左图中心 (24,24) 移到右图 (54,24)。局部边界为 (4,4)~(60,60)。右墙将中心限制为 54,X 速度从 32 变为 −15:先应用墙面恢复系数 128/256,再应用阻尼 255/256。面板原点为屏幕 (8,48)/(88,48),DMG 的圆与边框为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_circle.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_circle.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics2d_circle.h:42
Retain a caller-owned array without initializing bodies. Set zero gravity, four contact passes, per-axis limit 768 and damping 252/256; bounds start disabled. A null world is ignored. Keep the array alive for subsequent steps.
kitaqgb/lib/physics2d_circle.c:360
void kq2dc_world_init(KQCircleWorld2D* world, KQCircleBody2D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 0;
world->solver_iterations = 4;
world->max_speed = 768;
world->linear_damping_q8 = 252;
world->use_bounds = 0;
world->min_x = 0;
world->min_y = 0;
world->max_x = 159;
world->max_y = 143;
world->wall_restitution_q8 = 240;
world->wall_friction_q8 = 16;
}physics3d.h — 3D AABB 物理
不旋转的三维长方体物理,使用中心及各轴半尺寸表示。质量/逆质量决定接触权重,restitution_q8 控制反弹,ax/ay/az 提供持续加速度。绘制另行处理,示例用同一刚体的正视图和俯视图展示深度移动及撞墙反弹。
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
last_impact_speed 记录接触轴上的最大接近速度。积分会为活动动态刚体清零此值,静态及非活动刚体保留。break_speed 和 KQ3D_BODY_BROKEN 都不会自动销毁物体,游戏须自行决定破坏时机并修改 flags/active。
kq3d_body_init
初始化三维中心、半尺寸和质量,将速度、加速度、冲击值及标志清零,并激活刚体。
语法
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要修改的刚体指针,本操作未使用的字段保持不变。
x中心或点的 X 坐标,示例中以像素为单位。
y中心或点的 Y 坐标,示例中向下为正。
z中心 Z 坐标,在示例俯视图中沿竖直方向表示。
half_xX 方向半尺寸,完整宽度为其两倍。
half_yY 方向半尺寸,完整高度为其两倍。
half_zZ 方向半尺寸,完整深度为其两倍。
mass_q8质量值,256 表示 1.0,非正值创建静态刚体。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
恢复系数为 224,break_speed 为零。质量遵循 kq3d_body_set_mass 规则。忽略空指针,调用方须提供有效半尺寸。
使用示例
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),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:50
Initialize an active centered box with zero velocity/acceleration, restitution 224, cleared impact/flags and mass set through the weight helper. The caller supplies valid half extents. Null body is ignored.
kitaqgb/lib/physics3d.c:330
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8)
{
if (body == 0) return;
body->x = x;
body->y = y;
body->z = z;
body->vx = 0;
body->vy = 0;
body->vz = 0;
body->ax = 0;
body->ay = 0;
body->az = 0;
body->half_x = half_x;
body->half_y = half_y;
body->half_z = half_z;
body->restitution_q8 = 224;
body->break_speed = 0;
body->last_impact_speed = 0;
body->active = 1;
body->flags = 0;
kq3d_body_set_mass(body, mass_q8);
}kq3d_body_set_mass
保存请求的质量,计算用于位置修正与反弹的逆质量权重。
语法
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8);参数
body要修改的刚体指针,本操作未使用的字段保持不变。
mass_q8质量值,256 表示 1.0,非正值创建静态刚体。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
质量非正时逆质量为零,表示静态。正质量使用整数 32767/max(mass,64),结果至少为 1。保存的质量仍为请求值,该权重不是精确 Q8 倒数。质量 256 得到逆质量 127。速度和活动状态不变,忽略空指针。
使用示例
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),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:55
Store the requested mass; nonpositive mass makes the body static. For positive mass compute inverse weight as max(1,32767/max(mass,64)). The stored mass retains the original input; this is a relative solver weight, not an exact Q8 reciprocal. Null body is ignored.
kitaqgb/lib/physics3d.c:311
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8)
{
if (body == 0) return;
body->mass_q8 = mass_q8;
if (mass_q8 <= 0)
{
body->inv_mass_q8 = 0;
return;
}
if (mass_q8 < 64) mass_q8 = 64;
body->inv_mass_q8 = (s16)(32767 / mass_q8);
if (body->inv_mass_q8 <= 0) body->inv_mass_q8 = 1;
}kq3d_dot_q8_8
将三个带符号乘积各自除以 256、向零截断,然后求和。
语法
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az);参数
x与对应系数相乘的有符号 16 位分量,可表示位置或速度。
y与对应系数相乘的有符号 16 位分量,可表示位置或速度。
z与对应系数相乘的有符号 16 位分量,可表示位置或速度。
ax该轴的有符号字节系数,范围 −128~127,按 1/256 缩放。
ay该轴的有符号字节系数,范围 −128~127,按 1/256 缩放。
az该轴的有符号字节系数,范围 −128~127,按 1/256 缩放。
返回值
以 s16 返回三个量化项之和。
使用条件与限制
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
先将各项向零截断,再求和,结果可能不同于完整求和后只移位一次。该系数类型不能保存 1.0=256,最终和不做饱和处理。
使用示例
result[13]=kq3d_dot_q8_8(256,128,0,64,-64,127);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为 X/Y 正视图,右侧为 X/Z 俯视图。绿色轮廓表示初始位置 (24,24,16),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:74
Sum three signed products, shifting each by eight bits before addition. Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.
kitaqgb/lib/physics3d.c:576
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az)
{
return __sdot3_q8_8(x, y, z, ax, ay, az);
}kq3d_integrate_body
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
清除一个活动动态刚体的冲击值,叠加 world 重力与刚体加速度,对各轴限速,再推进位置。
语法
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);参数
world包含刚体数组及模拟设置的 world 指针。
body要修改的刚体指针,本操作未使用的字段保持不变。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
矩形积分与碰撞函数共享临时存储,不要从中断重入这些物理函数。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
last_impact_speed 记录接触轴上的最大接近速度。积分会为活动动态刚体清零此值,静态及非活动刚体保留。break_speed 和 KQ3D_BODY_BROKEN 都不会自动销毁物体,游戏须自行决定破坏时机并修改 flags/active。
ax/ay/az 会保留,并在下次积分再次应用。仅此函数不解决接触,空指针、非活动及静态刚体不修改。
使用示例
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),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:66
Reset impact for one active dynamic body, add gravity and persistent body acceleration, clamp velocity per axis and advance position. Static/inactive bodies retain impact state. Null inputs are ignored; no contacts are solved. Use a nonnegative speed limit and avoid overflow before clamping.
kitaqgb/lib/physics3d.c:436
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body)
{
if (world == 0) return;
if (body == 0) return;
if (body->active == 0) return;
if (body->inv_mass_q8 <= 0) return;
body->last_impact_speed = 0;
kq3d_asm_value = body->vx;
kq3d_asm_operand = world->gravity_x;
kq3d_add_s16_asm();
kq3d_asm_operand = body->ax;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vx = kq3d_asm_value;
kq3d_asm_value = body->vy;
kq3d_asm_operand = world->gravity_y;
kq3d_add_s16_asm();
kq3d_asm_operand = body->ay;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vy = kq3d_asm_value;
kq3d_asm_value = body->vz;
kq3d_asm_operand = world->gravity_z;
kq3d_add_s16_asm();
kq3d_asm_operand = body->az;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
body->vz = kq3d_asm_value;
kq3d_asm_value = body->x;
kq3d_asm_operand = body->vx;
kq3d_add_s16_asm();
body->x = kq3d_asm_value;
kq3d_asm_value = body->y;
kq3d_asm_operand = body->vy;
kq3d_add_s16_asm();
body->y = kq3d_asm_value;
kq3d_asm_value = body->z;
kq3d_asm_operand = body->vz;
kq3d_add_s16_asm();
body->z = kq3d_asm_value;
}kq3d_overlap_aabb
检查两个中心式长方体在三轴上是否严格重叠。
语法
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b);参数
a有效刚体指针,查询忽略活动状态、质量和标志。
b有效刚体指针,查询忽略活动状态、质量和标志。
返回值
内部重叠返回 1,刚好接触或分离返回 0。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
使用示例
result[14]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);
bodies[1].z=32; result[15]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为 X/Y 正视图,右侧为 X/Z 俯视图。绿色轮廓表示初始位置 (24,24,16),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:70
Test strict overlap on all three axes; face-only contact returns false. Valid pointers are required and flags are ignored. Center differences and extent sums must fit the signed 16-bit magnitude calculations.
kitaqgb/lib/physics3d.c:357
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b)
{
s16 dx = kq3d__abs_s16((s16)(b->x - a->x));
s16 dy = kq3d__abs_s16((s16)(b->y - a->y));
s16 dz = kq3d__abs_s16((s16)(b->z - a->z));
s16 sx = (s16)(a->half_x + b->half_x);
s16 sy = (s16)(a->half_y + b->half_y);
s16 sz = (s16)(a->half_z + b->half_z);
if (dx >= sx) return 0;
if (dy >= sy) return 0;
if (dz >= sz) return 0;
return 1;
}kq3d_step
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
积分活动动态刚体,处理 world 中活动元素之间的接触位置修正、反弹和冲击记录。
语法
void kq3d_step(KQWorld3D* world);参数
world包含刚体数组及模拟设置的 world 指针。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
和、差、半尺寸之和及接触位置修正乘积须保持在有符号 16 位范围内。速度限制在加法之后执行,不能防止加法本身溢出。
矩形积分与碰撞函数共享临时存储,不要从中断重入这些物理函数。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
接触选择穿透最浅的轴,相同深度按 X、Y、Z 顺序优先。只有正在接近的运动产生反弹,使用逆质量及限制在 0~256 的平均恢复系数。不处理高速穿透、旋转、角动量或摩擦。
last_impact_speed 记录接触轴上的最大接近速度。积分会为活动动态刚体清零此值,静态及非活动刚体保留。break_speed 和 KQ3D_BODY_BROKEN 都不会自动销毁物体,游戏须自行决定破坏时机并修改 flags/active。
忽略空 world 或空数组。solver_iterations 为零时仍执行一次接触处理。
中间值 impact×(256+restitution) 和 correction×inverse_mass 也使用 s16,乘积须不超过 32767。例如 restitution=256 时,impact 至多为 63。
使用示例
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),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:61
Integrate active dynamic bodies and reset their impact values, then solve each active unordered pair for the configured passes (zero means one). Static bodies retain previous impact maxima until the game clears them. Acceleration persists; flags and break thresholds remain game-owned. This discrete solver shares non-reentrant ASM scratch and can miss fast crossings.
kitaqgb/lib/physics3d.c:492
void kq3d_step(KQWorld3D* world)
{
if (world == 0) return;
if (world->bodies == 0) return;
KQBody3D* bodies = world->bodies;
u8 count = world->body_count;
u8 i;
for (i = 0; i < count; i++)
{
KQBody3D* b = &bodies[(__safe_index u8)i];
if (b->active == 0) continue;
if (b->inv_mass_q8 <= 0) continue;
b->last_impact_speed = 0;
kq3d_asm_value = b->vx;
kq3d_asm_operand = world->gravity_x;
kq3d_add_s16_asm();
kq3d_asm_operand = b->ax;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vx = kq3d_asm_value;
kq3d_asm_value = b->vy;
kq3d_asm_operand = world->gravity_y;
kq3d_add_s16_asm();
kq3d_asm_operand = b->ay;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vy = kq3d_asm_value;
kq3d_asm_value = b->vz;
kq3d_asm_operand = world->gravity_z;
kq3d_add_s16_asm();
kq3d_asm_operand = b->az;
kq3d_add_s16_asm();
kq3d_asm_limit = world->max_speed;
kq3d_clamp_abs_s16_asm();
b->vz = kq3d_asm_value;
kq3d_asm_value = b->x;
kq3d_asm_operand = b->vx;
kq3d_add_s16_asm();
b->x = kq3d_asm_value;
kq3d_asm_value = b->y;
kq3d_asm_operand = b->vy;
kq3d_add_s16_asm();
b->y = kq3d_asm_value;
kq3d_asm_value = b->z;
kq3d_asm_operand = b->vz;
kq3d_add_s16_asm();
b->z = kq3d_asm_value;
}
u8 iterations = world->solver_iterations;
if (iterations == 0) iterations = 1;
u8 it;
for (it = 0; it < iterations; it++)
{
for (i = 0; i < count; i++)
{
KQBody3D* a = &bodies[(__safe_index u8)i];
if (a->active == 0) continue;
u8 j;
for (j = (u8)(i + 1); j < count; j++)
{
KQBody3D* b = &bodies[(__safe_index u8)j];
if (b->active == 0) continue;
kq3d__resolve_pair(a, b);
}
}
}
}kq3d_world_init
为三维 world 绑定刚体数组,设置重力 (0,24,0)、四轮处理及各轴速度上限 512。
语法
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count);参数
world包含刚体数组及模拟设置的 world 指针。
bodies调用方持有的刚体数组首地址,包含 body_count 个元素。
body_count数组槽位数 0~255,包括非活动元素。
返回值
无返回值。本条说明会更新哪些字段及其生效时机。
使用条件与限制
位置、半径、半尺寸、速度和重力须采用一致的整数单位。一次 step 对应一次模拟更新,示例以一单位表示一像素。函数不会自动将位置转换为 Q8.8,也不负责绘制。
world 仅引用数组,刚体存储由调用方分配、持有并保持有效。初始化 world 不会初始化数组中的元素。
physics3d.c 位于 ROM 存储体 2。示例使用 MBC5、128 KB ROM,先包含该实现,再把 main 放在存储体 0。调用期间不要从外部改变 ROM 映射。
使用示例
kq3d_world_init(&world,bodies,2);
result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧为 X/Y 正视图,右侧为 X/Z 俯视图。绿色轮廓表示初始位置 (24,24,16),红色矩形表示更新后的 (36,24,24),蓝色区域为静态障碍。X 速度从 16 反弹为 −14,Z 速度保持 8。冲击值为 16,但即使 break_speed=1,flags 仍为零。面板原点为屏幕 (8,48)/(88,48),DMG 所有标记均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body3d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm实现与源码
kitaqgb/lib/physics3d.h:46
Borrow the body array without initializing it. Set gravity (0,24,0), four contact passes and a per-axis speed limit of 512. Null world is ignored; the caller retains ownership and must keep the array alive.
kitaqgb/lib/physics3d.c:294
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count)
{
if (world == 0) return;
world->bodies = bodies;
world->body_count = body_count;
world->gravity_x = 0;
world->gravity_y = 24;
world->gravity_z = 0;
world->solver_iterations = 4;
world->max_speed = 512;
}raster.h — 分段及逐扫描线滚动表
Raster 库提供八项分段表,以及在全部 144 条可见行改变 X 的 LineX 渲染器。分段适合固定 HUD、视差、窗口变化和 CGB 颜色区域;LineX 适合波动标题和屏幕特效。请选择一种驱动,不要同时运行。
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
逐扫描行波浪与动态标题入场
逐屏幕行改变背景 X,使立柱和字母弯成波浪。可用于传送、首领登场或标题演出。下方预览循环播放八张截图;ROM 每帧推进波形相位。
Raster_Push 操作最多安排八个分段。要为每条扫描行设置独立水平偏移,请用 Raster_LineXRunFrame。精灵和窗口不会弯曲。渲染器在全部 144 条可见行期间占用 CPU 并屏蔽中断,因此请在调用之间更新游戏逻辑和音频。

标题伴随大幅波浪从右侧进入,变为小幅波动后稳定。它包含同目录的 raster_wave.c,因此两个源文件须保存在一起。下方 API 条目提供构建命令和个别使用说明。
Raster 库提供八项分段表,以及在全部 144 条可见行改变 X 的 LineX 渲染器。分段适合固定 HUD、视差、窗口变化和 CGB 颜色区域;LineX 适合波动标题和屏幕特效。请选择一种驱动,不要同时运行。
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
Raster_BuildHudBottom
构建上方滚动游戏区和底部固定 HUD。LY=0 使用摄像机坐标,在 hud_y 切换为背景 (0,0)。
语法
u8 Raster_BuildHudBottom(u8 hud_y, u8 camera_x, u8 camera_y);参数
hud_y分割行,范围为 1..143。零或≥144 返回 BAD_RANGE。
camera_x背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
camera_y背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
参数有效时,停止现有分段、清空表并添加新分段。参数无效时保留表和计数,但记录 BAD_RANGE。构建成功后仍需 Raster_Commit。
使用示例
Raster_BuildHudBottom(96,8,8);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从第 96 行往下保持固定。摄像机 (8,8) 使上方矩形位于 (24,8) 和 (24,72),底部矩形保持 (32,112)。FIXED BOTTOM HUD 保持在底部。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_hud_bottom.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:73
Build a scrolling upper playfield and fixed bottom HUD after validating the split.
kitaqgb/lib/raster.c:183
u8 Raster_BuildHudBottom(u8 hud_y, u8 camera_x, u8 camera_y)
{
u8 r;
if (hud_y == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (hud_y >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, camera_x, camera_y);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(hud_y, 0, 0);
}Raster_BuildHudTop
为固定顶部 HUD 构建两个背景分段:LY=0 使用 (0,0),从 hud_height 起使用摄像机坐标。这是背景滚动分段,不使用窗口。
语法
u8 Raster_BuildHudTop(u8 hud_height, u8 camera_x, u8 camera_y);参数
hud_height分割行,范围为 1..143。零或≥144 返回 BAD_RANGE。
camera_x背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
camera_y背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
参数有效时,停止现有分段、清空表并添加新分段。参数无效时保留表和计数,但记录 BAD_RANGE。构建成功后仍需 Raster_Commit。
使用示例
Raster_BuildHudTop(32,8,8);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
顶部 32 行保持固定。上方矩形仍为 (32,16);摄像机 (8,8) 将另外两个移到 (24,72) 和 (24,104)。FIXED TOP HUD 保持在顶部。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_hud_top.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:71
Validate the HUD height, then replace pending splits with a fixed top band and scrolling playfield. The new table still requires an explicit commit.
kitaqgb/lib/raster.c:169
u8 Raster_BuildHudTop(u8 hud_height, u8 camera_x, u8 camera_y)
{
u8 r;
if (hud_height == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (hud_height >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, 0, 0);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(hud_height, camera_x, camera_y);
}Raster_BuildParallax2
构建水平偏移不同的两个背景分段:LY=0 使用 far_scx,从 split_y 起使用 near_scx,共用同一个 Y 滚动值。
语法
u8 Raster_BuildParallax2(u8 split_y, u8 far_scx, u8 near_scx, u8 scy);参数
split_y分割行,范围为 1..143。零或≥144 返回 BAD_RANGE。
far_scx背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
near_scx背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scy背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
参数有效时,停止现有分段、清空表并添加新分段。参数无效时保留表和计数,但记录 BAD_RANGE。构建成功后仍需 Raster_Commit。
使用示例
Raster_BuildParallax2(64,0,16,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
X 在安排的第 64 行从 0 变为 16。矩形显示在 (32,16)、(16,80)、(16,112);只有下部区域左移十六像素。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_parallax2.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:76
Build two horizontal-scroll bands sharing one vertical scroll value.
kitaqgb/lib/raster.c:197
u8 Raster_BuildParallax2(u8 split_y, u8 far_scx, u8 near_scx, u8 scy)
{
u8 r;
if (split_y == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (split_y >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, far_scx, scy);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(split_y, near_scx, scy);
}Raster_BuildParallax3
为从 LY=0、y1、y2 开始的分段注册 scx0、scx1、scx2。远、中、近层使用不同水平偏移,共用 Y 滚动值。
语法
u8 Raster_BuildParallax3(u8 y1, u8 y2, u8 scx0, u8 scx1, u8 scx2, u8 scy);参数
y1分割行;要求 1 <= y1 < y2 <= 143。
y2分割行;要求 1 <= y1 < y2 <= 143。
scx0背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scx1背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scx2背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scy背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
参数有效时,停止现有分段、清空表并添加新分段。参数无效时保留表和计数,但记录 BAD_RANGE。构建成功后仍需 Raster_Commit。
使用示例
Raster_BuildParallax3(64,104,0,8,16,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
安排的第 64、104 行使 X 偏移依次为 0、8、16。位于 (32,16)、(24,80)、(16,112) 的矩形展示三个分段逐渐增大的位移。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_parallax3.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:79
Build three horizontal-scroll bands at ordered visible split lines. Invalid parameters leave the previous pending table intact and set the error status.
kitaqgb/lib/raster.c:212
u8 Raster_BuildParallax3(u8 y1, u8 y2, u8 scx0, u8 scx1, u8 scx2, u8 scy)
{
u8 r;
if (y1 == 0) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (y2 >= 144) return Raster_SetError(KQ_RASTER_BAD_RANGE);
if (y2 <= y1) return Raster_SetError(KQ_RASTER_BAD_RANGE);
Raster_Clear();
r = Raster_Push(0, scx0, scy);
if (r != KQ_RASTER_OK) return r;
r = Raster_Push(y1, scx1, scy);
if (r != KQ_RASTER_OK) return r;
return Raster_Push(y2, scx2, scy);
}Raster_Clear
立即停止分段调度,重置计数、顺序和错误状态,以便在同一场景重建表。不会将表存储的每个字节都清零。
语法
void Raster_Clear();返回值
无返回值。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
停止分段不会将 SCX、SCY、WX 或 WY 清零。请用 Scroll_SetBg 等普通设置函数建立所需基础状态。这些函数也不恢复先前的 VBlank/STAT IE 位或 IME。
使用示例
Raster_Clear();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:39
Stop splits immediately and clear bookkeeping. Build and commit a new table to restart.
kitaqgb/lib/raster.c:65
void Raster_Clear()
{
Scroll_SplitReset();
Raster_CountValue = 0;
Raster_LastLyValue = 0;
Raster_HasLastLy = 0;
Raster_LastErrorValue = KQ_RASTER_OK;
}Raster_Commit
启用已构建的共享表。即使先前某次追加失败,之前成功追加的条目仍会启用;调用方须检查每次注册结果。
语法
u8 Raster_Commit();返回值
返回最近保存的错误。零不能证明之前每次注册都成功。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
非空表会设置 VBlank/STAT IE 位并执行 EI,启用中断;其他 IE 位保留。空表会关闭分段调度,但不恢复全局中断配置。
使用示例
result[2]=Raster_Commit();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:67
Publish pending splits and return the stored status. This commits even if an earlier append failed; callers should inspect errors while building the table.
kitaqgb/lib/raster.c:161
u8 Raster_Commit()
{
Scroll_SplitCommit();
return Raster_LastErrorValue;
}Raster_Disable
清除并提交空表,以禁用光栅分段。进入不使用分段滚动的场景时,可用它明确停止效果。
语法
void Raster_Disable();返回值
无返回值。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
停止分段不会将 SCX、SCY、WX 或 WY 清零。请用 Scroll_SetBg 等普通设置函数建立所需基础状态。这些函数也不恢复先前的 VBlank/STAT IE 位或 IME。
使用示例
Raster_Disable();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:41
Commit an empty split table so no raster bands remain active.
kitaqgb/lib/raster.c:75
void Raster_Disable()
{
Raster_Clear();
Scroll_SplitCommit();
}Raster_GetCount
读取通过 Raster 封装注册的分段数量。Clear/Disable 将其清零;仅提交会保留数量。
语法
u8 Raster_GetCount();返回值
返回条目数 0..8。用八减去该值即可得到剩余容量。读取不改变状态。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
result[0]=Raster_GetCount();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:44
Read the number of bands currently recorded by this wrapper.
kitaqgb/lib/raster.c:82
u8 Raster_GetCount()
{
return Raster_CountValue;
}Raster_GetLastError
读取最近保存的 Raster 状态,不清除它。这不是全部以往失败的累计记录。
语法
u8 Raster_GetLastError();返回值
返回保存的状态 0..4。Commit 返回此值,但不会自动撤销失败或丢弃表。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
result[1]=Raster_GetLastError();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:46
Read the last stored status without clearing it.
kitaqgb/lib/raster.c:88
u8 Raster_GetLastError()
{
return Raster_LastErrorValue;
}Raster_Init
清空共享分段表,停止分段,并初始化计数、最后 LY 有效性及错误状态。与 Scroll_Init 不同,不初始化背景/窗口坐标。
语法
void Raster_Init();返回值
无返回值。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
停止分段不会将 SCX、SCY、WX 或 WY 清零。请用 Scroll_SetBg 等普通设置函数建立所需基础状态。这些函数也不恢复先前的 VBlank/STAT IE 位或 IME。
使用示例
Raster_Init();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:37
Stop split scheduling, empty the shared table and clear error/count state.
kitaqgb/lib/raster.c:59
void Raster_Init()
{
Raster_Clear();
}Raster_LineXGetOffset
计算指定行的 SCX,不改变状态、显示或相位。
语法
u8 Raster_LineXGetOffset(const RasterLineX* effect, u8 ly);参数
effect指向已初始化 RasterLineX 的指针,调用期间须保持有效。
ly要查询的行坐标,范围为 0..255;可见行是 0..143。查表按波形周期回绕,不报告范围错误。
返回值
八位 SCX,等于基础 X 加上该行位移。示例在相位 8、行 0 处查表返回 17。
使用条件与限制
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
wave_lookup = Raster_LineXGetOffset(&wave, 0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
立柱和字母在水平方向弯曲,最大位移为 24 像素。一个波形跨 64 条扫描行,相位每帧推进一行。CGB 使用蓝色立柱和文字。应观察每行不同的水平偏移,而不是整个画面的统一移动。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png实现与源码
kitaqgb/lib/raster.h:91
Look up the selected periodic profile at LY + phase and add base X modulo 256. An unknown profile returns the unmodified base X.
kitaqgb/lib/raster.c:264
u8 Raster_LineXGetOffset(const RasterLineX* effect, u8 ly)
{
u8 index;
if (effect->profile == KQ_RASTER_LINE_X_TITLE) {
index = (u8)((ly + effect->phase) & 15);
return (u8)(effect->base_x + Raster_LineXXTitle[(__safe_index u8)index]);
}
if (effect->profile == KQ_RASTER_LINE_TRAVEL_GATE) {
index = (u8)((ly + effect->phase) & 63);
return (u8)(effect->base_x + Raster_LineXTravelGate[(__safe_index u8)index]);
}
return effect->base_x;
}Raster_LineXInit
初始化波形配置和基础 X,相位设为零,每帧推进一步相位。不绘制,也不写滚动寄存器。
语法
void Raster_LineXInit(RasterLineX* effect, u8 profile, u8 base_x);参数
effect指向可写 RasterLineX 存储空间的指针;无需预先初始化。
profile0 (FLAT) 不产生位移;1 (X_TITLE) 每 16 行重复 −4..+3 像素偏移;2 (TRAVEL_GATE) 每 64 行重复 ±24 像素波形。其他值不产生位移。
base_x加到波形位移上的背景 X,单位为像素,范围为 0..255。
返回值
无返回值。
使用条件与限制
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
Raster_LineXInit(&wave, KQ_RASTER_LINE_TRAVEL_GATE, 0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
立柱和字母在水平方向弯曲,最大位移为 24 像素。一个波形跨 64 条扫描行,相位每帧推进一行。CGB 使用蓝色立柱和文字。应观察每行不同的水平偏移,而不是整个画面的统一移动。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png实现与源码
kitaqgb/lib/raster.h:82
Initialize a scanline-wave state with phase zero and one phase step per frame.
kitaqgb/lib/raster.c:229
void Raster_LineXInit(RasterLineX* effect, u8 profile, u8 base_x)
{
effect->profile = profile;
effect->base_x = base_x;
effect->phase = 0;
effect->phase_step = 1;
effect->frame_divider = 1;
effect->frame_counter = 0;
}Raster_LineXRunFrame
等待 VBlank,再为接下来的 144 条可见行逐行设置背景 X。之后递增分频计数器,并按所选间隔更新相位。
语法
void Raster_LineXRunFrame(RasterLineX* effect);参数
effect指向已初始化 RasterLineX 的指针,调用期间须保持有效。
返回值
无返回值。
使用条件与限制
LCD 关闭时立即返回,不改变效果。否则阻塞直到全部 144 行的 SCX 更新完成,恢复中断启用状态,并将 Scroll 辅助层同步为最后的 SCX。返回不保证当前处于 VBlank。直接写 VRAM 前,应等待 VBlank,或使用自行管理安全访问时机的传送例程。
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
Raster_LineXRunFrame(&wave);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
立柱和字母在水平方向弯曲,最大位移为 24 像素。一个波形跨 64 条扫描行,相位每帧推进一行。CGB 使用蓝色立柱和文字。应观察每行不同的水平偏移,而不是整个画面的统一移动。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png实现与源码
kitaqgb/lib/raster.h:96
Render all 144 visible lines with one SCX write per line, then advance phase. This foreground renderer masks interrupts during the visible frame. Do game and audio work between calls; do not combine it with a STAT raster driver. Returns without changing the effect when the LCD is off.
kitaqgb/lib/raster.c:298
void Raster_LineXRunFrame(RasterLineX* effect)
{
u8 interrupt_state;
if ((Raster_LineXLcdc & 0x80) == 0) return;
Raster_LineXTable = Raster_LineXFlat;
Raster_LineXMask = 15;
if (effect->profile == KQ_RASTER_LINE_X_TITLE) {
Raster_LineXTable = Raster_LineXXTitle;
}
if (effect->profile == KQ_RASTER_LINE_TRAVEL_GATE) {
Raster_LineXTable = Raster_LineXTravelGate;
Raster_LineXMask = 63;
}
Raster_LineXPhase = effect->phase;
Raster_LineXBase = effect->base_x;
interrupt_state = __critical_enter();
__asm {
PUSH_AF
PUSH_BC
PUSH_DE
PUSH_HL
// Use the current VBlank when the preceding frame ended there.
// Waiting for another VBlank edge would leave every second frame flat.
raster_linex_vblank:
LDH_A_MEM 0x44
CP_IMM 144
JR_C raster_linex_vblank
LD_B_IMM 0
LD_A_MEM Raster_LineXPhase
LD_C_A
LD_A_MEM Raster_LineXMask
LD_D_A
LD_A_MEM Raster_LineXBase
LD_E_A
raster_linex_line:
LD_A_B
ADD_C
AND_D
LD_L_A
LD_H_IMM 0
PUSH_DE
LD_A_MEM Raster_LineXTable
LD_E_A
LD_A_MEM Raster_LineXTable+1
LD_D_A
ADD_HL_DE
POP_DE
LD_A_HL
ADD_E
PUSH_AF
raster_linex_wait:
LDH_A_MEM 0x44
CP_B
JR_NZ raster_linex_wait
POP_AF
LDH_MEM_A 0x43
INC_B
LD_A_B
CP_IMM 144
JR_NZ raster_linex_line
POP_HL
POP_DE
POP_BC
POP_AF
}
// Synchronize the scroll helper's tracked state with the last hardware write.
__scroll_bg_x_set(Raster_LineXScx);
__critical_leave(interrupt_state);
effect->frame_counter++;
if (effect->frame_counter >= effect->frame_divider) {
effect->frame_counter = 0;
effect->phase = (u8)(effect->phase + effect->phase_step);
}
}Raster_LineXSetPhase
直接设置波形位置,保留基础 X、速度和分频计数器。
语法
void Raster_LineXSetPhase(RasterLineX* effect, u8 phase);参数
effect指向已初始化 RasterLineX 的指针,调用期间须保持有效。
phase八位相位值;所选波形每 16 或 64 步重复。
返回值
无返回值。
使用条件与限制
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
Raster_LineXSetPhase(&wave, 8);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
立柱和字母在水平方向弯曲,最大位移为 24 像素。一个波形跨 64 条扫描行,相位每帧推进一行。CGB 使用蓝色立柱和文字。应观察每行不同的水平偏移,而不是整个画面的统一移动。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png实现与源码
kitaqgb/lib/raster.h:88
Set the byte phase directly without resetting the frame-divider counter.
kitaqgb/lib/raster.c:257
void Raster_LineXSetPhase(RasterLineX* effect, u8 phase)
{
effect->phase = phase;
}Raster_LineXSetProfile
选择波形配置,将相位和帧分频计数器清零,保留基础 X 和速度。
语法
void Raster_LineXSetProfile(RasterLineX* effect, u8 profile);参数
effect指向已初始化 RasterLineX 的指针,调用期间须保持有效。
profile0 (FLAT) 不产生位移;1 (X_TITLE) 每 16 行重复 −4..+3 像素偏移;2 (TRAVEL_GATE) 每 64 行重复 ±24 像素波形。其他值不产生位移。
返回值
无返回值。
使用条件与限制
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
Raster_LineXSetProfile(&wave, KQ_RASTER_LINE_X_TITLE);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
原创标题 ORBIT GUARD 伴随大幅波浪从右侧进入。绘制 48 帧后变为小幅波动;从第 96 帧开始静止。每 160 帧重复入场。图像依次展示入场、小幅波动和稳定后的标题。






构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_title_entry.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_title_entry.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_title_entry.gb --hardware cgb --run-frames 120 --png .\out\raster_title_entry.png实现与源码
kitaqgb/lib/raster.h:84
Change the wave profile and restart its phase/divider counter.
kitaqgb/lib/raster.c:240
void Raster_LineXSetProfile(RasterLineX* effect, u8 profile)
{
effect->profile = profile;
effect->phase = 0;
effect->frame_counter = 0;
}Raster_LineXSetSpeed
设置相位增量和更新间隔。重置分频计数器,保留当前相位。
语法
void Raster_LineXSetSpeed(RasterLineX* effect, u8 phase_step, u8 frame_divider);参数
effect指向已初始化 RasterLineX 的指针,调用期间须保持有效。
phase_step每次更新的相位增量,范围为 0..255。零冻结波形;255 利用八位回绕实现每次后退一步。
frame_divider每次相位更新之间实际绘制的 RunFrame 调用次数,范围为 1..255。零按一处理。因 LCD 关闭而返回的调用不计入。
返回值
无返回值。
使用条件与限制
SCX 为八位值,背景在 256 像素范围内回绕。负位移采用模 256 的字节值。可见视口之外的地图也应填充。
效果改变背景的水平滚动;精灵和窗口不会弯曲。RunFrame 在全部 144 条可见行期间占用 CPU 并屏蔽中断。请在调用之间更新游戏逻辑和音频,不要与 STAT 光栅中断驱动同时使用。
图像来自 KOKURA 中的 DMG 和 CGB 运行。160×144 场景的每个像素(包括立柱和文字)都与预期逐行坐标比较。尚未测试硬件时序。
将 kitaqgb、kokura 和 kitaq-docs 放在同一父目录下,并在该目录打开 PowerShell。ROM 同时支持 DMG 和 CGB;通过 KOKURA --hardware 选择 dmg 或 cgb。
使用示例
Raster_LineXSetSpeed(&wave, 1, 1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
立柱和字母在水平方向弯曲,最大位移为 24 像素。一个波形跨 64 条扫描行,相位每帧推进一行。CGB 使用蓝色立柱和文字。应观察每行不同的水平偏移,而不是整个画面的统一移动。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_wave.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_wave.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
.\kokura\kokura-cli.exe .\out\raster_wave.gb --hardware cgb --run-frames 120 --png .\out\raster_wave.png实现与源码
kitaqgb/lib/raster.h:86
Configure the phase increment and frame interval; clamp a zero interval to one.
kitaqgb/lib/raster.c:248
void Raster_LineXSetSpeed(RasterLineX* effect, u8 phase_step, u8 frame_divider)
{
effect->phase_step = phase_step;
if (frame_divider == 0) frame_divider = 1;
effect->frame_divider = frame_divider;
effect->frame_counter = 0;
}Raster_Push
追加一个分段,在安排的 LY 改变背景 SCX/SCY。此操作只影响背景,不改变窗口或颜色。
语法
u8 Raster_Push(u8 ly, u8 scx, u8 scy);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
scx背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scy背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
Raster_Push(0,0,0);Raster_Push(64,8,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:50
Append a validated background-scroll band and advance the monotonic-LY bookkeeping. A successful append clears the previous error.
kitaqgb/lib/raster.c:95
u8 Raster_Push(u8 ly, u8 scx, u8 scy)
{
u8 r;
r = Raster_CheckPush(ly);
if (r != KQ_RASTER_OK) return r;
Scroll_SplitPush(ly, scx, scy);
Raster_LastLyValue = ly;
Raster_HasLastLy = 1;
Raster_CountValue++;
Raster_LastErrorValue = KQ_RASTER_OK;
return KQ_RASTER_OK;
}Raster_PushBgColor0
追加分段,在一帧中改变 CGB 背景调色板零的颜色零。不选择其他调色板,也不改变其余三个颜色。在 DMG 上注册不会产生彩色效果。
语法
u8 Raster_PushBgColor0(u8 ly, u16 rgb15);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
rgb15RGB15:红色占位 0..4,绿色占位 5..9,蓝色占位 10..14。0x001F 为红,0x03E0 为绿,0x7C00 为蓝。不检查输入范围。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
颜色更新使用 BCPS/BCPD,不保留先前 BCPS 索引。前台代码也写调色板时,应明确设置索引。处理程序等待 LCD 可访问模式,因此可见颜色边界可能晚于安排的 LY。
使用示例
Raster_PushBgColor0(0,0x7C00);Raster_PushBgColor0(64,0x001F);Raster_PushBgColor0(112,0x03E0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
已验证的 CGB 普通速度截图中,第 0..65 行为蓝色,66..113 为红色,114..143 为绿色。安排的 LY 为 0、64、112;中断及调色板访问延迟影响实际可见边界。没有 DMG 彩色版本。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_color.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:63
Pack a 15-bit color into the extended band's two scroll-byte fields and select the color-zero operation, which interprets those fields as color data.
kitaqgb/lib/raster.c:154
u8 Raster_PushBgColor0(u8 ly, u16 rgb15)
{
return Raster_PushEx(ly, (u8)rgb15, (u8)(rgb15 >> 8), 0, 0, KQ_RASTER_BG_COLOR0);
}Raster_PushBgWindowScreen
在同一个分段中更新背景滚动和窗口坐标。封装函数加上 BG 和 WIN,并将窗口屏幕 X 转换为 WX。
语法
u8 Raster_PushBgWindowScreen(u8 ly, u8 scx, u8 scy, u8 screen_x, u8 screen_y, u8 flags);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
scx背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scy背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
screen_x窗口左缘的屏幕 X 坐标。封装函数加七得到 WX,通常使用 0..159。值≥249 时,字节加法会回绕;不要用这些值隐藏窗口。
screen_y窗口 WY,以距顶边的像素数表示。与 X 不同,Y 无需加七。WY≥144 时,在可见区域内没有窗口起始行。
flags通常传 WIN_SHOW 或 WIN_HIDE。封装函数自动加上 WIN,因此零即可只更新坐标、不改变可见性。其他支持位会继续传递;应避免意外的背景或颜色操作。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
Raster_PushBgWindowScreen(0,8,0,24,48,KQ_RASTER_WIN_SHOW);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
背景 X=8 将红色背景矩形移到 (24,16)。窗口原点仍为 (24,48),蓝色矩形保持在 (32,56),展示两套坐标系统互相独立。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bg_window.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:60
Queue both background and window updates, converting window screen X to WX.
kitaqgb/lib/raster.c:145
u8 Raster_PushBgWindowScreen(u8 ly, u8 scx, u8 scy, u8 screen_x, u8 screen_y, u8 flags)
{
flags = (u8)(flags | KQ_RASTER_BG | KQ_RASTER_WIN);
return Raster_PushEx(ly, scx, scy, (u8)(screen_x + 7), screen_y, flags);
}Raster_PushEx
追加扩展分段,执行标志选择的背景、窗口、可见性或颜色操作。普通背景变化可用参数更少的 Raster_Push。
语法
u8 Raster_PushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
scx背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
scy背景滚动字节,范围为 0..255。SCX/SCY 指定从背景地图何处读取,不是屏幕位置。坐标在 256 像素背景内回绕。
wx原始硬件 WX 值。普通屏幕 X 坐标须加七;WX=7 将窗口置于左边缘。本函数不限制或验证 WX。
wy窗口 WY,以距顶边的像素数表示。与 X 不同,Y 无需加七。WY≥144 时,在可见区域内没有窗口起始行。
flags标志:BG=1 更新背景滚动,WIN=2 更新 WX/WY,WIN_SHOW=4 显示窗口,WIN_HIDE=8 隐藏窗口,BG_COLOR0=16 更新 CGB 背景颜色零。未知高位被丢弃。SHOW 和 HIDE 同时存在时,最后应用 SHOW。颜色更新复用 SCX/SCY 数据字段;不要与普通背景滚动组合。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
Raster_PushEx(104,16,0,7,0,KQ_RASTER_BG|KQ_RASTER_WIN_HIDE);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三个 24×16 像素红色矩形(DMG 上为黑色)显示在 (32,16)、(24,80)、(16,112)。越靠下的分段向左位移越大。内部结果记录 count=3、error=0、commit result=0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_bands.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:52
Append a validated extended band after masking flags to supported operations.
kitaqgb/lib/raster.c:112
u8 Raster_PushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags)
{
u8 r;
r = Raster_CheckPush(ly);
if (r != KQ_RASTER_OK) return r;
// Masking removes unknown bits but does not reject conflicting supported operations.
flags = (u8)(flags & KQ_RASTER_FLAG_MASK);
Scroll_SplitPushEx(ly, scx, scy, wx, wy, flags);
Raster_LastLyValue = ly;
Raster_HasLastLy = 1;
Raster_CountValue++;
Raster_LastErrorValue = KQ_RASTER_OK;
return KQ_RASTER_OK;
}Raster_PushWindowRaw
使用原始硬件 WX/WY 追加窗口分段。自动加上 WIN,并将两个背景数据字节设为零。
语法
u8 Raster_PushWindowRaw(u8 ly, u8 wx, u8 wy, u8 flags);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
wx原始硬件 WX 值。普通屏幕 X 坐标须加七;WX=7 将窗口置于左边缘。本函数不限制或验证 WX。
wy窗口 WY,以距顶边的像素数表示。与 X 不同,Y 无需加七。WY≥144 时,在可见区域内没有窗口起始行。
flags通常传 WIN_SHOW 或 WIN_HIDE。封装函数自动加上 WIN,因此零即可只更新坐标、不改变可见性。其他支持位会继续传递;应避免意外的背景或颜色操作。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
Raster_PushWindowRaw(0,31,48,KQ_RASTER_WIN_SHOW);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
WX=31、WY=48 将窗口原点置于屏幕 (24,48)。窗口局部 (8,8) 的蓝色 16×16 矩形显示在屏幕 (32,56)。上方红色背景矩形保持在 (32,16)。DMG 上两者均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_window_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:55
Enable window updates and pass hardware WX/WY values without coordinate conversion.
kitaqgb/lib/raster.c:131
u8 Raster_PushWindowRaw(u8 ly, u8 wx, u8 wy, u8 flags)
{
flags = (u8)(flags | KQ_RASTER_WIN);
return Raster_PushEx(ly, 0, 0, wx, wy, flags);
}Raster_PushWindowScreen
使用屏幕坐标追加窗口分段。先给 X 加七,再调用原始坐标版本,使调用方无需混淆屏幕 X 和 WX。
语法
u8 Raster_PushWindowScreen(u8 ly, u8 screen_x, u8 screen_y, u8 flags);参数
ly安排的可见行号,范围为 0..143,且必须严格大于表中前一项。第一项若为 LY=0,将由 VBlank 处理应用,以建立帧起始状态。
screen_x窗口左缘的屏幕 X 坐标。封装函数加七得到 WX,通常使用 0..159。值≥249 时,字节加法会回绕;不要用这些值隐藏窗口。
screen_y窗口 WY,以距顶边的像素数表示。与 X 不同,Y 无需加七。WY≥144 时,在可见区域内没有窗口起始行。
flags通常传 WIN_SHOW 或 WIN_HIDE。封装函数自动加上 WIN,因此零即可只更新坐标、不改变可见性。其他支持位会继续传递;应避免意外的背景或颜色操作。
返回值
成功返回 KQ_RASTER_OK (0),否则返回错误状态 1..4。请参见函数限制和 Raster_GetLastError。
使用条件与限制
包含 raster.h,并编译 raster.c 和 scroll.c。共享表最多容纳八个分段。用 Raster_* 构建时,不要交错直接调用 Scroll_Split* 或 __scroll_split_*;它们会绕过封装层的计数和最后行号记录。
在 VBlank 或其他能控制显示影响的时段重建:先清除,再追加,最后提交。清除也会停止当前分段,因此不是编辑独立的后备缓冲区。LY 用于安排中断,寄存器写入还需要额外时间。逐扫描行波浪绘制请使用独立的 Raster_LineX* 系列;不要同时运行两个驱动。
状态码为 0 OK、1 FULL、2 BAD_LY、3 UNSORTED、4 BAD_RANGE。追加操作依次检查 LY 范围、容量及顺序。被拒绝的条目不增加表长。之后成功追加或清除会将错误重置为零,因此每次调用都应立即检查返回值。
使用示例
Raster_PushWindowScreen(0,24,48,KQ_RASTER_WIN_SHOW);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
直接传入屏幕 (24,48),得到与原始坐标示例相同的结果:蓝色窗口矩形位于 (32,56)。库会给 X 加七。上方红色背景矩形位于 (32,16)。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\raster_bands_window_screen.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\raster_bands.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/raster.h:58
Convert screen X to hardware WX by adding seven, then queue a window band. The byte addition wraps if callers supply an excessive X coordinate.
kitaqgb/lib/raster.c:139
u8 Raster_PushWindowScreen(u8 ly, u8 screen_x, u8 screen_y, u8 flags)
{
return Raster_PushWindowRaw(ly, (u8)(screen_x + 7), screen_y, flags);
}rpg.h — 随机数、文字、地图、保存及 RPG/ADV/SLG 功能
绝对文本辅助函数直接绘制背景地图。text_window 打开带框区域,text_clear_rect 填充区域,text_print_xy 写入图块字节而不解码控制码。text_print_u8、text_print_u16、text_print_s16 将数值转为十进制,只使用必要格数。替换长度不同的值时,请预留并清除完整字段。
rpg.h 声明的 RNG 函数由 lib/rng.c 实现。它们共享编译器的 GB 生成器:rng_seed 重置状态,rng8/rng_next8 消耗一个字节,rng16/rng_next16 消耗两个字节;rand_range/rng_range、rng_chance 和 weighted_choice 将取值用于游戏选择。调用这些库函数时,请将 rng.c 一起编译。要复现测试,请固定种子和调用顺序;基于取模的选择不保证精确概率。
RLE 使用数量/数值字节对,以数量零结束。rle_decode 从普通指针读取并展开到 RAM;rle_decode_far 从带 ROM 存储体编号的指针读取并展开到 RAM;__rle_decode_vram 则根据 LCD 访问时机,将跨存储体输入直接展开到 VRAM。RAM 函数实现在 lib/rle.c 中,直接 VRAM 操作由编译器生成。三者均返回输出字节数,要求数据有效且目标空间充足。
rpg.h 中声明的游戏状态存储由 lib/flags.c 实现。三个标志函数操作 2048 个独立布尔位;quest_state 和 quest_set_state 操作 64 个独立字节,用于游戏定义的阶段。使用时须编译 flags.c,并将 ID 保持在限制内;若进度需断电保存,请使用独立存档系统。
窗口式会话共用一个活动矩形、光标和字符速度设置。text_open 初始化区域,text_close 擦除区域,text_set_speed 设置每字符的 VBlank 等待次数。text_print 和 text_print_far 解释控制流;text_choice 运行阻塞式垂直选项列表。请先加载图块、安排区域尺寸,并设计游戏如何处理这些阻塞调用。
提供普通地图/元图块绘制、脚本、SRAM 存档和寻路功能。普通格子为 8×8 像素,元图块为 16×16。加载/绘制、移动范围掩码和攻击范围掩码分别操作;游戏负责协调图形加载与事件执行。
搜索使用约 4 KiB 的共享工作区,不可重入。地图最多为 1024 格,探索距离最多为 254。距离 0xFF 表示尚未到达,因此无法表示 255 步的路径。代价和掩码输出各需 width*height 个可写字节。
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
菜单既支持阻塞选择,也支持由游戏循环分别驱动输入和绘制。物品菜单显示名称与数量。
camera_center
将普通地图中的一个格子置于 20×18 图块视口的中心,并在地图边缘限制滚动。
语法
void camera_center(u8 x, u8 y);参数
x格子的 X 坐标。
y格子的 Y 坐标。
返回值
无返回值。
使用条件与限制
视口原点先取 max(x−10,0)、max(y−9,0),再限制在地图边界内,然后将原点乘以 8 的低字节写入 SCX/SCY。元图块模式不应用此操作。它不会向 VRAM 流式加载新的行或列。
使用示例
camera_center(5,4);result[9]=*((u8*)0xFF43);result[10]=*((u8*)0xFF42);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:297
Center an ordinary-map tile coordinate within a 20-by-18 viewport and clamp the scroll origin to its edges. This adjusts scroll registers only; it does not stream newly exposed map rows into VRAM.
kitaqgb/lib/map.c:105
void camera_center(u8 x, u8 y)
{
u8 tile_x = 0;
u8 tile_y = 0;
u8 max_x = 0;
u8 max_y = 0;
map_camera_x = x;
map_camera_y = y;
if (map_loaded == 0) return;
if (map_current.width > 20) max_x = (u8)(map_current.width - 20);
if (map_current.height > 18) max_y = (u8)(map_current.height - 18);
if (x > 10) tile_x = (u8)(x - 10);
if (y > 9) tile_y = (u8)(y - 9);
if (tile_x > max_x) tile_x = max_x;
if (tile_y > max_y) tile_y = max_y;
__scroll_bg_set((u8)(tile_x << 3), (u8)(tile_y << 3));
}far_read16
通过带存储体信息的指针读取一个小端序字。
语法
#define far_read16(p) __farpeek16((p).bank, (p).ptr)参数
p含 bank 和 ptr 的 far_ptr_t,其中 ptr 是 CPU 可见地址。
返回值
以 u16 返回字。
使用条件与限制
宏会分别读取 p.bank 和 p.ptr;不要传入有副作用的表达式。内建函数会临时选择 ROM 存储体,并在结束后恢复。应避免中断同时修改存储体。
使用示例
result[1]=far_read16(far);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从另一个 ROM 存储体读取十六进制数据 34 12 AB:首字节为 52,小端序字为 4660,复制后的末字节为 171。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:172
far_read8
通过带存储体信息的指针读取一个字节。
语法
#define far_read8(p) __farpeek8((p).bank, (p).ptr)参数
p含 bank 和 ptr 的 far_ptr_t,其中 ptr 是 CPU 可见地址。
返回值
以 u8 返回字节。
使用条件与限制
宏会分别读取 p.bank 和 p.ptr;不要传入有副作用的表达式。内建函数会临时选择 ROM 存储体,并在结束后恢复。应避免中断同时修改存储体。
使用示例
far.bank=1;far.ptr=bank_bytes;result[0]=far_read8(far);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从另一个 ROM 存储体读取十六进制数据 34 12 AB:首字节为 52,小端序字为 4660,复制后的末字节为 171。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:171
far_read_block
从带存储体信息的指针处,将连续字节复制到 RAM。
语法
#define far_read_block(dst, p, len) __farmemcpy((dst), (p).bank, (p).ptr, (len))参数
dstlen 个可写 RAM 字节的起始地址。
p含 bank 和 ptr 的 far_ptr_t,其中 ptr 是 CPU 可见地址。
len字节数;源数据范围不得超出所在存储体。
返回值
无返回值。
使用条件与限制
转发到 __farmemcpy 的宏。不会自动跨越 ROM 存储体边界,也不适用于源与目标重叠的内存移动。
使用示例
far_read_block(copy,far,3);result[2]=copy[2];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从另一个 ROM 存储体读取十六进制数据 34 12 AB:首字节为 52,小端序字为 4660,复制后的末字节为 171。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_far.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_far.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:173
flag_clear
将一个游戏标志清为 0,不清除相邻标志。适用于撤销事件条件或显式初始化标志。已清零的标志再次清除后仍为零。
语法
void flag_clear(u16 id);参数
id标志编号,范围为 0–2047,以 u16 传入。字节偏移为 id / 8,字节内位编号为 id % 8,从最低有效位开始。较宽的参数类型不会扩大固定的 2048 标志存储区。
返回值
无(void)。
使用条件与限制
函数不验证 ID。标志 ID 必须小于 2048,任务 ID 必须小于 64;越界访问可能读取或覆盖无关 RAM。示例采用显式初始化,不依赖启动时的内存内容。
flags.c 在 RAM 中拥有 256 字节标志存储和独立的 64 字节任务存储。这些函数不访问卡带存档 RAM,不能跨断电保存。要持久保存进度,游戏必须通过自身存档系统序列化标志和任务值,并在加载时恢复。
包含 rpg.h,并将 lib/flags.c 与游戏一起编译。数组是该源文件的私有数据,请通过标志/任务 API 访问。所有调用方共享存储,因此主循环和中断处理程序的写入协调须自行处理。
使用示例
flag_clear(9);flag_clear(9); // Mark it closed; clearing twice still leaves 0.
cleared=flag_get(9);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例设置门标志 9 后将其清除。即使清除两次,DOOR CLOSED 仍显示 000。之前的 DOOR OPEN 值以 001 保留在画面上,便于比较两次保存的观测结果。
完整程序在白底上显示十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 000。截图只展示 RAM 状态行为,不演示游戏存档。
DMG 模拟器验证覆盖全部 0 至 2047 标志 ID、重复设置/清除、相邻条目,以及 64 个任务各自全部 256 种状态值。最后将 256 字节标志模式和 64 字节任务数据与预期值比较。两个教学画面都在 240 帧后检查。未测试实体硬件或存档持久性。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:372
Clear a flag bit; id must be below 2048.
kitaqgb/lib/flags.c:21
void flag_clear(u16 id)
{
__bit_clear(flags_bits, id);
}flag_get
从库的 RAM 位数组读取一个游戏开关标志。可用于判断门是否打开、物品是否收集,或其他由游戏定义的事件是否发生。读取不会改变标志。
语法
u8 flag_get(u16 id);参数
id标志编号,范围为 0–2047,以 u16 传入。字节偏移为 id / 8,字节内位编号为 id % 8,从最低有效位开始。较宽的参数类型不会扩大固定的 2048 标志存储区。
返回值
规范化的 u8 布尔值:标志清零时为 0,置位时为 1。此 GB 库调用会规范化结果的 GB __bit_test 内建函数。
使用条件与限制
函数不验证 ID。标志 ID 必须小于 2048,任务 ID 必须小于 64;越界访问可能读取或覆盖无关 RAM。示例采用显式初始化,不依赖启动时的内存内容。
flags.c 在 RAM 中拥有 256 字节标志存储和独立的 64 字节任务存储。这些函数不访问卡带存档 RAM,不能跨断电保存。要持久保存进度,游戏必须通过自身存档系统序列化标志和任务值,并在加载时恢复。
包含 rpg.h,并将 lib/flags.c 与游戏一起编译。数组是该源文件的私有数据,请通过标志/任务 API 访问。所有调用方共享存储,因此主循环和中断处理程序的写入协调须自行处理。
使用示例
before=flag_get(9); // Door-open flag: 0 after explicit initialization.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将标志 9 分配给一扇门。显式清除后,BEFORE 显示 000。随后设置和清除标志,分别得到 DOOR OPEN 001 和 DOOR CLOSED 000,说明读取反映了已保存的条件。
完整程序在白底上显示十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 000。截图只展示 RAM 状态行为,不演示游戏存档。
DMG 模拟器验证覆盖全部 0 至 2047 标志 ID、重复设置/清除、相邻条目,以及 64 个任务各自全部 256 种状态值。最后将 256 字节标志模式和 64 字节任务数据与预期值比较。两个教学画面都在 240 帧后检查。未测试实体硬件或存档持久性。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:368
Read a bit from the flag array. Callers must keep id below 2048.
kitaqgb/lib/flags.c:9
u8 flag_get(u16 id)
{
return __bit_test(flags_bits, id);
}flag_set
将一个游戏标志设为 1,保留同一字节中的其他七个标志。重复设置仍保持置位;可用于记录开门等一次性事件。
语法
void flag_set(u16 id);参数
id标志编号,范围为 0–2047,以 u16 传入。字节偏移为 id / 8,字节内位编号为 id % 8,从最低有效位开始。较宽的参数类型不会扩大固定的 2048 标志存储区。
返回值
无(void)。
使用条件与限制
函数不验证 ID。标志 ID 必须小于 2048,任务 ID 必须小于 64;越界访问可能读取或覆盖无关 RAM。示例采用显式初始化,不依赖启动时的内存内容。
flags.c 在 RAM 中拥有 256 字节标志存储和独立的 64 字节任务存储。这些函数不访问卡带存档 RAM,不能跨断电保存。要持久保存进度,游戏必须通过自身存档系统序列化标志和任务值,并在加载时恢复。
包含 rpg.h,并将 lib/flags.c 与游戏一起编译。数组是该源文件的私有数据,请通过标志/任务 API 访问。所有调用方共享存储,因此主循环和中断处理程序的写入协调须自行处理。
使用示例
flag_set(9); // Mark the door as open; setting an already set flag is harmless.
flag_set(9);
opened=flag_get(9);neighbor=flag_get(8); // 1 and 0: adjacent flag is unchanged.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例将门标志 9 设置两次。DOOR OPEN 显示 001,相邻标志 8 保持 NEIGHBOR 000。两个标志位于同一字节,因此相邻值证明设置一个标志会保留另一个。
完整程序在白底上显示十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 000。截图只展示 RAM 状态行为,不演示游戏存档。
DMG 模拟器验证覆盖全部 0 至 2047 标志 ID、重复设置/清除、相邻条目,以及 64 个任务各自全部 256 种状态值。最后将 256 字节标志模式和 64 字节任务数据与预期值比较。两个教学画面都在 240 帧后检查。未测试实体硬件或存档持久性。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:370
Set a flag bit without disturbing adjacent flags; id must be below 2048.
kitaqgb/lib/flags.c:15
void flag_set(u16 id)
{
__bit_set(flags_bits, id);
}map_current_height
读取活动地图的高度。
语法
u8 map_current_height();返回值
返回以格子为单位的高度;未加载地图时返回零。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[1]=map_current_height();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:391
Return the active map height in its own cell units, or zero when no map is loaded.
kitaqgb/lib/map.c:25
u8 map_current_height()
{
if (map_meta_loaded != 0) return map_meta_current.height;
if (map_loaded == 0) return 0;
return map_current.height;
}map_current_width
读取活动地图的宽度。
语法
u8 map_current_width();返回值
返回以格子为单位的宽度;未加载地图时返回零。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[0]=map_current_width();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:389
Return the active map width in its own cell units, or zero when no map is loaded.
kitaqgb/lib/map.c:17
u8 map_current_width()
{
if (map_meta_loaded != 0) return map_meta_current.width;
if (map_loaded == 0) return 0;
return map_current.width;
}map_draw_visible
从指定原点绘制 10×9 个元图块,并记录该摄像机位置,供脏区域重绘使用。
语法
void map_draw_visible(u8 camera_tx, u8 camera_ty);参数
camera_tx最左侧格子的 X 坐标;最大应为 246,以免遍历时发生字节回绕。
camera_ty最上侧格子的 Y 坐标;最大应为 247。
返回值
无返回值。
使用条件与限制
地图外的格子绘制元图块 ID 零,因此必须提供编号零的定义。本调用不会改变 20×18 图块视口之外的格子,也不会改变 CGB 属性。
使用示例
map_draw_visible(1,0);result[9]=*((u8*)0x9800);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:304
Draw a 10-by-9 metatile viewport as 20-by-18 individual background tiles. Out-of-range cells use definition zero; palette attributes are not written. Remember the camera and clear the dirty flag after the full redraw.
kitaqgb/lib/map.c:212
void map_draw_visible(u8 camera_tx, u8 camera_ty)
{
u8 my;
if (map_meta_loaded == 0) return;
map_camera_x = camera_tx;
map_camera_y = camera_ty;
my = 0;
while (my < 9) {
u8 mx = 0;
while (mx < 10) {
u8 tx = (u8)(camera_tx + mx);
u8 ty = (u8)(camera_ty + my);
u8 sx = (u8)(mx << 1);
u8 sy = (u8)(my << 1);
metatile_t meta;
u8 id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
__settile_xy(sx, sy, meta.tile_tl);
__settile_xy((u8)(sx + 1), sy, meta.tile_tr);
__settile_xy(sx, (u8)(sy + 1), meta.tile_bl);
__settile_xy((u8)(sx + 1), (u8)(sy + 1), meta.tile_br);
mx++;
}
my++;
}
map_dirty = 0;
}map_flush_dirty
有脏标记时,按已保存的摄像机原点重绘视口,然后清除脏标记。
语法
void map_flush_dirty();返回值
无返回值。
使用条件与限制
没有脏标记时不执行操作。重绘会覆盖全部 10×9 个元图块,而不只是发生变化的格子。
使用示例
map_flush_dirty();result[11]=*((u8*)0x9800);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:321
Redraw the remembered metatile viewport only when the shared dirty flag is set.
kitaqgb/lib/map.c:254
void map_flush_dirty()
{
if (map_dirty == 0) return;
map_draw_visible(map_camera_x, map_camera_y);
}map_get_attr
从格子的元图块定义中读取调色板元数据。
语法
u8 map_get_attr(u8 tx, u8 ty);参数
tx元图块 X 坐标。
ty元图块 Y 坐标。
返回值
返回调色板字节。地图外的格子使用编号零的定义;未启用元图块模式时返回零。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[3]=map_get_attr(0,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:310
Return the selected definition's palette byte. An out-of-range coordinate uses the metatile-zero fallback; this function does not write CGB attributes.
kitaqgb/lib/map.c:172
u8 map_get_attr(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return 0;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return meta.palette;
}map_get_event
读取元图块的事件 ID;普通地图则返回是否存在事件。
语法
u8 map_get_event(u8 tx, u8 ty);参数
tx元图块 X 坐标。
ty元图块 Y 坐标。
返回值
返回元图块的事件字节,或普通地图的布尔值;格子不存在或位于地图外时返回零。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[7]=map_get_event(0,0);result[8]=map_get_event(3,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:316
Return a metatile event ID, or the ordinary-map Boolean trigger result. Missing/out-of-range metatile cells have no event.
kitaqgb/lib/map.c:197
u8 map_get_event(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return map_trigger_at(tx, ty);
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 0;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return meta.event;
}map_get_metatile
读取格子的元图块 ID。
语法
u8 map_get_metatile(u8 tx, u8 ty);参数
tx元图块 X 坐标。
ty元图块 Y 坐标。
返回值
返回 ID;位于地图外或未启用元图块模式时返回零。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[1]=map_get_metatile(0,0);result[2]=map_get_metatile(3,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:307
Read a row-major metatile ID, returning ID zero for inactive or out-of-range lookups. Callers must provide a usable definition for metatile zero.
kitaqgb/lib/map.c:159
u8 map_get_metatile(u8 tx, u8 ty)
{
u16 index;
if (map_meta_loaded == 0) return 0;
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 0;
index = __map_index(tx, ty, map_meta_current.width);
return __farpeek8(map_meta_data_bank(), map_meta_current.metatile_ids + index);
}map_is_blocked
检查目标格是否阻挡移动;元图块模式使用定义中的 collision 字段。
语法
u8 map_is_blocked(u8 x, u8 y);参数
x格子的 X 坐标。
y格子的 Y 坐标。
返回值
墙壁、地图外坐标或未加载地图时返回 1;可通行格返回 0。普通地图的碰撞数组为 NULL 时,边界内的所有格子都可通行。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[4]=map_is_blocked(2,2);result[5]=map_is_blocked(6,2);result[6]=map_is_blocked(1,2);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:290
Use metatile collision when that mode is active; otherwise read one packed collision bit. Missing maps/out-of-range cells block movement, but a valid ordinary map without a collision array is entirely passable.
kitaqgb/lib/map.c:68
u8 map_is_blocked(u8 x, u8 y)
{
u16 index;
u8 value;
if (map_meta_loaded != 0) return map_is_solid(x, y);
if (map_loaded == 0) return 1;
if (x >= map_current.width || y >= map_current.height) return 1;
if (map_current.collision_bits == 0) return 0;
index = __map_index(x, y, map_current.width);
// Collision uses least-significant bit first within each row-major group of eight cells.
value = __farpeek8(map_bank, map_current.collision_bits + (index >> 3));
if ((value & (u8)(1 << (index & 7))) != 0) return 1;
return 0;
}map_is_solid
检查是否阻挡移动;普通地图模式下改为检查碰撞位。
语法
u8 map_is_solid(u8 tx, u8 ty);参数
tx元图块 X 坐标。
ty元图块 Y 坐标。
返回值
碰撞值非零或格子位于地图外时返回 1,否则返回 0。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[4]=map_is_solid(0,0);result[5]=map_is_solid(1,0);result[6]=map_is_solid(3,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:313
Treat out-of-range metatile cells as solid; otherwise inspect the definition collision flag. In ordinary-map mode, delegate to its packed collision bits.
kitaqgb/lib/map.c:184
u8 map_is_solid(u8 tx, u8 ty)
{
metatile_t meta;
u8 id;
if (map_meta_loaded == 0) return map_is_blocked(tx, ty);
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return 1;
id = map_get_metatile(tx, ty);
map_read_metatile(id, &meta);
return (u8)(meta.collision != 0);
}map_load
加载普通地图描述符,从地图左上角起,向所选背景地图最多绘制 20×18 个图块。
语法
void map_load(u8 bank, const map_t* map);参数
bank存放描述符、碰撞位和事件的 ROM 存储体。
map紧凑排列的九字节 map_t。tiles 包含 width*height 个图块 ID;collision_bits 按最低有效位优先的顺序存放各格碰撞位;events 为每格存放一个字节。读取图块时使用 bank_tiles。
返回值
无返回值。
使用条件与限制
只维护一个共享的活动地图。描述符会被复制,但它引用的数组须在使用期间保持可读。图块图形需另行加载到 VRAM。
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
不会清除较小地图之外的格子。普通地图中,bank_tiles=0 表示存储体零,不会继承描述符所在的存储体。
使用示例
map_load(0,maps);result[2]=map_current_width();result[3]=map_current_height();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:286
Load the nine-byte ordinary-map descriptor and draw up to 20 by 18 tiles from its top-left corner. Drawing does not upload tile graphics or clear background cells beyond smaller map dimensions.
kitaqgb/lib/map.c:35
void map_load(u8 bank, const map_t* map)
{
u8 row;
u8 h;
u8 w;
// The literal copy size depends on the packed map_t layout; changing that layout requires updating this copy.
__farmemcpy(&map_current, bank, map, 9);
map_bank = bank;
map_loaded = 1;
map_meta_loaded = 0;
w = map_current.width;
h = map_current.height;
if (w > 20) w = 20;
if (h > 18) h = 18;
row = 0;
while (row < h) {
u8 x = 0;
u16 base_index = __map_index(0, row, map_current.width);
while (x < w) {
map_row[x] = __farpeek8(map_current.bank_tiles, map_current.tiles + base_index + x);
x++;
}
__settile_row(0, row, map_row, w);
row++;
}
}map_load_metatile
选择元图块地图,将摄像机位置 (0,0) 处的 10×9 格视口展开为 20×18 个图块。
语法
void map_load_metatile(u8 bank, const metatile_map_t* map);参数
bank存放描述符的 ROM 存储体。
map指向 metatile_map_t 的指针。尺寸以 16×16 像素格子计数。它引用一个 ID 数组和一张由七字节 metatile_t 定义组成的表。
返回值
无返回值。
使用条件与限制
只维护一个共享的活动地图。描述符会被复制,但它引用的数组须在使用期间保持可读。图块图形需另行加载到 VRAM。
bank_tiles=0 时继承描述符所在的存储体;非零值则指定 ID 和定义共同使用的存储体。每个定义保存左上、右上、左下、右下图块,以及碰撞、调色板和事件信息。绘制不会自动应用调色板元数据。
使用示例
map_load_metatile(0,&level);result[0]=*((u8*)0x9800);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:300
Select metatile mode, copy its descriptor and draw the initial viewport with the camera at cell (0,0).
kitaqgb/lib/map.c:145
void map_load_metatile(u8 bank, const metatile_map_t* map)
{
map_meta_bank = bank;
__farmemcpy(&map_meta_current, bank, map, sizeof(metatile_map_t));
map_meta_loaded = 1;
map_loaded = 0;
map_dirty = 0;
map_camera_x = 0;
map_camera_y = 0;
map_draw_visible(0, 0);
}map_mark_dirty
传入有效的元图块坐标时,将视口标记为需要重绘。
语法
void map_mark_dirty(u8 tx, u8 ty);参数
tx元图块 X 坐标。
ty元图块 Y 坐标。
返回值
无返回值。
使用条件与限制
设置一个共享的脏标记,而不是将单格更新加入队列。地图外的坐标会被忽略。调用方须先另行修改 ID 或定义,再设置脏标记。
使用示例
ids[1]=1;map_mark_dirty(1,0);result[10]=*((u8*)0x9800);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
修改 RAM 中的元图块 ID,设置脏标记并提交重绘。摄像机位于 (1,0) 时,左上角显示绿色 16×16 方块。示例明确将调色板元数据应用到 CGB 属性;DMG 显示黑色方块。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_metatile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_metatile.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:319
Mark the viewport for a full redraw when a valid metatile cell changes. The coordinate is validated but no per-cell dirty list is retained.
kitaqgb/lib/map.c:246
void map_mark_dirty(u8 tx, u8 ty)
{
if (map_meta_loaded == 0) return;
if (tx >= map_meta_current.width || ty >= map_meta_current.height) return;
map_dirty = 1;
}map_trigger_at
检查格子是否有事件,但不执行事件。
语法
u8 map_trigger_at(u8 x, u8 y);参数
x格子的 X 坐标。
y格子的 Y 坐标。
返回值
有事件时返回 1,否则返回 0;不会返回存储的事件字节或元图块事件 ID。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
使用示例
result[7]=map_trigger_at(5,4);result[8]=map_trigger_at(6,4);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:293
Report whether an event exists at the cell. Ordinary maps expose a Boolean byte-presence test; metatile maps reduce their event ID to a Boolean here.
kitaqgb/lib/map.c:87
u8 map_trigger_at(u8 x, u8 y)
{
u16 index;
u8 value;
if (map_meta_loaded != 0) return (u8)(map_get_event(x, y) != 0);
if (map_loaded == 0) return 0;
if (x >= map_current.width || y >= map_current.height) return 0;
if (map_current.events == 0) return 0;
index = __map_index(x, y, map_current.width);
value = __farpeek8(map_bank, ((const u8*)map_current.events) + index);
return (u8)((value != 0) ? 1 : 0);
}path_find_bfs
寻找只沿上下左右移动的最短路径,并按从起点到终点的顺序写入方向。
语法
u8 path_find_bfs(u8 sx, u8 sy, u8 gx, u8 gy, u8* out_path, u8 max_len);参数
sx地图范围内的起点格 X 坐标。
sy起点格 Y 坐标。
gx终点格 X 坐标;若终点不是起点且被阻挡,则拒绝该终点。
gy终点格 Y 坐标。
out_path至少含 max_len 个字节的可写数组;0=上、1=右、2=下、3=左。
max_len输出容量;路径过长时返回失败,不会截断路径。
返回值
返回路径长度;零表示不可达、坐标无效、max_len 不足,或已经位于终点。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
搜索使用约 4 KiB 的共享工作区,不可重入。地图最多为 1024 格,探索距离最多为 254。距离 0xFF 表示尚未到达,因此无法表示 255 步的路径。代价和掩码输出各需 width*height 个可写字节。
max_len 限制输出长度,而不是搜索半径。失败后不要将输出内容当作新路径使用。
使用示例
result[49]=path_find_bfs(0,2,5,2,route,30);x=0;y=2;for(i=0;i<result[49];i++){if(route[i]==0)y--;if(route[i]==1)x++;if(route[i]==2)y++;if(route[i]==3)x--;}result[50]=x;result[51]=y;result[52]=path_find_bfs(0,2,5,2,route,2);result[53]=path_find_bfs(0,2,2,2,route,30);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:361
Find a shortest cardinal path with BFS and write direction codes 0=up, 1=right, 2=down, 3=left. Return its byte length, or zero for failure or an already-reached goal. The caller supplies max_len output bytes and respects the shared workspace/distance limits; excessive paths are not truncated.
kitaqgb/lib/slg_path.c:78
u8 path_find_bfs(u8 sx, u8 sy, u8 gx, u8 gy, u8* out_path, u8 max_len)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 head = 0;
u16 tail = 0;
u16 start;
u16 goal;
u8 found = 0;
if (w == 0 || h == 0) return 0;
if (sx >= w || sy >= h || gx >= w || gy >= h) return 0;
if ((sx != gx || sy != gy) && map_is_blocked(gx, gy) != 0) return 0;
total = (u16)((u16)w * (u16)h);
__memset(path_dist, 0xFF, total);
start = __map_index(sx, sy, w);
goal = __map_index(gx, gy, w);
path_dist[start] = 0;
path_queue[tail++] = start;
while (head < tail) {
u16 cur = path_queue[head++];
u8 cost = path_dist[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (cur == goal) {
found = 1;
break;
}
if (y > 0) {
u16 next = (u16)(cur - w);
if (path_dist[next] == 0xFF && map_is_blocked(x, (u8)(y - 1)) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(x + 1) < w) {
u16 next = (u16)(cur + 1);
if (path_dist[next] == 0xFF && map_is_blocked((u8)(x + 1), y) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(y + 1) < h) {
u16 next = (u16)(cur + w);
if (path_dist[next] == 0xFF && map_is_blocked(x, (u8)(y + 1)) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if (x > 0) {
u16 next = (u16)(cur - 1);
if (path_dist[next] == 0xFF && map_is_blocked((u8)(x - 1), y) == 0) {
path_dist[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
}
// Reject unreachable or oversized paths before reconstructing output.
if (found == 0) return 0;
if (path_dist[goal] > max_len) return 0;
{
// Walk backward through decreasing costs, recording forward directions in
// reverse order. The fixed neighbor order makes tied routes deterministic.
u8 len = 0;
u16 cur = goal;
while (cur != start) {
u8 cur_cost = path_dist[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (y > 0 && path_dist[(u16)(cur - w)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 2;
cur = (u16)(cur - w);
continue;
}
if ((u8)(x + 1) < w && path_dist[(u16)(cur + 1)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 3;
cur = (u16)(cur + 1);
continue;
}
if ((u8)(y + 1) < h && path_dist[(u16)(cur + w)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 0;
cur = (u16)(cur + w);
continue;
}
if (x > 0 && path_dist[(u16)(cur - 1)] == (u8)(cur_cost - 1)) {
path_rev[len++] = 1;
cur = (u16)(cur - 1);
continue;
}
return 0;
}
// Reverse the recovered directions so the first output step starts at the origin.
while (len != 0) {
len--;
out_path[path_dist[goal] - 1 - len] = path_rev[len];
}
return path_dist[goal];
}
}quest_set_state
替换一个任务的完整状态字节。各值的含义由游戏定义,例如用 1 表示已接受、3 表示已完成。函数不检查状态转换是否合法,也不触发奖励或回调。
语法
void quest_set_state(u8 quest_id, u8 state);参数
quest_id任务编号,范围为 0–63,以 u8 传入。直接索引 64 个状态字节之一;这是游戏定义的 ID,不是指针或已完成任务数。
state新的 u8 状态,范围为 0–255。全部八位替换原值。库不为这些值规定内置含义,也不保留 0 或 255。
返回值
无(void)。
使用条件与限制
函数不验证 ID。标志 ID 必须小于 2048,任务 ID 必须小于 64;越界访问可能读取或覆盖无关 RAM。示例采用显式初始化,不依赖启动时的内存内容。
flags.c 在 RAM 中拥有 256 字节标志存储和独立的 64 字节任务存储。这些函数不访问卡带存档 RAM,不能跨断电保存。要持久保存进度,游戏必须通过自身存档系统序列化标志和任务值,并在加载时恢复。
包含 rpg.h,并将 lib/flags.c 与游戏一起编译。数组是该源文件的私有数据,请通过标志/任务 API 访问。所有调用方共享存储,因此主循环和中断处理程序的写入协调须自行处理。
使用示例
quest_set_state(2,1); // In this game, 1 means accepted.
started=quest_state(2);
quest_set_state(2,3); // In this game, 3 means completed; replace the whole byte.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例为任务 2 先保存 1,再用 3 覆盖。ACCEPTED 显示首次观测值 001,COMPLETED 显示替换后的 003。将 255 保存到最后一个有效任务 ID 63 后,LAST QUEST 为 255。
完整程序在白底上显示十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 000。截图只展示 RAM 状态行为,不演示游戏存档。
DMG 模拟器验证覆盖全部 0 至 2047 标志 ID、重复设置/清除、相邻条目,以及 64 个任务各自全部 256 种状态值。最后将 256 字节标志模式和 64 字节任务数据与预期值比较。两个教学画面都在 240 帧后检查。未测试实体硬件或存档持久性。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:377
Replace a quest-state byte without validating its game-specific meaning. The caller must keep quest_id below 64.
kitaqgb/lib/flags.c:34
void quest_set_state(u8 quest_id, u8 state)
{
quest_states[quest_id] = state;
}quest_state
读取某个任务保存的状态字节。与单个位标志不同,任务字节可以区分未开始、已接受、已完成等多个阶段。函数只读取存储,不执行任务逻辑或推进阶段。
语法
u8 quest_state(u8 quest_id);参数
quest_id任务编号,范围为 0–63,以 u8 传入。直接索引 64 个状态字节之一;这是游戏定义的 ID,不是指针或已完成任务数。
返回值
已保存的 u8 值,范围为 0–255。这是完整状态字节,不是规范化布尔值;请与游戏定义的阶段值比较。
使用条件与限制
函数不验证 ID。标志 ID 必须小于 2048,任务 ID 必须小于 64;越界访问可能读取或覆盖无关 RAM。示例采用显式初始化,不依赖启动时的内存内容。
flags.c 在 RAM 中拥有 256 字节标志存储和独立的 64 字节任务存储。这些函数不访问卡带存档 RAM,不能跨断电保存。要持久保存进度,游戏必须通过自身存档系统序列化标志和任务值,并在加载时恢复。
包含 rpg.h,并将 lib/flags.c 与游戏一起编译。数组是该源文件的私有数据,请通过标志/任务 API 访问。所有调用方共享存储,因此主循环和中断处理程序的写入协调须自行处理。
使用示例
finished=quest_state(2); // Read back 3 without advancing any quest logic.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在游戏明确将任务 2 的状态从 1 改为 3 后读取它。ACCEPTED 显示 001,COMPLETED 显示 003。名称和阶段含义由示例选择,库不会强制规定。
完整程序在白底上显示十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 000。截图只展示 RAM 状态行为,不演示游戏存档。
DMG 模拟器验证覆盖全部 0 至 2047 标志 ID、重复设置/清除、相邻条目,以及 64 个任务各自全部 256 种状态值。最后将 256 字节标志模式和 64 字节任务数据与预期值比较。两个教学画面都在 240 帧后检查。未测试实体硬件或存档持久性。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\flags_quests.c .\kitaqgb\lib\flags.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\flags_quests.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:374
Read a quest-state byte; quest_id must be below 64.
kitaqgb/lib/flags.c:27
u8 quest_state(u8 quest_id)
{
return quest_states[quest_id];
}rand_range
通过 rng8() % max 选择小于 max、从零开始的值。例如上限 10 得到 0 至 9 的索引。上限为零时立即返回零,不取随机值。
语法
u8 rand_range(u8 max);参数
max不含端点的上限,以 u8 表示,范围为 0–255。max > 0 时结果小于 max。此参数无法表示 256;若需要完整字节范围而不取模,请使用 rng8。
返回值
max > 0 时,取值一次后返回 0 至 max−1 的 u8;max == 0 时不取值,直接返回零。即使 max == 1、结果始终为零,也会消耗一次取值。
使用条件与限制
输入范围不能被上限整除时,取模可能偏向某些结果。底层生成器也不一定均匀。请将有界结果和概率阈值视为游戏近似,而不是精确概率。
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
bounded=rand_range(10); // An index from 0 through 9; this seed yields 2.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例选择小于 10 的索引。RANGE 10 显示 00002,因为种子 0x1234 的首字节为 2。ZERO BOUND 为 00000,随后 NEXT BYTE 仍为 00002,证明零上限调用没有取值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:199
Reduce a random byte modulo max; zero returns zero without consuming randomness. Modulo reduction is biased unless max divides the generator range.
kitaqgb/lib/rng.c:37
u8 rand_range(u8 max)
{
if (max == 0) return 0;
return (u8)(rng8() % max);
}range_fill_move
使用广度优先搜索,沿上下左右四个方向以每格一单位的代价扩展,生成移动代价表。
语法
void range_fill_move(u8 sx, u8 sy, u8 move, u8* out_costmap);参数
sx地图范围内的起点格 X 坐标。
sy起点格 Y 坐标。
move移动上限,范围为 0..254;为零时只有起点可达。
out_costmap含
width*height个字节的可写代价数组:255 表示未到达,起点为零。
返回值
无返回值。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
搜索使用约 4 KiB 的共享工作区,不可重入。地图最多为 1024 格,探索距离最多为 254。距离 0xFF 表示尚未到达,因此无法表示 255 步的路径。代价和掩码输出各需 width*height 个可写字节。
未加载地图或起点无效时,输出保持不变。搜索不会进入被阻挡的目标格。
使用示例
range_fill_move(0,2,3,costs);for(i=0;i<30;i++)result[13+i]=costs[i];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:365
Flood cardinal neighbors at unit cost, stopping expansion at the movement limit and rejecting blocked destinations. Initialize output to 0xFF only after validating the map and origin; invalid input leaves output unchanged.
kitaqgb/lib/slg_path.c:14
void range_fill_move(u8 sx, u8 sy, u8 move, u8* out_costmap)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 head = 0;
u16 tail = 0;
u16 start;
if (w == 0 || h == 0) return;
if (sx >= w || sy >= h) return;
total = (u16)((u16)w * (u16)h);
__memset(out_costmap, 0xFF, total);
start = __map_index(sx, sy, w);
out_costmap[start] = 0;
path_queue[tail++] = start;
while (head < tail) {
u16 cur = path_queue[head++];
u8 cost = out_costmap[cur];
u8 y = (u8)(cur / w);
u8 x = (u8)(cur - ((u16)y * (u16)w));
if (cost >= move) continue;
if (y > 0) {
u16 next = (u16)(cur - w);
if (out_costmap[next] == 0xFF && map_is_blocked(x, (u8)(y - 1)) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(x + 1) < w) {
u16 next = (u16)(cur + 1);
if (out_costmap[next] == 0xFF && map_is_blocked((u8)(x + 1), y) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if ((u8)(y + 1) < h) {
u16 next = (u16)(cur + w);
if (out_costmap[next] == 0xFF && map_is_blocked(x, (u8)(y + 1)) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
if (x > 0) {
u16 next = (u16)(cur - 1);
if (out_costmap[next] == 0xFF && map_is_blocked((u8)(x - 1), y) == 0) {
out_costmap[next] = (u8)(cost + 1);
path_queue[tail++] = next;
}
}
}
}rle_decode
将游程编码的字节流展开到普通可写 RAM。每组数量/数值重复输出指定次数的一个字节,数量为零时结束。压缩数据可通过普通指针读取,且需要在上传前检查或修改展开结果时,使用此接口。
语法
u16 rle_decode(void* dst, const void* src);参数
dst目标可写 RAM 指针,空间必须足够容纳全部展开数据。函数没有容量参数。输出与压缩源数据须分开,防止覆盖尚未读取的编码。
src指向数量/数值字节对的可读指针,以数量零结束。普通数据段的数量为 1~255,数值可以是任意字节,包括零。结束用的零后面没有数值字节。整个调用期间,源数据必须在当前映射中可读。
返回值
以 u16 返回写入的字节数。首个数量为零时不输出数据,返回 0;结束标记不会被复制。展开长度须能用 u16 表示。返回值没有独立错误码,也不是图块数量。
使用条件与限制
此格式面向字节,不是文本。例如 {3, 0x12, 2, 0xA5, 0} 展开为五字节:12 12 12 A5 A5,这里的输出以十六进制表示。非零数量之后的数值零属于普通数据,不是结束标记。
接口没有压缩长度、输出容量或错误数据参数。生成或载入素材时应验证格式,提供数量零的结束标记,并预先计算展开长度。无效或未终止的数据可能导致源读取越界和目标写入越界。
RAM 解码函数使用普通写入,不与 LCD 可访问时段同步,因此目标必须放在 RAM 中。显示结果时应另行调用适当的 VRAM 复制 API;示例在关闭 LCD 后上传数据。
读取带存储体编号的数据期间,不要让中断程序改变 ROM 映射或重用远地址复制辅助函数,因为这些操作共享存储体管理状态。普通指针解码函数及源数据必须处于兼容的映射中,切换到函数所在存储体时不能使源指针失效。
rle_decode 和 rle_decode_far 在 rpg.h 中声明,在 lib/rle.c 中实现;游戏应只编译一次实现。完整示例在 #pragma bank 0 后包含 rle.c,使调用方和解码器都位于公共存储体;对此示例不要再次把 rle.c 作为独立输入。__rle_decode_vram 内建函数本身无需库实现文件。
使用示例
near_count=rle_decode(near_buffer+1,local_rle); // 48 bytes in writable RAM.
__vram_copy(0x8800,near_buffer+1,near_count); // Upload those bytes while the LCD is off.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本例将九个编码字节展开到带保护区的 RAM 缓冲,再把 48 字节结果复制到图块地址 0x8800。RLE TO RAM 应显示 048。最上排展示普通指针路径解码的三个图块,在 CGB 上为红色。
每排从左到右分别为 8×8 实心方块、单像素宽边框的 8×8 空心方框,以及占图块左半边的 4×8 实心矩形。图块列为 2、5、8,对应像素 X=16、40、64;三排起始 Y 为 32、72、112。DMG 使用黑色图形,CGB 三排依次为红、绿、蓝。FAILED 必须为 000。数值表示字节数:三个 16 字节 GB 图块共需展开 48 字节。
现有验证矩阵包含 42 个用例,覆盖空流、单字节数据段、数值零、255 字节数据段、跨目标页的 510 字节输出,以及跨源页的编码。检查全部 528 个观测目标/保护字节、输出长度和恢复后的 ROM 映射。直接 VRAM 用例包括 DMG、CGB 的 LCD 开/关及 CGB VBK 1。两个教学画面在模拟器运行 240 帧后检查。验证不涵盖无效素材、中断并发调用或实机。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:382
Expand count/value byte pairs until a zero count terminates the stream. The caller must provide a valid terminator and sufficient destination storage; there are no source or destination bounds parameters. Return the 16-bit byte count.
kitaqgb/lib/rle.c:8
u16 rle_decode(void* dst, const void* src)
{
const u8* in = (const u8*)src;
u8* out = (u8*)dst;
u16 total = 0;
while (1) {
u8 count = *in++;
u8 value;
if (count == 0) break;
value = *in++;
while (count != 0) {
*out++ = value;
total++;
count--;
}
}
return total;
}rle_decode_far
通过指定 ROM 存储体的 __farpeek8 逐字节读取相同的数量/数值格式,并展开到 RAM。适合放在可切换 ROM 存储体中的压缩素材。目标仍是普通 RAM 指针,bank 参数描述的是源数据。
语法
u16 rle_decode_far(void* dst, u8 bank, const void* src);参数
dst目标可写 RAM 指针,空间必须足够容纳全部展开数据。函数没有容量参数。输出与压缩源数据须分开,防止覆盖尚未读取的编码。
banku8 类型的源 ROM 存储体编号。编号及 CPU 地址必须与卡带映射器和素材布局一致。每次读取编码字节后会恢复存储体选择。此参数不选择目标 VRAM 存储体,也不能表示 MBC5 的九位存储体编号。
src指定存储体中压缩流的 CPU 指针。函数会递增地址,但存储体编号不变。包括结束标记在内的完整数据必须位于可读映射范围中;越过 0x7FFF 不会自动转到下一个 ROM 存储体。
返回值
以 u16 返回写入的字节数。首个数量为零时不输出数据,返回 0;结束标记不会被复制。展开长度须能用 u16 表示。返回值没有独立错误码,也不是图块数量。
使用条件与限制
此格式面向字节,不是文本。例如 {3, 0x12, 2, 0xA5, 0} 展开为五字节:12 12 12 A5 A5,这里的输出以十六进制表示。非零数量之后的数值零属于普通数据,不是结束标记。
接口没有压缩长度、输出容量或错误数据参数。生成或载入素材时应验证格式,提供数量零的结束标记,并预先计算展开长度。无效或未终止的数据可能导致源读取越界和目标写入越界。
RAM 解码函数使用普通写入,不与 LCD 可访问时段同步,因此目标必须放在 RAM 中。显示结果时应另行调用适当的 VRAM 复制 API;示例在关闭 LCD 后上传数据。
读取带存储体编号的数据期间,不要让中断程序改变 ROM 映射或重用远地址复制辅助函数,因为这些操作共享存储体管理状态。普通指针解码函数及源数据必须处于兼容的映射中,切换到函数所在存储体时不能使源指针失效。
rle_decode 和 rle_decode_far 在 rpg.h 中声明,在 lib/rle.c 中实现;游戏应只编译一次实现。完整示例在 #pragma bank 0 后包含 rle.c,使调用方和解码器都位于公共存储体;对此示例不要再次把 rle.c 作为独立输入。__rle_decode_vram 内建函数本身无需库实现文件。
使用示例
far_count=rle_decode_far(far_buffer+1,2,banked_rle); // Read encoded pairs from ROM bank 2.
if(*rle_bank_marker!=0xA1)failures++; // ROM bank 1 must be visible again.
__vram_copy(0x8830,far_buffer+1,far_count);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
本例从 ROM 存储体 2 读取压缩流,向 RAM 展开 48 字节,再上传到 0x8830。BANKED RAM 应显示 048。中间一排在 CGB 上为绿色,并检查 ROM 存储体 1 中的标记,以确认调用方映射得到恢复。
每排从左到右分别为 8×8 实心方块、单像素宽边框的 8×8 空心方框,以及占图块左半边的 4×8 实心矩形。图块列为 2、5、8,对应像素 X=16、40、64;三排起始 Y 为 32、72、112。DMG 使用黑色图形,CGB 三排依次为红、绿、蓝。FAILED 必须为 000。数值表示字节数:三个 16 字节 GB 图块共需展开 48 字节。
现有验证矩阵包含 42 个用例,覆盖空流、单字节数据段、数值零、255 字节数据段、跨目标页的 510 字节输出,以及跨源页的编码。检查全部 528 个观测目标/保护字节、输出长度和恢复后的 ROM 映射。直接 VRAM 用例包括 DMG、CGB 的 LCD 开/关及 CGB VBK 1。两个教学画面在模拟器运行 240 帧后检查。验证不涵盖无效素材、中断并发调用或实机。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:386
Decode the same count/value format using bank-qualified reads for every source byte. Keep the encoded stream within the supplied bank/address mapping and provide enough destination space; a zero count ends the stream.
kitaqgb/lib/rle.c:34
u16 rle_decode_far(void* dst, u8 bank, const void* src)
{
const u8* p = (const u8*)src;
u8* out = (u8*)dst;
u16 total = 0;
while (1) {
u8 count = __farpeek8(bank, p);
u8 value;
p++;
if (count == 0) break;
value = __farpeek8(bank, p);
p++;
while (count != 0) {
*out++ = value;
total++;
count--;
}
}
return total;
}rng16
用连续两次 rng8 取值构造无符号 16 位值。首字节占位 15–8,第二字节占位 7–0。一个字节不足以表示所需范围时使用。
语法
u16 rng16();返回值
一个 u16 结果,范围为 0–65535,按 (first_byte << 8) | second_byte 构造。恰好消耗两次字节取值;不表示全部 65536 个值均匀分布。
使用条件与限制
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
word=rng16(); // First draw 2 is high, second draw 34 is low: 0x0222 = 546.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例展示字的组装。重新设置种子 0x1234 后,字节 2 和 34 组成 0x0222,因此 RNG16 显示 00546。FIRST 和 SECOND 分别显示对应字节值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:196
Consume two generator bytes in high-byte/low-byte order to form a 16-bit value.
kitaqgb/lib/rng.c:22
u16 rng16()
{
u16 hi = (u16)rng8();
u16 lo = (u16)rng8();
return (u16)((hi << 8) | lo);
}rng8
通过 GB 库取得一个伪随机字节。rng8 恰好调用一次 __rng8,是 rng.c 其他辅助函数使用的基本取值操作。
语法
u8 rng8();返回值
共享生成器推进一次后的 u8 结果,范围为 0–255。这描述类型和边界,不表示每个字节等概率。
使用条件与限制
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234); // Reset the same generator used by the intrinsics.
wrapped=rng8(); // One byte draw: 2 after the preceding seed.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例比较库的字节取值与内建函数。rng_seed(0x1234) 后,RNG8 显示 00002,与 FIRST 一致。设置种子不消耗首次取值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:194
Advance the compiler-provided pseudorandom generator and return one byte.
kitaqgb/lib/rng.c:4
u8 rng8()
{
return __rng8();
}rng_chance
通过 rng_range(100) < percent 作出是/否选择,适用于敌人是否掉落物品等近似游戏概率。零始终返回假,100 及以上始终返回真;这两端均不消耗随机取值。
语法
u8 rng_chance(u8 percent);参数
percent以 u8 表示的请求概率。1–99 使用一次取模后的字节;0 返回假,100–255 返回真。中间值是近似阈值,不保证精确百分比。
返回值
规范化布尔值:0 表示假,1 表示真。阈值为 1 至 99 时消耗一次字节取值。
使用条件与限制
输入范围不能被上限整除时,取模可能偏向某些结果。底层生成器也不一定均匀。请将有界结果和概率阈值视为游戏近似,而不是精确概率。
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
chance=rng_chance(50); // Here 2 % 100 < 50, so the result is 1 (true).这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例进行一次近似 50% 的判断。种子对应的字节为 2,2 % 100 < 50 为真,因此 CHANCE 50 显示 00001。一次成功取值不能证明长期成功率为 50%。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:214
Return false for zero and true for values at least 100. Intermediate values use the modulo-based range generator, so percentages are approximate.
kitaqgb/lib/rng.c:51
u8 rng_chance(u8 percent)
{
if (percent == 0) return 0;
if (percent >= 100) return 1;
return (u8)(rng_range(100) < percent);
}rng_next16
返回与 rng16 相同的两次取值组成的字。通过库提供别名,首次取值在高字节、第二次在低字节;不会先额外跳过一个值。
语法
u16 rng_next16();返回值
一个 u16 结果,范围为 0–65535,按 (first_byte << 8) | second_byte 构造。恰好消耗两次字节取值;不表示全部 65536 个值均匀分布。
使用条件与限制
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
nextword=rng_next16(); // The same two-draw word: 546.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在相同种子后比较字别名与 rng16。NEXT16 和 RNG16 均显示 00546,表示按高/低顺序组合两次字节取值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:209
Alias the two-step 16-bit generator.
kitaqgb/lib/rng.c:30
u16 rng_next16()
{
return rng16();
}rng_next8
使用与 rng8 相同的操作取得下一字节。名称强调推进序列;依次调用两个名称,会从同一个共享生成器消耗两个连续值。
语法
u8 rng_next8();返回值
共享生成器推进一次后的 u8 结果,范围为 0–255。这描述类型和边界,不表示每个字节等概率。
使用条件与限制
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
nextbyte=rng_next8(); // One draw, just like rng8(): 2.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在相同种子后检查字节别名。NEXT8 和 RNG8 均显示 00002,因为每次比较前都重新设置种子;若不重置,它们会消耗连续的不同取值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:207
Alias the byte generator; this consumes one generator step.
kitaqgb/lib/rng.c:16
u8 rng_next8()
{
return rng8();
}rng_range
使用相同的不含端点上限调用 rand_range。此写法将有界选择归入 rng_ 函数,使用相同的字节取值和取模,包括上限为零时不取值直接返回。
语法
u8 rng_range(u8 max);参数
max不含端点的上限,以 u8 表示,范围为 0–255。max > 0 时结果小于 max。此参数无法表示 256;若需要完整字节范围而不取模,请使用 rng8。
返回值
max > 0 时,取值一次后返回 0 至 max−1 的 u8;max == 0 时不取值,直接返回零。即使 max == 1、结果始终为零,也会消耗一次取值。
使用条件与限制
输入范围不能被上限整除时,取模可能偏向某些结果。底层生成器也不一定均匀。请将有界结果和概率阈值视为游戏近似,而不是精确概率。
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234);
alias=rng_range(10); // The same exclusive upper bound and result, 2.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例比较有界选择的两种写法。每次调用前重设种子,使 ALIAS 10 和 RANGE 10 均显示 00002。上限 10 本身不包含在结果中。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:211
Alias rand_range and return a value in [0,max), or zero when max is zero.
kitaqgb/lib/rng.c:44
u8 rng_range(u8 max)
{
return rand_range(max);
}rng_seed
通过 GB 库接口设置种子。这是 __rng_seed 的封装,重置 rng8、rng16 和选择辅助函数共用的状态,不创建独立生成器。
语法
void rng_seed(u16 seed);参数
seed无符号 16 位种子,范围为 0–65535。两个字节原样保存。在希望复现的游戏取值序列开始前,选择并记录种子。
返回值
无(void)。
使用条件与限制
接受零状态。下一次取字节时,GB 生成器先替换为 0x5AA5 再推进,因此 __rng_seed(0) 后调用 __rng8() 返回 122。反复设置种子只会重新开始序列,不会增加随机性。
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
rng_seed(0x1234); // Reset the same generator used by the intrinsics.
wrapped=rng8(); // One byte draw: 2 after the preceding seed.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例比较库的字节取值与内建函数。rng_seed(0x1234) 后,RNG8 显示 00002,与 FIRST 一致。设置种子不消耗首次取值。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:205
Replace the generator seed so subsequent calls can reproduce a sequence.
kitaqgb/lib/rng.c:10
void rng_seed(u16 seed)
{
__rng_seed(seed);
}save_check
执行与 save_exists 相同的完整性检查,但不提取有效载荷。
语法
u8 save_check(u8 slot);参数
slot槽位编号,范围为 0..3。
返回值
有效时返回 1,否则返回 0。长度为零的存档也可能有效。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
使用示例
result[4]=save_check(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:341
Alias save_exists, including its header and checksum validation.
kitaqgb/lib/save.c:175
u8 save_check(u8 slot)
{
return save_exists(slot);
}save_clear
将一个槽位的全部 512 字节清零,使其存档失效。
语法
void save_clear(u8 slot);参数
slot槽位编号,范围为 0..3。
返回值
无返回值。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
忽略 0..3 之外的槽位,不改变其他槽位。
使用示例
save_clear(0);result[16]=save_check(0);result[17]=save_write(3,payload,0);result[18]=save_exists(3);save_clear(3);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:344
Zero the complete selected slot, including header and payload; invalid slot IDs are ignored. Cartridge RAM is disabled when clearing finishes.
kitaqgb/lib/save.c:225
void save_clear(u8 slot)
{
u16 base;
u16 i;
if (slot >= SAVE_SLOT_COUNT) return;
base = save_slot_base(slot);
save_enable();
i = 0;
while (i < SAVE_SLOT_SIZE) {
save_write_abs((u16)(base + i), 0);
i++;
}
save_disable();
}save_exists
验证槽位的头部、长度和校验和,检查是否存在可用存档。
语法
u8 save_exists(u8 slot);参数
slot槽位编号,范围为 0..3。
返回值
有效时返回 1,否则返回 0。长度为零的存档也可能有效。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
使用示例
result[0]=save_exists(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:334
Validate a slot's header, payload bounds and checksum without copying the payload out. A nonzero result means the stored record passes these checks.
kitaqgb/lib/save.c:182
u8 save_exists(u8 slot)
{
u16 base;
u16 stored_len;
u8 stored_sum;
if (slot >= SAVE_SLOT_COUNT) return 0;
base = save_slot_base(slot);
save_enable();
if (save_read_abs(base + 0) != SAVE_MAGIC0) {
save_disable();
return 0;
}
if (save_read_abs(base + 1) != SAVE_MAGIC1) {
save_disable();
return 0;
}
if (save_read_abs(base + 2) != SAVE_VERSION) {
save_disable();
return 0;
}
stored_len = (u16)save_read_abs(base + 3);
stored_len |= (u16)((u16)save_read_abs(base + 4) << 8);
stored_sum = save_read_abs(base + 5);
if (stored_len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) {
save_disable();
return 0;
}
if (save_checksum_ram((u16)(base + SAVE_HEADER_SIZE), stored_len) != stored_sum) {
save_disable();
return 0;
}
save_disable();
return 1;
}save_init
选择 RAM 存储体零,短暂启用后再禁用。
语法
void save_init();返回值
无返回值。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
不会擦除存档数据、检测卡带硬件或格式化槽位。
使用示例
save_init();save_clear(0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:337
Initialize the expected RAM access state and leave cartridge RAM disabled. Existing slot contents are preserved.
kitaqgb/lib/save.c:70
void save_init()
{
save_enable();
save_disable();
}save_load
执行与 save_read 相同的验证后,加载保存的数据。
语法
u8 save_load(u8 slot, void* data, u16 len);参数
slot槽位编号,范围为 0..3。
data含 len 个字节的可写目标空间。
len有效载荷长度,范围为 0..506 字节。读取时要求长度与保存时完全一致。
返回值
加载成功返回 1,否则返回 0;请求长度必须与保存时的长度一致。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
八位累加校验和无法检测所有损坏。断电时写入不具有原子性;仅凭成功返回值,不能保证数据已经可靠地持久保存。
使用示例
readback[1]=0;result[10]=save_load(0,readback,3);result[11]=readback[1];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:339
Alias the checked save_read operation.
kitaqgb/lib/save.c:114
u8 save_load(u8 slot, void* data, u16 len)
{
return save_read(slot, data, len);
}save_read
检查头部、精确长度和校验和;只有全部验证成功后才复制数据。
语法
u8 save_read(u8 slot, void* data, u16 len);参数
slot槽位编号,范围为 0..3。
data含 len 个字节的可写目标空间。
len有效载荷长度,范围为 0..506 字节。读取时要求长度与保存时完全一致。
返回值
成功返回 1,验证失败返回 0;失败时目标内容保持不变。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
八位累加校验和无法检测所有损坏。断电时写入不具有原子性;仅凭成功返回值,不能保证数据已经可靠地持久保存。
使用示例
readback[0]=99;result[5]=save_read(0,readback,2);result[6]=readback[0];result[7]=save_read(0,readback,3);result[8]=readback[0];result[9]=readback[2];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:331
Require matching magic/version, exact requested length and a valid checksum before copying data into the destination. Validation failure leaves the destination unchanged. An invalid slot returns before touching RAM control; validation failures after enabling RAM disable it before returning zero.
kitaqgb/lib/save.c:125
u8 save_read(u8 slot, void* data, u16 len)
{
u8* dst = (u8*)data;
u16 base;
u16 stored_len;
u8 stored_sum;
u16 i;
if (slot >= SAVE_SLOT_COUNT) return 0;
base = save_slot_base(slot);
save_enable();
if (save_read_abs(base + 0) != SAVE_MAGIC0) {
save_disable();
return 0;
}
if (save_read_abs(base + 1) != SAVE_MAGIC1) {
save_disable();
return 0;
}
if (save_read_abs(base + 2) != SAVE_VERSION) {
save_disable();
return 0;
}
stored_len = (u16)save_read_abs(base + 3);
stored_len |= (u16)((u16)save_read_abs(base + 4) << 8);
stored_sum = save_read_abs(base + 5);
if (stored_len != len || stored_len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) {
save_disable();
return 0;
}
if (save_checksum_ram((u16)(base + SAVE_HEADER_SIZE), stored_len) != stored_sum) {
save_disable();
return 0;
}
i = 0;
while (i < stored_len) {
dst[i] = save_read_abs((u16)(base + SAVE_HEADER_SIZE + i));
i++;
}
save_disable();
return 1;
}save_write
将数据、长度和校验和写入一个槽位。
语法
u8 save_write(u8 slot, const void* data, u16 len);参数
slot槽位编号,范围为 0..3。
data含 len 个字节的可读源数据。
len有效载荷长度,范围为 0..506 字节。读取时要求长度与保存时完全一致。
返回值
参数有效时返回 1;槽位或长度无效时返回 0。不执行读回校验。
使用条件与限制
默认有四个 512 字节的槽位,每个含六字节头部及最多 506 字节的有效载荷。示例使用 MBC5 和 8 KiB SRAM。调用结束后 RAM 处于禁用状态,不恢复先前的 RAM 存储体或启用状态。
八位累加校验和无法检测所有损坏。断电时写入不具有原子性;仅凭成功返回值,不能保证数据已经可靠地持久保存。
使用示例
payload[0]=7;payload[1]=42;payload[2]=255;result[1]=save_write(0,payload,3);result[2]=save_write(4,payload,3);result[3]=save_write(0,payload,507);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
保存三个字节 7/42/255,然后测试长度不匹配、无效槽位和校验和损坏。加载成功时,中间字节为 42;读取失败时,目标值保持为 77;清空后验证返回 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k实现与源码
kitaqgb/lib/rpg.h:326
Validate slot and payload length, write payload bytes, then write the header and checksum. Return one after completing the writes; interruption can leave a partially replaced slot, so this is not transactional save storage.
kitaqgb/lib/save.c:81
u8 save_write(u8 slot, const void* data, u16 len)
{
const u8* src = (const u8*)data;
u16 base;
u16 i;
u8 sum = 0;
if (slot >= SAVE_SLOT_COUNT) return 0;
if (len > (u16)(SAVE_SLOT_SIZE - SAVE_HEADER_SIZE)) return 0;
base = save_slot_base(slot);
save_enable();
i = 0;
while (i < len) {
u8 v = src[i];
save_write_abs((u16)(base + SAVE_HEADER_SIZE + i), v);
sum = (u8)(sum + v);
i++;
}
save_write_abs(base + 0, SAVE_MAGIC0);
save_write_abs(base + 1, SAVE_MAGIC1);
save_write_abs(base + 2, SAVE_VERSION);
save_write_abs(base + 3, (u8)(len & 0xFF));
save_write_abs(base + 4, (u8)(len >> 8));
save_write_abs(base + 5, sum);
save_disable();
return 1;
}script_run
选择脚本,并重置等待计数、选项结果和已保存的返回位置。
语法
void script_run(u8 bank, const u8* script);参数
bank指令流所在的 ROM 存储体。
script有效且带结束标记的指令流起始位置;本调用只准备执行,不执行命令。
返回值
无返回值。
使用条件与限制
操作码:00 END;01 TEXT(bank,ptr16);02 SET_FLAG(id16);03 CLEAR_FLAG(id16);04 JUMP(bank,ptr16);05 IF_FLAG(id16,bank,ptr16);06 CHOICE(count,table16);07 WAIT(n);08 CALL_EVENT(bank,ptr16)。字按小端序存放。字符串、指针表和指令必须保持可读,且位于各自映射的存储体内。
使用示例
flag_clear(19);script_run(1,(const u8*)&entry);result[0]=flag_get(19);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从存储体 1 跳转到存储体 2,设置标志 19 并等待。最初两次等待调用返回 1;目标标志变为 1;WAIT 完成后返回 2。该标志证明控制流已经到达另一个存储体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:250
Replace the active script and clear wait, choice and saved-return state.
kitaqgb/lib/script.c:105
void script_run(u8 bank, const u8* script)
{
script_active = 1;
script_bank = bank;
script_pc = script;
script_wait = 0;
script_last_choice = 0xFF;
script_return_active = 0;
}script_step
连续执行命令,直到输出、选择、等待或完成操作让出控制权。
语法
u8 script_step();返回值
0=TEXT/CHOICE 让出控制权;1=等待中;2=已结束或未激活。未知操作码也会以 2 结束。
使用条件与限制
操作码:00 END;01 TEXT(bank,ptr16);02 SET_FLAG(id16);03 CLEAR_FLAG(id16);04 JUMP(bank,ptr16);05 IF_FLAG(id16,bank,ptr16);06 CHOICE(count,table16);07 WAIT(n);08 CALL_EVENT(bank,ptr16)。字按小端序存放。字符串、指针表和指令必须保持可读,且位于各自映射的存储体内。
WAIT n 在解码时返回 1,随后再等待 n 次调用。CALL_EVENT 只有一个返回位置槽。CHOICE 的 count 必须非零,且会在内部阻塞,直到完成选择。不让出控制权的跳转循环会阻塞游戏主循环,因此应确保执行最终到达结束指令或让出控制权的命令。
使用示例
result[3]=script_step();result[4]=flag_get(19);result[5]=script_step();result[6]=script_step();result[7]=script_step();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从存储体 1 跳转到存储体 2,设置标志 19 并等待。最初两次等待调用返回 1;目标标志变为 1;WAIT 完成后返回 2。该标志证明控制流已经到达另一个存储体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:254
Execute commands until an output/choice yields (0), a wait yields (1), or execution ends (2). Control/flag commands continue within the same call, so a script loop without a yielding command can block the game loop indefinitely.
kitaqgb/lib/script.c:125
u8 script_step()
{
if (script_active == 0) return 2;
if (script_wait != 0) {
script_wait--;
return 1;
}
while (script_active != 0) {
u8 op = script_read8();
// An END returns to the one saved caller when present; otherwise it ends the script.
if (op == SCRIPT_OP_END) {
if (script_return_active != 0) {
script_bank = script_return_bank;
script_pc = script_return_pc;
script_return_active = 0;
continue;
}
script_active = 0;
return 2;
}
if (op == SCRIPT_OP_TEXT) {
u8 bank = script_read8();
u16 ptr = script_read16();
text_print_far(bank, (const u8*)ptr);
return 0;
}
if (op == SCRIPT_OP_SET_FLAG) {
u16 id = script_read16();
flag_set(id);
continue;
}
if (op == SCRIPT_OP_CLEAR_FLAG) {
u16 id = script_read16();
flag_clear(id);
continue;
}
if (op == SCRIPT_OP_JUMP) {
// Read all operands in the source bank before installing the destination.
u8 bank = script_read8();
u16 ptr = script_read16();
script_bank = bank;
script_pc = (const u8*)ptr;
continue;
}
// Read all branch operands before switching bank/address so an untaken branch
// still consumes the full instruction.
if (op == SCRIPT_OP_IF_FLAG) {
u16 id = script_read16();
u8 bank = script_read8();
u16 ptr = script_read16();
if (flag_get(id) != 0) {
script_bank = bank;
script_pc = (const u8*)ptr;
}
continue;
}
if (op == SCRIPT_OP_CHOICE) {
u8 count = script_read8();
u16 table = script_read16();
script_last_choice = script_choice(script_bank, (const u8*)table, count);
return 0;
}
if (op == SCRIPT_OP_WAIT) {
script_wait = script_read8();
return 1;
}
// Save the continuation only when the single return slot is empty. Nested
// calls replace the current script but do not push another return address.
if (op == SCRIPT_OP_CALL_EVENT) {
u8 bank = script_read8();
u16 ptr = script_read16();
if (script_return_active == 0) {
script_return_active = 1;
script_return_bank = script_bank;
script_return_pc = script_pc;
}
script_bank = bank;
script_pc = (const u8*)ptr;
continue;
}
// Unknown opcodes terminate with the same return code as END; there is no distinct malformed-stream error.
script_active = 0;
return 2;
}
return 2;
}script_wait_frames
让接下来指定次数的 script_step 调用处于等待状态。
语法
void script_wait_frames(u8 n);参数
n等待的调用次数,范围为 0..255;零表示清除等待。
返回值
无返回值。
使用条件与限制
只有每帧调用一次 script_step 时,这个值才相当于帧数;函数本身不等待 VBlank。
使用示例
script_wait_frames(2);result[1]=script_step();result[2]=script_step();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
从存储体 1 跳转到存储体 2,设置标志 19 并等待。最初两次等待调用返回 1;目标标志变为 1;WAIT 完成后返回 2。该标志证明控制流已经到达另一个存储体。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_script.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_script.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:257
Set the number of subsequent script_step calls to spend waiting. Frame-based timing requires the application to call script_step once per frame.
kitaqgb/lib/script.c:117
void script_wait_frames(u8 n)
{
script_wait = n;
}text_choice
在活动文本区域内绘制垂直选项列表,并等待选择。UP 和 DOWN 循环移动光标;A 确认,B 取消。循环会重绘选项,并在每次等待 VBlank 后轮询一次输入。返回从零开始的索引;取消或列表为空时返回 255。
语法
u8 text_choice(const u8* const* choices, u8 count);参数
choices指向含 count 个可读字符串指针的数组。每个字符串以零结束,内容为原始图块 ID;选项文字不解释会话控制码。在选择结束前,指针表和字符串都须保持映射。
count选项数量。为零时立即返回 255,不读取指针表。非空列表须提供至少 count 行内部空间,且宽度足以容纳一格光标及最长选项文字。不提供裁剪或滚动。
返回值
A 确认时返回 0 至 count−1 的索引。255 表示 B 取消或 count 为零;若需区分,请在调用方处理。函数不会关闭文本区域或清除列表。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
调用前须打开区域并设置尺寸。选择例程使用保存的位置,但不检查区域是否已打开。选项文字从左边缘内侧第二列、顶端下方第一行开始;光标占据文字紧邻左侧的一格。选项打印不使用会话的字符速度设置。
输入循环只响应新按下的键,先处理 UP 再处理 DOWN,先处理 A 再处理 B。按住方向键不会重复移动;需松开再按。由于先前按键掩码初始为零,已按住的键可能在进入时立即生效。同时移动并按 A,会返回移动后的索引,但最后绘制的光标可能仍显示在前一个位置。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
selected=text_choice(dialogue_choices,3);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
按一次 DOWN,再按 A。在截取的运行画面中,向右光标位于图块 (2,4),指向 SHIELD;SELECTED 显示 1。三个选项文字分别从 (3,3)、(3,4) 和 (3,5) 开始。边框、光标和选项文字在 CGB 上为绿色,在 DMG 上为黑色;结果及操作说明保持黑色。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_choice.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_choice.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:231
Redraw choices and block for edge-triggered navigation: UP/DOWN wrap, A accepts, and B returns 0xFF. Zero choices also returns 0xFF. Choice strings use raw tile bytes, and the caller must fit them inside the available rows.
kitaqgb/lib/text.c:316
u8 text_choice(const u8* const* choices, u8 count)
{
u8 cursor;
u8 prev;
if (count == 0) return 0xFF;
cursor = 0;
prev = 0;
while (1) {
u8 i;
for (i = 0; i < count; i++) {
u8 py = (u8)(text_y + 1 + i);
__settile_xy((u8)(text_x + 1), py, (i == cursor) ? TEXT_TILE_CURSOR : TEXT_TILE_FILL);
text_draw_string_at((u8)(text_x + 2), py, choices[i]);
}
__wait_vblank();
{
u16 kt = __readpadex(prev);
u8 trigger;
prev = (u8)kt;
trigger = (u8)(kt >> 8);
if ((trigger & PAD_KEY_UP) != 0) {
if (cursor == 0) cursor = (u8)(count - 1);
else cursor--;
}
if ((trigger & PAD_KEY_DOWN) != 0) {
cursor++;
if (cursor >= count) cursor = 0;
}
if ((trigger & PAD_KEY_A) != 0) return cursor;
if ((trigger & PAD_KEY_B) != 0) return 0xFF;
}
}
}text_clear_rect
用 TEXT_TILE_FILL 填充背景图块地图的矩形区域。可擦除标签、清理数值字段或面板的一部分。不移动活动文本光标、不关闭文本区域,也不改变尺寸。
语法
void text_clear_rect(u8 x, u8 y, u8 w, u8 h);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
w宽度,以图块格子为单位。text_window 包含两列边框,内部列数为 max(w−2, 0);text_clear_rect 表示每行填充格数。
h高度,以图块格子为单位。text_window 包含上下边框,内部行数为 max(h−2, 0);text_clear_rect 表示填充行数。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
公开数值函数只占用新数值所需宽度,不擦除右侧残留格。在同一位置用 7 覆盖 12345,会显示 72345。若只想显示 7,请先用 text_clear_rect 清除预留的五格字段。有符号 16 位字段应预留六格。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_clear_rect(7,5,5,1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
边框内第 5 行最初包含 KEEP ERASE KEEP。清除 (7,5) 起的五格会移除 ERASE,而两个 KEEP 和边框保持。CLEAN 行也先清除五格数值字段,再写 7。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:237
Fill the supplied tile rectangle without changing the active text window or cursor.
kitaqgb/lib/text.c:220
void text_clear_rect(u8 x, u8 y, u8 w, u8 h)
{
__settile_rect(x, y, w, h, TEXT_TILE_FILL);
}text_close
用 TEXT_TILE_FILL 擦除活动文本区域及边框,并将其标记为关闭。已关闭时再次调用不产生效果。不会恢复打开区域之前的背景图块。
语法
void text_close();返回值
无返回值。所有绘制及阻塞等待均在函数返回之前完成。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
区域已关闭或没有可用内部空间时,普通字符被忽略。但控制字节仍会解释:WAIT 仍可能阻塞,翻页控制仍可能等待或清除已保存的区域。因此,在关闭的区域上调用 text_print,并不等于完全不执行操作。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
text_close();
text_print("HIDDEN"); // With the area closed, ordinary characters are not drawn.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在 (1,9) 打开 18×3 格区域,写入 ERASE ME,随后关闭并尝试写入 HIDDEN。在 CLOSED AREA 下方,x=1–18、y=9–11 的格子应为空白:文字和边框都不应残留。上方蓝色会话仍保持可见。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:220
Erase the open window with the fill tile and disable windowed character output.
kitaqgb/lib/text.c:200
void text_close()
{
if (text_opened == 0) return;
__settile_rect(text_x, text_y, text_w, text_h, TEXT_TILE_FILL);
text_opened = 0;
}text_open
在背景地图上打开软件文本区域。保存位置和尺寸,将字符光标重置到内部首格,绘制边框并清空内部。打开另一个区域会改变活动光标和区域,但不会擦除地图其他位置已绘制的区域。
语法
void text_open(u8 x, u8 y, u8 w, u8 h);参数
x区域最左列,以 8×8 背景地图格子为单位。整个宽度须位于列 0–31 内。
y区域最上行,以 8×8 背景地图格子为单位。整个高度须位于行 0–31 内。
w总宽度,以格子为单位,包含左右边框。至少需要三格,才能容纳一列内部字符。
h总高度,以格子为单位,包含上下边框。至少需要三格,才能容纳一行内部字符。
返回值
无返回值。所有绘制及阻塞等待均在函数返回之前完成。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
text_open(1,2,18,5);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
DIALOGUE FLOW 在 (1,2) 打开 18×5 格边框,内部为 16×3 格。NEAR、READY 和 BANK 2 分别占据三行。可见边框线从像素 x=11 延伸到 147,从 y=19 延伸到 51。边框和文字在 CGB 上为蓝色,在 DMG 上为黑色。之后打开擦除测试区域时,该边框仍然可见。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:218
Set the tile-space window, reset its cursor and redraw it immediately. Opening does not reset the existing character speed or load font tiles.
kitaqgb/lib/text.c:187
void text_open(u8 x, u8 y, u8 w, u8 h)
{
text_x = x;
text_y = y;
text_w = w;
text_h = h;
text_opened = 1;
text_reset_cursor();
text_draw_frame();
text_clear_inside();
}text_print
通过普通指针读取控制流,在活动文本窗口光标处写入字符图块。函数解释换行、定时等待和翻页等待,并按内部宽度自动换行。只有到达 END 才会返回,因此翻页等待可能一直阻塞,直到用户按键。
语法
void text_print(const u8* str);参数
str指向以 TEXT_CTRL_END (0) 结束的可读流。阻塞等待期间,整个流都须保持映射,包括 WAIT 后面的操作数。不提供缓冲区长度或有效性检查。
返回值
无返回值。所有绘制及阻塞等待均在函数返回之前完成。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
控制字节:0x00 结束流;0x0A 移至内部新的一行;0x01 将下一个字节作为 VBlank 等待次数,允许为零;0x02 等待翻页,然后清空内部并重置光标。其他字节均为图块 ID。WAIT 的零操作数不是 END 标记。
换行或自动折行使光标超出内部最后一行时,会显示翻页等待标记,等待 A、B 或 START,然后清空内部并从首格继续。此操作替换整页,而不是逐行滚动。显式 0x02 会等待,但不添加标记。翻页轮询开始时没有先前按键记录,因此进入等待时已经按住的键也可能推进翻页。
区域已关闭或没有可用内部空间时,普通字符被忽略。但控制字节仍会解释:WAIT 仍可能阻塞,翻页控制仍可能等待或清除已保存的区域。因此,在关闭的区域上调用 text_print,并不等于完全不执行操作。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
text_print(dialogue_near);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
普通流依次写入 NEAR、换行、READY 和十二次等待命令,然后到达 END。NEAR 从图块 (2,3) 开始,READY 从 (2,4) 开始。后续跨存储体流从同一光标继续,因此其开头的换行会将 BANK 2 放到内部第三行。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:224
Decode an END-terminated stream of tile bytes and newline/wait/page controls. WAIT consumes a following frame-count byte. The source must remain readable through blocking waits; the routine does not validate stream length.
kitaqgb/lib/text.c:261
void text_print(const u8* str)
{
while (*str != TEXT_CTRL_END) {
u8 c = *str++;
if (c == TEXT_CTRL_NEWLINE) {
text_newline();
continue;
}
if (c == TEXT_CTRL_WAIT) {
u8 frames = *str++;
text_delay_frames(frames);
continue;
}
if (c == TEXT_CTRL_PAGE_WAIT) {
text_wait_advance();
text_clear_inside();
text_reset_cursor();
continue;
}
text_put_tile(c);
}
}text_print_far
解释相同的会话控制流,但通过 __farpeek8 从指定 ROM 存储体逐字节读取。适用于存放在当前映射源存储体之外的会话。字符位置、速度、换行和翻页控制与 text_print 相同。
语法
void text_print_far(u8 bank, const u8* str);参数
bank无符号 8 位源 ROM 存储体编号。示例将流存放在存储体 2,并在打印后检查调用方仍能读取存储体 1 中的标记。
str流在指定存储体中的 CPU 地址。地址会递增,但存储体编号保持不变;越过可切换 ROM 窗口后,不会自动转到下一个存储体。
返回值
无返回值。所有绘制及阻塞等待均在函数返回之前完成。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
控制字节:0x00 结束流;0x0A 移至内部新的一行;0x01 将下一个字节作为 VBlank 等待次数,允许为零;0x02 等待翻页,然后清空内部并重置光标。其他字节均为图块 ID。WAIT 的零操作数不是 END 标记。
换行或自动折行使光标超出内部最后一行时,会显示翻页等待标记,等待 A、B 或 START,然后清空内部并从首格继续。此操作替换整页,而不是逐行滚动。显式 0x02 会等待,但不添加标记。翻页轮询开始时没有先前按键记录,因此进入等待时已经按住的键也可能推进翻页。
区域已关闭或没有可用内部空间时,普通字符被忽略。但控制字节仍会解释:WAIT 仍可能阻塞,翻页控制仍可能等待或清除已保存的区域。因此,在关闭的区域上调用 text_print,并不等于完全不执行操作。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
text_print_far(2,dialogue_far);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例从 ROM 存储体 2 读取换行和 BANK 2。之后读取调用方存储体 1 中的标记,使 BANK RESTORED 显示 161。可见的 BANK 2 行从图块 (2,5) 开始;恢复检查数值显示在 (16,14)。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:227
Decode the same text controls while fetching each byte from the specified ROM bank. The pointer advances within that bank; it does not roll into the next bank.
kitaqgb/lib/text.c:286
void text_print_far(u8 bank, const u8* str)
{
const u8* p = str;
while (1) {
u8 c = __farpeek8(bank, p);
p++;
if (c == TEXT_CTRL_END) return;
if (c == TEXT_CTRL_NEWLINE) {
text_newline();
continue;
}
if (c == TEXT_CTRL_WAIT) {
u8 frames = __farpeek8(bank, p);
p++;
text_delay_frames(frames);
continue;
}
if (c == TEXT_CTRL_PAGE_WAIT) {
text_wait_advance();
text_clear_inside();
text_reset_cursor();
continue;
}
text_put_tile(c);
}
}text_print_s16
绘制有符号 16 位十进制值。负值先显示减号图块,再显示绝对值;零和正值无符号。最长输出 -32768 占六格。
语法
void text_print_s16(u8 x, u8 y, s16 value);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
value有符号值,范围为 −32768 至 32767。示例将 16 位模式 32768 (0x8000) 转为 s16,以显示 −32768。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
公开数值函数只占用新数值所需宽度,不擦除右侧残留格。在同一位置用 7 覆盖 12345,会显示 72345。若只想显示 7,请先用 text_clear_rect 清除预留的五格字段。有符号 16 位字段应预留六格。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_print_s16(11,12,(s16)32768);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
S16 行从 (11,12) 起显示 -32768,含减号共六格。CGB 上为绿色。此例测试有符号 16 位最小值,其正的绝对值无法放入 s16。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:247
Print a minus tile for negative values, followed by the unsigned magnitude. Positive values use the unsigned decimal path; no plus sign is emitted.
kitaqgb/lib/text.c:246
void text_print_s16(u8 x, u8 y, s16 value)
{
u16 magnitude;
if (value < 0) {
__settile_xy(x, y, (u8)'-');
magnitude = (u16)((s16)0 - value);
text_print_u16_width((u8)(x + 1), y, magnitude, text_dec_digits_u16(magnitude));
return;
}
text_print_u16(x, y, (u16)value);
}text_print_u16
从指定图块位置起,以十进制绘制无符号 16 位值。使用一至五格,无前导零。适用于可能超过 255 的分数或物品数量。
语法
void text_print_u16(u8 x, u8 y, u16 value);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
value无符号值,范围为 0 至 65535。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
公开数值函数只占用新数值所需宽度,不擦除右侧残留格。在同一位置用 7 覆盖 12345,会显示 72345。若只想显示 7,请先用 text_clear_rect 清除预留的五格字段。有符号 16 位字段应预留六格。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_print_u16(11,10,65535);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
U16 行在 (11,10) 显示 65535,使用五格。CGB 上为蓝色。OVERWRITE 未先清除五格字段,用 7 覆盖 12345 后显示 72345。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:244
Print an unsigned word in decimal using only the required number of cells.
kitaqgb/lib/text.c:239
void text_print_u16(u8 x, u8 y, u16 value)
{
text_print_u16_width(x, y, value, text_dec_digits_u16(value));
}text_print_u8
从指定图块位置起,以十进制绘制无符号 8 位值。占一至三格,无前导零;零占一格。适用于生命数或弹药等小计数器。
语法
void text_print_u8(u8 x, u8 y, u8 value);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
value无符号值,范围为 0 至 255。更宽的表达式会通过函数参数转换为 u8。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
公开数值函数只占用新数值所需宽度,不擦除右侧残留格。在同一位置用 7 覆盖 12345,会显示 72345。若只想显示 7,请先用 text_clear_rect 清除预留的五格字段。有符号 16 位字段应预留六格。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_print_u8(11,8,255);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
U8 行从 (11,8) 起显示 255,恰好使用三格。CGB 上为蓝色。OVERWRITE 和 CLEAN 两行展示为何较短替换值需要显式清理字段。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:242
Print an unsigned byte in decimal using only the required number of cells.
kitaqgb/lib/text.c:233
void text_print_u8(u8 x, u8 y, u8 value)
{
text_print_u16_width(x, y, (u16)value, text_dec_digits_u16((u16)value));
}text_print_xy
从绝对背景地图位置起,水平绘制以零结束的图块编号序列。不执行窗口裁剪、自动换行、文本控制码解释或逐字符延迟。不移动 text_print 使用的光标。
语法
void text_print_xy(u8 x, u8 y, const u8* str);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
str指向以 0 结束的可读图块字节。各字节直接作为图块编号。只有对应字形位于匹配图块 ID 时,ASCII 字符串才能显示为文字。调用返回前,源须保持映射和可读。这些函数不解码 UTF-8,也不加载字形。多字节文字须先提供游戏定义的编码及匹配图块数据,才能显示。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_print_xy(2,3,"HELLO!");这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
HELLO! 从图块 (2,3)、像素 (16,24) 开始,位于 CGB 红框内。这是背景地图绝对位置;打开文本区域不会给 x 或 y 添加偏移。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:240
Draw a zero-terminated tile string at absolute tile coordinates, without window clipping, control codes or the windowed printing delay.
kitaqgb/lib/text.c:227
void text_print_xy(u8 x, u8 y, const u8* str)
{
text_draw_string_at(x, y, str);
}text_set_speed
设置 text_print 或 text_print_far 写入每个普通字符后等待 VBlank 的次数。零表示不进行逐字符等待。打开或关闭区域不会重置此设置。即使字符速度为零,显式 WAIT 命令仍会等待。
语法
void text_set_speed(u8 speed);参数
speed每个显示字符之后等待 VBlank 的次数,范围为 0–255。这是等待次数,不是毫秒数。不影响 text_print_xy、数值辅助函数或选项列表。
返回值
无返回值。所有绘制及阻塞等待均在函数返回之前完成。
使用条件与限制
请使用有效的背景地图矩形,并先加载字体和边框图块。软件文本区域使用 LCDC 第 3 位选择的背景地图,不会启用硬件 Window 层。CGB 上用 VBK 0 写入图块编号,颜色属性另行设置。示例提供按 ASCII 映射的字体,以及边框、光标和翻页提示图块。会话与选项文字使用字节图块 ID,不进行 UTF-8 解码。要显示所提供字体以外的字符,请提供字形图块并按其 ID 编码文本;会话控制字节的取值须保留。
这些是阻塞式辅助函数:等待期间不会调用游戏更新、音乐服务或渲染循环。LCD 关闭时,__wait_vblank 立即返回,因此字符速度和 WAIT 不会产生帧延迟。定时会话和按帧轮询输入要求 LCD 保持启用。若会话期间其他任务也必须运行,请在游戏中使用状态机。
底层安全图块写入在 LCD 开启时等待可写模式,并在写入前后使用 DI/EI。它们可能启用中断,而不是保留原先禁用中断的状态。不要从中断处理程序调用这些阻塞辅助函数,也不要依赖它们保留该状态。
每个完整示例只包含一次 text.c。构建时添加库和手册示例的包含目录;不要再把 lib/text.c 作为另一个源文件输入。跨存储体流示例使用 MBC5 和 64 KiB ROM,以确保存储体 2 的数据存在。
使用示例
text_set_speed(2);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
流程示例对普通会话和跨存储体会话使用速度 2,并包含操作数为 12 的 WAIT。完成后的图像显示等待结束后的文字;另外的逐帧跟踪检查速度 3、显式五次等待命令,以及区域关闭时的四次等待命令。不会仅凭最终截图验证时序。
模拟器检查会比较完整背景地图内容和四张示例图像。其他运行覆盖打开/关闭状态、普通及跨存储体控制流、定时等待、显式及自动翻页等待、A/B/START 翻页、持续按键、选项循环、空列表和同时输入的优先级。脚本输入前后的检查点用于验证翻页等待确实会阻塞。尚未测试实体硬件和中断并发。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\dialogue_flow.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\dialogue_flow.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:233
Set VBlank waits per windowed character; zero prints without character delays.
kitaqgb/lib/text.c:208
void text_set_speed(u8 speed)
{
text_speed = speed;
}text_window
在背景图块地图绘制带框文本区域,并将其设为窗口打印的活动区域。这是 text_open 的别名:将窗口光标重置到内部首格,重绘边框并清空内部。保留当前字符延迟。
语法
void text_window(u8 x, u8 y, u8 w, u8 h);参数
x起始列,以 8×8 背景地图格子为单位。完整输出须位于列 0–31 内。不滚动背景时,可见列为 0–19。
y起始行,以 8×8 背景地图格子为单位,范围为 0–31。不滚动背景时,可见行为 0–17。矩形高度也须在范围内。
w宽度,以图块格子为单位。text_window 包含两列边框,内部列数为 max(w−2, 0);text_clear_rect 表示每行填充格数。
h高度,以图块格子为单位。text_window 包含上下边框,内部行数为 max(h−2, 0);text_clear_rect 表示填充行数。
返回值
无返回值。函数在返回前写入图块地图格子;不返回字符数或状态码。
使用条件与限制
这些函数绘制 LCDC 第 3 位选择的背景地图。文本窗口是该地图上的软件文本区域,不是硬件 Window 层。CGB 写图块编号时保持 VBK 为 0;这些调用不设置颜色属性、调色板或滚动。
绘制前加载字体和边框图形。默认边框 ID 为 1–6,填充图块为 0,十进制数字从 ASCII 0(图块 48)开始。减号用 ASCII -(图块 45)。示例提供六个边框图块,以及字形 ID 与 ASCII 对应的手册字体。
矩形和文字须位于 32×32 地图内。矩形填充不会按地图边界裁剪宽高。宽度或高度为零时不填充;text_window 仍将该区域记录为打开,但没有可用内部空间。
图块写入内建函数检查 LCD 状态:关闭时立即写;开启时等待可写模式,并在写入前后使用 DI/EI。这条路径返回时可能启用中断,因此不要假定保留原有禁用状态。本节示例和字节级测试均在 LCD 关闭时写入。
完整示例只包含一次 text.c,使库实现与程序一起编译。不要再将 lib/text.c 作为独立输入。若自定义 TEXT_* 图块常量,请在包含 rpg.h 和实现前定义;编译该库的所有位置都须使用相同定义。
使用示例
text_window(1,2,18,5);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在 (1,2) 打开 18×5 格区域,内部为 16×3 格。提供的边框线宽一像素,外部可见线从 x=11 至 147、y=19 至 51。CGB 上为红色,DMG 上为黑色。
验证比较两个示例画面的每个可见像素及全部 1024 背景地图字节。独立 DMG/CGB ROM 检查全部 256 种无符号字节值、十进制位数边界和有符号极值,并保留数值输出旁的点作为保护标记。布局检查覆盖空矩形、窄矩形、地图边缘放置、取值等于控制码的原始图块字节,以及绝对写入后的窗口光标。这些是模拟器测试,不是实体硬件测试。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\text_layout.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\text_layout.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:235
Open and redraw a window using the same tile-space arguments as text_open.
kitaqgb/lib/text.c:214
void text_window(u8 x, u8 y, u8 w, u8 h)
{
text_open(x, y, w, h);
}unit_attack_range
标出曼哈顿距离位于 atk_min 与 atk_max 之间的格子,包含两个端点。
语法
u8 unit_attack_range(unit_t* u, u8* out_mask);参数
u有效的 unit_t:包含格子位置、以步数表示的 move、含端点的 atk_min/atk_max 攻击范围,以及用于判断能否行动的 hp 和 acted。
out_maskwidth*height个可写字节;每格输出零或一。
返回值
地图可用时返回 1;尺寸为零时返回 0。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
忽略障碍物、视线和友方单位。计算的距离应不超过 255。若最小射程大于最大射程,所有格子均为零。
使用示例
result[46]=unit_attack_range(&hero,mask);result[47]=mask[12];result[48]=mask[14];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:352
Mark cells whose Manhattan distance falls within the inclusive attack range. This geometric mask does not test obstacles, line of sight or other units.
kitaqgb/lib/slg_unit.c:38
u8 unit_attack_range(unit_t* u, u8* out_mask)
{
u8 w = map_current_width();
u8 h = map_current_height();
u8 y;
if (w == 0 || h == 0) return 0;
__memset(out_mask, 0, (u16)((u16)w * (u16)h));
y = 0;
while (y < h) {
u8 x = 0;
while (x < w) {
u8 dist = __manhattan(u->x, u->y, x, y);
if (dist >= u->atk_min && dist <= u->atk_max) {
out_mask[__map_index(x, y, w)] = 1;
}
x++;
}
y++;
}
return 1;
}unit_can_act
检查单位是否还有 HP 且尚未行动。
语法
u8 unit_can_act(const unit_t* u);参数
u有效的 unit_t:包含格子位置、以步数表示的 move、含端点的 atk_min/atk_max 攻击范围,以及用于判断能否行动的 hp 和 acted。
返回值
当 hp 非零且 acted 为零时返回 1,否则返回 0。不修改单位。
使用示例
result[11]=unit_can_act(&hero);hero.acted=1;result[12]=unit_can_act(&hero);hero.acted=0;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:355
A unit can act only while HP is nonzero and its acted flag is clear. The unit pointer must refer to a valid record.
kitaqgb/lib/slg_unit.c:5
u8 unit_can_act(const unit_t* u)
{
if (u->hp == 0) return 0;
if (u->acted != 0) return 0;
return 1;
}unit_move_range
生成移动范围掩码,标出单位在允许步数内能够到达的格子。
语法
u8 unit_move_range(unit_t* u, u8* out_mask);参数
u有效的 unit_t:包含格子位置、以步数表示的 move、含端点的 atk_min/atk_max 攻击范围,以及用于判断能否行动的 hp 和 acted。
out_maskwidth*height个可写字节;每格输出零或一。
返回值
地图可用时返回 1;尺寸为零时返回 0。单位的起点必须位于地图内。
使用条件与限制
坐标指向当前地图中的格子:普通图块为 8×8 像素,元图块为 16×16 像素。
搜索使用约 4 KiB 的共享工作区,不可重入。地图最多为 1024 格,探索距离最多为 254。距离 0xFF 表示尚未到达,因此无法表示 255 步的路径。代价和掩码输出各需 width*height 个可写字节。
起点即使被阻挡,也会被标记为可到达。HP、acted 和其他单位不影响移动范围掩码。
使用示例
result[43]=unit_move_range(&hero,mask);result[44]=mask[12];result[45]=mask[14];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
左侧网格显示三步以内的移动代价;右侧显示从 S 到 G 的最短路径。绿色星号沿九步路线绕过红色的三格墙壁;蓝色数字表示代价。DMG 以相同布局显示黑色图形。RAM 检查还展示攻击范围掩码会忽略墙壁。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_map_path.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_map_path.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none实现与源码
kitaqgb/lib/rpg.h:349
Build movement costs, then convert reachable cells to one and unreachable 0xFF cells to zero. The caller provides one output byte per current-map cell and a valid unit origin; path-search workspace limits also apply.
kitaqgb/lib/slg_unit.c:15
u8 unit_move_range(unit_t* u, u8* out_mask)
{
u8 w = map_current_width();
u8 h = map_current_height();
u16 total;
u16 i;
if (w == 0 || h == 0) return 0;
// A valid origin is required: the fill leaves storage unchanged on invalid origins, but this loop still converts it.
range_fill_move(u->x, u->y, u->move, out_mask);
total = (u16)((u16)w * (u16)h);
i = 0;
while (i < total) {
out_mask[i] = (u8)((out_mask[i] == 0xFF) ? 0 : 1);
i++;
}
return 1;
}weighted_choice
从相对字节权重列表选择项目索引。先求权重总和,将一次 rng16 取值对总和取模,再查找包含结果的首个累积区间。权重 [1, 3, 6] 中,索引 0、1、2 分别占据区间 0、1–3、4–9。
语法
u8 weighted_choice(const u8* weights, u8 count);参数
weights指向 count 个可读 u8 权重的指针。数组不会修改。总和为正时跳过零权重项目。count == 0 时不读取指针,可为 null;否则须提供有效数组。
count权重数量,范围为 0–255。最多 255 个值为 255 的权重总和为 65025,可放入 u16 累加器。函数先扫描列表求和,再扫描到所选区间。
返回值
总和为正时,消耗两次字节取值后返回 0 至 count−1 的索引。count 为零或全部权重为零时,不取值,直接返回零。此回退结果无法与有效选择索引 0 区分;若需要区分,请自行检查空列表或全零列表。
使用条件与限制
权重定义相对区间大小,不保证实际观测百分比。rng16 的输出分布及对总和取模都会影响实际频率。总和为正时始终消耗两次字节取值,即使只有一个项目权重非零。
这些调用共享持久状态。复现要求相同目标、种子和调用顺序,包括其他辅助函数发起的调用。应在同一执行上下文中设置种子和取值;这些检查不证明可从中断处理程序安全并发使用。
GB 和 FC 使用不同算法,不保证相同种子产生相同序列。它们是确定性的游戏用生成器,不是密码学随机源;测试检查实现行为,不评估统计质量或周期。
使用库函数时,包含 rpg.h,并将 lib/rng.c 与游戏一起编译。__rng_seed 和 __rng8 内建函数由编译器生成;只使用这两个函数的程序无需 rng.c 目标代码。
使用示例
weights[0]=1;weights[1]=3;weights[2]=6;
rng_seed(0x1234);
chosen=weighted_choice(weights,3); // 546 % 10 = 6 selects index 2.
empty=weighted_choice((const u8*)0,0); // No items: 0 is a fallback, not a chosen item.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例为三个项目分配权重 1、3、6。种子对应的字值 546 取模得 6,位于索引 2 的区间 4–9,因此 WEIGHT INDEX 显示 00002。EMPTY LIST 显示 00000 是空列表回退值,不是成功选择项目 0。
完整程序在白底上打印十进制结果:DMG 为黑字,CGB 为蓝字。FAILED 必须为 00000。图像展示该示例的实际模拟器输出。
验证将每个案例的 32 个输出及随后八次字节取值与独立宿主模型比较:GB 93 个案例、FC 18 个案例,覆盖常量/运行时种子及 GB 范围、概率和权重边界。教学画面在模拟器运行 240 帧后检查。未测试实体硬件或统计质量。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_choices.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_choices.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/rpg.h:203
Select an index from cumulative byte weights using a 16-bit random draw. A zero total, including an empty list, returns zero; callers must distinguish that case from a valid index. Modulo reduction can introduce a small bias.
kitaqgb/lib/rng.c:61
u8 weighted_choice(const u8* weights, u8 count)
{
u8 i;
u16 total;
u16 pick;
u16 acc;
// At most 255 byte weights sum to 65025, so this accumulator fits u16.
// A nonempty list requires count readable weight bytes.
total = 0;
i = 0;
while (i < count) {
total = (u16)(total + (u16)weights[i]);
i++;
}
if (total == 0) return 0;
pick = (u16)(rng16() % total);
acc = 0;
i = 0;
while (i < count) {
acc = (u16)(acc + (u16)weights[i]);
if (pick < acc) return i;
i++;
}
return (u8)(count - 1);
}scene.h — 场景注册、切换与更新
分派场景表的 enter、update、draw、exit 回调,用于标题、游戏等画面之间切换。
scene_change
若存在当前场景则退出它,再进入指定场景。
语法
void scene_change(u8 scene_id);参数
scene_id要进入的场景索引,0~count−1。
返回值
无返回值。
使用条件与限制
忽略越界 ID。有效变更依次执行 exit、保存新 ID 与变更标志、执行 enter。选择同一 ID 也会执行 exit 和 enter。调用方须保持场景表及函数目标有效,跳过空回调;不要从 enter/exit 递归切换或替换表。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
scene_change(0);scene_change(9);result[11]=events;result[12]=scene_was_changed();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
程序激活 0,忽略无效 ID 9,再切换 0→1、重新进入 1→1,并检查回调顺序。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:20
Ignore invalid IDs; otherwise exit the current scene and enter the requested one. Selecting the current ID still performs exit/enter. Callbacks run synchronously.
kitaqgb/lib/scene.c:27
void scene_change(u8 scene_id)
{
SceneFunc fn;
if (scene_id >= scene_count) return;
// Exit/enter handlers must not recursively change scenes or replace this shared table.
if (scene_has_current != 0) {
fn = scene_table[(__safe_index u8)scene_current].exit;
if (fn != 0) fn();
}
scene_current = scene_id;
scene_has_current = 1;
scene_changed = 1;
fn = scene_table[(__safe_index u8)scene_current].enter;
if (fn != 0) fn();
}scene_draw
调用一次活动场景的 draw 回调。
语法
void scene_draw();返回值
无返回值。
使用条件与限制
没有活动场景或 draw 为空时不处理,不清变更标志。显示传输及时序由回调或外围主循环安排。调用方须保持表和函数目标有效,空回调跳过,不要在 enter/exit 中递归切换或替换表。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
scene_draw();result[15]=events;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
事件序列包含场景 0 的 draw 产生的 12,以及场景 1 的 draw 产生的 22。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:25
Call the active scene's draw handler if one exists; no current scene is a no-op.
kitaqgb/lib/scene.c:60
void scene_draw()
{
SceneFunc fn;
if (scene_has_current == 0) return;
fn = scene_table[(__safe_index u8)scene_current].draw;
if (fn != 0) fn();
}scene_get_current
读取保存的当前场景 ID。
语法
u8 scene_get_current();返回值
返回保存的八位 ID,初始化也会将其设为零。
使用条件与限制
仅返回零不能证明场景 0 已活动。须跟踪初始化后是否已执行首次 scene_change()。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
result[16]=scene_get_current();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例激活场景 0 后读到零,切换到场景 1 后读到一。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:27
Return the stored scene ID; zero is also returned before the first transition.
kitaqgb/lib/scene.c:70
u8 scene_get_current()
{
return scene_current;
}scene_init
绑定场景表,初始没有活动场景。
语法
void scene_init(const SceneDef* scenes, u8 count);参数
scenesSceneDef数组,各项包含 enter、update、draw、exit 回调。count条目数,零表示没有有效场景 ID。
返回值
无返回值。
使用条件与限制
将保存的 ID 和变更标志清零,不调用 enter;用 scene_change() 激活场景。调用方持有表,使用期间表及普通函数指针目标须有效。跳过空回调,不要在 enter/exit 中递归切换场景或替换表。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
scene_init(scenes,2);scene_update();scene_draw();result[9]=events;result[10]=scene_was_changed();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例注册两个场景,确认激活前调用 update/draw 不产生事件。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:15
Attach the caller-owned scene table and clear current-scene state without invoking callbacks.
kitaqgb/lib/scene.c:10
void scene_init(const SceneDef* scenes, u8 count)
{
scene_table = scenes;
scene_count = count;
scene_current = 0;
scene_changed = 0;
scene_has_current = 0;
}scene_set_table
替换场景表,并使所有场景处于未激活状态。
语法
void scene_set_table(const SceneDef* scenes, u8 count);参数
scenesSceneDef数组,各项包含 enter、update、draw、exit 回调。count条目数,零表示没有有效场景 ID。
返回值
无返回值。
使用条件与限制
像 scene_init() 一样重置状态,既不调用旧 exit,也不调用新 enter。调用方持有表,使用期间表和普通函数指针目标须有效。跳过空回调,不要从 enter/exit 递归切换场景或替换表。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
scene_set_table(scenes,2);result[23]=events;result[24]=scene_was_changed();scene_draw();result[25]=events;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
发生九个事件后替换表,事件数仍为九、变更标志清零,后续 draw 不执行操作。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:17
Replace the table by resetting scene state; this does not call the old scene's exit handler.
kitaqgb/lib/scene.c:20
void scene_set_table(const SceneDef* scenes, u8 count)
{
scene_init(scenes, count);
}scene_update
调用一次活动场景的 update 回调。
语法
void scene_update();返回值
无返回值。
使用条件与限制
没有活动场景则不处理。有活动场景时先清变更标志再调用,因此 update 内发生的切换会保留标志。调用方须保持表和函数目标有效,空回调跳过,不要在 enter/exit 中递归切换场景或替换表。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
scene_update();result[13]=events;result[14]=scene_was_changed();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
场景 0 的 update 记录事件 11,原为一的变更标志在此次普通更新后归零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:23
Clear the change flag before calling the active scene's update handler, so a transition made during that handler is visible afterward.
kitaqgb/lib/scene.c:49
void scene_update()
{
SceneFunc fn;
if (scene_has_current == 0) return;
scene_changed = 0;
fn = scene_table[(__safe_index u8)scene_current].update;
if (fn != 0) fn();
}scene_was_changed
读取当前是否记录了场景切换。
语法
u8 scene_was_changed();返回值
变更标志已设置返回 1,否则返回 0。
使用条件与限制
读取不清标志。初始化、替换表和活动场景 update 开始时清零,无效场景 ID 不改变标志。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
result[17]=scene_was_changed();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
检查切换后立即为一、普通更新后为零、替换表后为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scene.h:29
Read the transition flag, which is cleared at the start of an active scene update.
kitaqgb/lib/scene.c:76
u8 scene_was_changed()
{
return scene_changed;
}scroll.h — 背景、窗口与分屏滚动
控制背景与窗口的立即滚动、缓冲滚动及最多八个扫描行分段事件。请区分读取函数维护的当前值、实时硬件寄存器值和待提交值。
Scroll_BgAdd
将有符号位移加到库记录的背景坐标上,并立即应用。
语法
void Scroll_BgAdd(s8 dx, s8 dy);参数
dx有符号水平位移,单位为像素,范围为 −128 至 127。
dy有符号垂直位移,单位为像素,范围为 −128 至 127。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。结果按模 256 回绕。增量操作以库记录的当前值为起点,而不是待提交值或光栅处理临时写入寄存器的值。本函数封装了 __scroll_bg_add。
使用示例
Scroll_BgAdd(-20,3);result[22]=M_SCX;result[23]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:73
Apply signed deltas to the byte-sized background-scroll coordinates.
kitaqgb/lib/scroll.c:41
void Scroll_BgAdd(s8 dx, s8 dy) { __scroll_bg_add(dx, dy); }Scroll_Flush
将带脏标记的背景或窗口坐标应用到库记录的状态和硬件,然后清除待提交标记。
语法
void Scroll_Flush();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不处理没有脏标记的坐标对。不会屏蔽中断,也不等待 VBlank;请由调用方安排所需的执行时机。本函数封装了 __scroll_flush。
使用示例
Scroll_Flush();result[18]=M_SCX;result[19]=M_SCY;result[20]=M_WX;result[21]=M_WY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:70
Apply the buffered scroll/window state through the compiler runtime.
kitaqgb/lib/scroll.c:38
void Scroll_Flush() { __scroll_flush(); }Scroll_GetBgX
读取库记录的当前背景水平滚动量。
语法
u8 Scroll_GetBgX();返回值
返回库记录的当前字节值,范围为 0–255。WX 包含硬件所需的七像素偏移。
使用条件与限制
这不是直接读取硬件寄存器。返回值不包含待提交的缓冲值、直接写入寄存器的值,也不包含分段扫描处理中临时应用的值。本函数封装了 __scroll_bg_x_get。
使用示例
result[6]=Scroll_GetBgX();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:50
Read the tracked current background X; this is not a direct SCX register read.
kitaqgb/lib/scroll.c:18
u8 Scroll_GetBgX() { return __scroll_bg_x_get(); }Scroll_GetBgY
读取库记录的当前背景垂直滚动量。
语法
u8 Scroll_GetBgY();返回值
返回库记录的当前字节值,范围为 0–255。WX 包含硬件所需的七像素偏移。
使用条件与限制
这不是直接读取硬件寄存器。返回值不包含待提交的缓冲值、直接写入寄存器的值,也不包含分段扫描处理中临时应用的值。本函数封装了 __scroll_bg_y_get。
使用示例
result[7]=Scroll_GetBgY();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:52
Read the tracked current background Y; pending and raster-only values are excluded.
kitaqgb/lib/scroll.c:20
u8 Scroll_GetBgY() { return __scroll_bg_y_get(); }Scroll_GetWindowX
读取库记录的当前窗口 WX 值。
语法
u8 Scroll_GetWindowX();返回值
返回库记录的当前字节值,范围为 0–255。WX 包含硬件所需的七像素偏移。
使用条件与限制
这不是直接读取硬件寄存器。返回值不包含待提交的缓冲值、直接写入寄存器的值,也不包含分段扫描处理中临时应用的值。本函数封装了 __scroll_win_x_get。
使用示例
result[12]=Scroll_GetWindowX();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:61
Read tracked current WX, including its seven-pixel offset; not a live hardware read.
kitaqgb/lib/scroll.c:29
u8 Scroll_GetWindowX() { return __scroll_win_x_get(); }Scroll_GetWindowY
读取库记录的当前窗口 WY 值。
语法
u8 Scroll_GetWindowY();返回值
返回库记录的当前字节值,范围为 0–255。WX 包含硬件所需的七像素偏移。
使用条件与限制
这不是直接读取硬件寄存器。返回值不包含待提交的缓冲值、直接写入寄存器的值,也不包含分段扫描处理中临时应用的值。本函数封装了 __scroll_win_y_get。
使用示例
result[13]=Scroll_GetWindowY();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:63
Read tracked current WY; pending and raster-only values are excluded.
kitaqgb/lib/scroll.c:31
u8 Scroll_GetWindowY() { return __scroll_win_y_get(); }Scroll_Init
将背景滚动位置初始化为 (0,0),将窗口位置初始化为 WX=7、WY=0。
语法
void Scroll_Init();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
同步当前坐标和待提交坐标;不重置窗口可见性或分段事件表。
使用示例
Scroll_Init();result[0]=M_SCX;result[1]=M_SCY;result[2]=M_WX;result[3]=M_WY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:42
Reset background scroll to zero and place the window at screen origin using hardware WX=7 and WY=0.
kitaqgb/lib/scroll.c:5
void Scroll_Init()
{
__scroll_bg_set(0, 0);
__scroll_win_set(7, 0);
}Scroll_SetBg
立即设置背景的两个滚动坐标。
语法
void Scroll_SetBg(u8 scx, u8 scy);参数
scx背景水平滚动量,范围为 0–255 像素;增大该值会使背景向左移动。
scy背景垂直滚动量,范围为 0–255 像素;增大该值会使背景向上移动。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
封装 __scroll_bg_set:同步当前值、待提交值和硬件值,并清除背景的脏标记;不等待 VBlank。
使用示例
Scroll_SetBg(8,16);result[28]=M_SCX;result[29]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:44
Write both hardware background-scroll bytes immediately.
kitaqgb/lib/scroll.c:12
void Scroll_SetBg(u8 scx, u8 scy) { __scroll_bg_set(scx, scy); }Scroll_SetBgBuffered
暂存背景的两个滚动坐标,等待后续提交。
语法
void Scroll_SetBgBuffered(u8 scx, u8 scy);参数
scx背景水平滚动量,范围为 0–255 像素;增大该值会使背景向左移动。
scy背景垂直滚动量,范围为 0–255 像素;增大该值会使背景向上移动。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不改变库记录的当前值或寄存器。请调用 __scroll_flush 或 Scroll_Flush 应用暂存值;暂存操作本身不等待 VBlank。本函数封装了 __scroll_bg_set_buffered。
使用示例
Scroll_SetBgBuffered(16,24);result[14]=M_SCX;result[15]=Scroll_GetBgX();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:66
Stage background-scroll values in the intrinsic buffer for a later flush.
kitaqgb/lib/scroll.c:34
void Scroll_SetBgBuffered(u8 scx, u8 scy) { __scroll_bg_set_buffered(scx, scy); }Scroll_SetBgX
立即设置背景的水平滚动位置。
语法
void Scroll_SetBgX(u8 scx);参数
scx背景水平滚动量,范围为 0–255 像素;增大该值会使背景向左移动。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。本函数封装了 __scroll_bg_x_set。
使用示例
Scroll_SetBgX(250);result[4]=M_SCX;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:46
Write horizontal background scroll while leaving vertical scroll unchanged.
kitaqgb/lib/scroll.c:14
void Scroll_SetBgX(u8 scx) { __scroll_bg_x_set(scx); }Scroll_SetBgY
立即设置背景的垂直滚动位置。
语法
void Scroll_SetBgY(u8 scy);参数
scy背景垂直滚动量,范围为 0–255 像素;增大该值会使背景向上移动。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。本函数封装了 __scroll_bg_y_set。
使用示例
Scroll_SetBgY(2);result[5]=M_SCY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:48
Write vertical background scroll while leaving horizontal scroll unchanged.
kitaqgb/lib/scroll.c:16
void Scroll_SetBgY(u8 scy) { __scroll_bg_y_set(scy); }Scroll_SetWindow
立即设置窗口的 WX 和 WY。
语法
void Scroll_SetWindow(u8 wx, u8 wy);参数
wx原始 WX 字节值,范围为 0–255:在所需屏幕 X 坐标上加 7;窗口左缘位于屏幕左侧时使用 7。
wy窗口顶端的屏幕 Y 坐标,范围为 0–255 像素;可见行范围为 0–143。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。本函数封装了 __scroll_win_set。
使用示例
Scroll_SetWindow(15,8);result[8]=M_WX;result[9]=M_WY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:55
Write raw hardware WX/WY values; callers add seven to a screen-space window X.
kitaqgb/lib/scroll.c:23
void Scroll_SetWindow(u8 wx, u8 wy) { __scroll_win_set(wx, wy); }Scroll_SetWindowBuffered
暂存窗口的两个坐标,等待后续提交。
语法
void Scroll_SetWindowBuffered(u8 wx, u8 wy);参数
wx原始 WX 字节值,范围为 0–255:在所需屏幕 X 坐标上加 7;窗口左缘位于屏幕左侧时使用 7。
wy窗口顶端的屏幕 Y 坐标,范围为 0–255 像素;可见行范围为 0–143。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不改变库记录的当前值或寄存器。请调用 __scroll_flush 或 Scroll_Flush 应用暂存值;暂存操作本身不等待 VBlank。本函数封装了 __scroll_win_set_buffered。
使用示例
Scroll_SetWindowBuffered(39,32);result[16]=M_WX;result[17]=Scroll_GetWindowX();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:68
Stage raw window-coordinate values for a later flush.
kitaqgb/lib/scroll.c:36
void Scroll_SetWindowBuffered(u8 wx, u8 wy) { __scroll_win_set_buffered(wx, wy); }Scroll_SetWindowX
立即设置窗口的原始 WX 坐标,不改变 WY。
语法
void Scroll_SetWindowX(u8 wx);参数
wx原始 WX 字节值,范围为 0–255:在所需屏幕 X 坐标上加 7;窗口左缘位于屏幕左侧时使用 7。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。本函数封装了 __scroll_win_x_set。
使用示例
Scroll_SetWindowX(23);result[10]=M_WX;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:57
Write raw hardware WX without converting a screen-space X coordinate.
kitaqgb/lib/scroll.c:25
void Scroll_SetWindowX(u8 wx) { __scroll_win_x_set(wx); }Scroll_SetWindowY
立即设置窗口的 WY 坐标,不改变 WX。
语法
void Scroll_SetWindowY(u8 wy);参数
wy窗口顶端的屏幕 Y 坐标,范围为 0–255 像素;可见行范围为 0–143。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。本函数封装了 __scroll_win_y_set。
使用示例
Scroll_SetWindowY(16);result[11]=M_WY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:59
Write the window Y coordinate through the intrinsic.
kitaqgb/lib/scroll.c:27
void Scroll_SetWindowY(u8 wy) { __scroll_win_y_set(wy); }Scroll_SplitCommit
保存窗口的基础可见性状态,并启用分段事件表中的第一个扫描行事件。
语法
void Scroll_SplitCommit();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
共享事件表最多容纳八项。追加操作既不应用事件,也不排序;超出的项会被忽略。请在安全的帧边界按 reset→push→commit 的顺序重建事件表,并为相邻事件留出足够间隔,使处理程序能及时结束。由于中断延迟,计划触发的扫描行不一定是第一个受影响像素所在的行。事件表非空时,会在 IE 中启用 VBlank/STAT 并执行 EI。空表会关闭 LYC 中断源,但不恢复 IE 或 CPU 中断状态。本函数封装了 __scroll_split_commit。
使用示例
Scroll_SplitCommit();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
使用 Scroll 封装函数实现三个背景滚动带。LY=64 和 104 处的事件将 X 分别改为 8 和 16,使 24×16 矩形显示在 (32,16)、(24,80) 和 (16,112)。图像直观展示各滚动带的位移。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:92
Publish the pending split configuration through the compiler runtime.
kitaqgb/lib/scroll.c:66
void Scroll_SplitCommit() { __scroll_split_commit(); }Scroll_SplitPush
追加一个分段事件,在指定扫描行事件触发时改变背景滚动位置。
语法
void Scroll_SplitPush(u8 ly, u8 scx, u8 scy);参数
ly扫描行事件编号;可见区域使用 0–143,并按升序追加。若第一项的行号为零,则会在 VBlank 期间应用。
scx背景水平滚动量,范围为 0–255 像素;增大该值会使背景向左移动。
scy背景垂直滚动量,范围为 0–255 像素;增大该值会使背景向上移动。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
共享事件表最多容纳八项。追加操作既不应用事件,也不排序;超出的项会被忽略。请在安全的帧边界按 reset→push→commit 的顺序重建事件表,并为相邻事件留出足够间隔,使处理程序能及时结束。由于中断延迟,计划触发的扫描行不一定是第一个受影响像素所在的行。本函数封装了 __scroll_split_push。
使用示例
Scroll_SplitPush(64,8,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
使用 Scroll 封装函数实现三个背景滚动带。LY=64 和 104 处的事件将 X 分别改为 8 和 16,使 24×16 矩形显示在 (32,16)、(24,80) 和 (16,112)。图像直观展示各滚动带的位移。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:85
Append a background-scroll split directly. Use Raster_Push when caller-side scanline ordering and capacity validation are desired.
kitaqgb/lib/scroll.c:53
void Scroll_SplitPush(u8 ly, u8 scx, u8 scy) { __scroll_split_push(ly, scx, scy); }Scroll_SplitPushBgColor0
追加一个事件,用于改变 CGB 背景调色板零中的颜色零。
语法
void Scroll_SplitPushBgColor0(u8 ly, u16 rgb15);参数
ly扫描行事件编号;可见区域使用 0–143,并按升序追加。若第一项的行号为零,则会在 VBlank 期间应用。
rgb1515 位 RGB555 颜色:红色占位 0–4,绿色占位 5–9,蓝色占位 10–14。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
仅适用于 CGB。通过扩展分段操作占用八项事件表中的一项。写调色板时会等待 PPU 允许访问。在这个以普通速度运行的 CGB 示例中,LY=64/112 的事件从第 66/114 行开始产生可见效果。不要将事件行号视为第一个实际绘制受影响的行号。
使用示例
Scroll_SplitPushBgColor0(0,0x7C00);Scroll_SplitPushBgColor0(64,0x001F);Scroll_SplitPushBgColor0(112,0x03E0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
在一帧绘制过程中改变 CGB 的背景颜色零。以普通速度运行时,本示例的第 0–65 行为蓝色,第 66–113 行为红色,第 114–143 行为绿色。事件安排在第 0、64 和 112 行;中断处理和调色板访问会产生延迟。DMG 不具备这些彩色调色板。

构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_color.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_color.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:90
Reuse the two background-scroll fields to carry the low/high RGB15 bytes for a split whose operation flag selects background color zero.
kitaqgb/lib/scroll.c:61
void Scroll_SplitPushBgColor0(u8 ly, u16 rgb15)
{
__scroll_split_push_ex(ly, (u8)rgb15, (u8)(rgb15 >> 8), 0, 0, KQ_SCROLL_SPLIT_BG_COLOR0);
}Scroll_SplitPushEx
追加一个分段事件,通过标志位选择背景、窗口、可见性或背景颜色零的操作。
语法
void Scroll_SplitPushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);参数
ly扫描行事件编号;可见区域使用 0–143,并按升序追加。若第一项的行号为零,则会在 VBlank 期间应用。
scx背景水平滚动量,范围为 0–255 像素;增大该值会使背景向左移动。
scy背景垂直滚动量,范围为 0–255 像素;增大该值会使背景向上移动。
wx原始 WX 字节值,范围为 0–255:在所需屏幕 X 坐标上加 7;窗口左缘位于屏幕左侧时使用 7。
wy窗口顶端的屏幕 Y 坐标,范围为 0–255 像素;可见行范围为 0–143。
flags操作标志位:背景=1、窗口位置=2、显示=4、隐藏=8、CGB 背景调色板 0 的颜色 0=16。未知位会被丢弃。颜色零模式将 scx/scy 分别用作 RGB15 的低字节和高字节。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
共享事件表最多容纳八项。追加操作既不应用事件,也不排序;超出的项会被忽略。请在安全的帧边界按 reset→push→commit 的顺序重建事件表,并为相邻事件留出足够间隔,使处理程序能及时结束。由于中断延迟,计划触发的扫描行不一定是第一个受影响像素所在的行。显示和隐藏标志同时置位时,以显示为准。颜色零操作仅适用于 CGB;若与背景移动同时使用,两项操作会共用 scx/scy 字节。本函数封装了 __scroll_split_push_ex。
使用示例
Scroll_SplitPushEx(104,16,0,7,0,KQ_SCROLL_SPLIT_USE_BG|KQ_SCROLL_SPLIT_WIN_HIDE);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
使用 Scroll 封装函数实现三个背景滚动带。LY=64 和 104 处的事件将 X 分别改为 8 和 16,使 24×16 矩形显示在 (32,16)、(24,80) 和 (16,112)。图像直观展示各滚动带的位移。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:87
Append an extended split with raw background/window fields and operation flags.
kitaqgb/lib/scroll.c:55
void Scroll_SplitPushEx(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags)
{
__scroll_split_push_ex(ly, scx, scy, wx, wy, flags);
}Scroll_SplitReset
清零分段事件数量及索引,关闭分段处理,并清除 STAT 中的 LYC 中断使能位。
语法
void Scroll_SplitReset();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
共享事件表最多容纳八项。追加操作既不应用事件,也不排序;超出的项会被忽略。请在安全的帧边界按 reset→push→commit 的顺序重建事件表,并为相邻事件留出足够间隔,使处理程序能及时结束。由于中断延迟,计划触发的扫描行不一定是第一个受影响像素所在的行。本操作不会擦除表中的字节、清除 IE 或关闭 CPU 中断。本函数封装了 __scroll_split_reset。
使用示例
Scroll_SplitReset();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
使用 Scroll 封装函数实现三个背景滚动带。LY=64 和 104 处的事件将 X 分别改为 8 和 16,使 24×16 矩形显示在 (32,16)、(24,80) 和 (16,112)。图像直观展示各滚动带的位移。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:82
Clear the pending split table before building a new raster configuration.
kitaqgb/lib/scroll.c:50
void Scroll_SplitReset() { __scroll_split_reset(); }Scroll_WindowAdd
将有符号位移加到库记录的窗口坐标上,并立即应用。
语法
void Scroll_WindowAdd(s8 dx, s8 dy);参数
dx有符号水平位移,单位为像素,范围为 −128 至 127。
dy有符号垂直位移,单位为像素,范围为 −128 至 127。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不执行等待。立即设置或增量操作会将受影响的整对待提交坐标同步为当前值,并清除其脏标记。即使只立即设置一个轴,同一对中另一个轴的待提交值也会被取消。结果按模 256 回绕。增量操作以库记录的当前值为起点,而不是待提交值或光栅处理临时写入寄存器的值。本函数封装了 __scroll_win_add。
使用示例
Scroll_WindowAdd(4,-40);result[24]=M_WX;result[25]=M_WY;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:75
Apply signed deltas to the byte-sized hardware window coordinates.
kitaqgb/lib/scroll.c:43
void Scroll_WindowAdd(s8 dx, s8 dy) { __scroll_win_add(dx, dy); }Scroll_WindowHide
清除 LCDC 的窗口显示位,并将库记录的窗口状态设为隐藏。
语法
void Scroll_WindowHide();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不改变坐标或地图选择。实际显示还要求 LCD 及背景/窗口功能已启用,并且窗口位于可见区域内。本函数封装了 __scroll_win_hide。
使用示例
Scroll_WindowHide();result[27]=M_LCDC&32;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:79
Request that the window be hidden through the compiler intrinsic.
kitaqgb/lib/scroll.c:47
void Scroll_WindowHide() { __scroll_win_hide(); }Scroll_WindowShow
置位 LCDC 的窗口显示位,并将库记录的窗口状态设为可见。
语法
void Scroll_WindowShow();返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
不改变坐标或地图选择。实际显示还要求 LCD 及背景/窗口功能已启用,并且窗口位于可见区域内。本函数封装了 __scroll_win_show。
使用示例
Scroll_WindowShow();result[26]=M_LCDC&32;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
通过 Scroll 函数学习立即更新、暂存、提交、增量和可见性控制。提交后,背景 X 从 250 变为 16,WX 从 23 变为 39。显示窗口后,窗口位的值为 32;隐藏后为 0。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_wrappers.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_wrappers.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k使用示例
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
摄像机位于 (8,16) 时,红色 32×24 矩形显示在屏幕坐标 (24,16)。窗口在 (96,80) 显示蓝色 64×64 边框,在 (112,96) 显示绿色 16×16 方块。在 DMG 上,所有图形均为黑色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/scroll.h:77
Request window visibility through the compiler intrinsic.
kitaqgb/lib/scroll.c:45
void Scroll_WindowShow() { __scroll_win_show(); }slg.h — 棋盘、行动列表与撤销
提供以字节存值的棋盘、有容量上限的走法列表,以及撤销记录栈。游戏通过组合这些操作,把走法或撤销记录应用到棋盘。
slg_board_clear
用指定值填充棋盘的所有格子。
语法
void slg_board_clear(SLGBoard* board, u8 value);参数
board要操作的
SLGBoard;初始化时必须提供非 NULL 的存储空间。value要写入的字节值;由游戏定义其含义,例如棋子种类或地形类型。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
slg_board_clear(&board,0);result[1]=cells[0];result[2]=cells[11];这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:33
Fill every cell with value; a null board is ignored. A valid board must own enough accessible cell storage for its declared dimensions.
kitaqgb/lib/slg_board.c:36
void slg_board_clear(SLGBoard* board, u8 value)
{
u16 total;
u16 i;
u8* p;
if (board == 0) return;
total = (u16)((u16)board->width * (u16)board->height);
i = 0;
p = board->cells;
while (i < total) {
*p = value;
p++;
i++;
}
}slg_board_get
读取一个格子;棋盘为 NULL 或坐标越界时返回零。
语法
u8 slg_board_get(const SLGBoard* board, u8 x, u8 y);参数
board要操作的
SLGBoard;初始化时必须提供非 NULL 的存储空间。x从左起的列坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
y从上起的行坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
返回值
格子的字节值;读取位置无效时返回零。仅凭零无法区分空格与无效坐标。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
result[3]=slg_board_get(&board,1,1);result[4]=slg_board_get(&board,4,1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:36
Return zero for a null board or out-of-range coordinate; otherwise read the cell. Zero therefore cannot distinguish an invalid lookup from an empty cell.
kitaqgb/lib/slg_board.c:54
u8 slg_board_get(const SLGBoard* board, u8 x, u8 y)
{
u8* p;
if (board == 0) return 0;
if (x >= board->width || y >= board->height) return 0;
p = slg_cell_ptr((SLGBoard*)board, x, y);
return *p;
}slg_board_init
关联棋盘尺寸和格子数组,保留数组中的现有内容。
语法
void slg_board_init(SLGBoard* board, u8 width, u8 height, u8* cells);参数
board要操作的
SLGBoard;初始化时必须提供非 NULL 的存储空间。width棋盘的列数,同时也是每行的跨度。
height棋盘的行数。
cells可写的行优先数组,至少容纳
width*height个格子字节。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
cells[0]=9;slg_board_init(&board,4,3,cells);result[0]=slg_board_get(&board,0,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:30
Attach width * height bytes of caller-owned cell storage without clearing it.
kitaqgb/lib/slg_board.c:27
void slg_board_init(SLGBoard* board, u8 width, u8 height, u8* cells)
{
board->width = width;
board->height = height;
board->cells = cells;
}slg_board_set
写入棋盘中的一个有效格子;棋盘为 NULL 或坐标越界时不执行操作。
语法
void slg_board_set(SLGBoard* board, u8 x, u8 y, u8 value);参数
board要操作的
SLGBoard;初始化时必须提供非 NULL 的存储空间。x从左起的列坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
y从上起的行坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
value要写入的字节值;由游戏定义其含义,例如棋子种类或地形类型。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
slg_board_set(&board,1,1,7);slg_board_set(&board,4,1,9);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:38
Write a cell only when the board and coordinate are valid.
kitaqgb/lib/slg_board.c:64
void slg_board_set(SLGBoard* board, u8 x, u8 y, u8 value)
{
u8* p;
if (board == 0) return;
if (x >= board->width || y >= board->height) return;
p = slg_cell_ptr(board, x, y);
*p = value;
}slg_move_list_clear
将走法记录数归零,但不清除已存储的记录。
语法
void slg_move_list_clear(SLGMoveList* list);参数
list要操作的走法列表;初始化时指针不得为 NULL。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
slg_move_list_clear(&moves);result[10]=moves.count;result[11]=move_data[0].value;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:42
Forget the moves without erasing their storage; a null list is ignored.
kitaqgb/lib/slg_board.c:83
void slg_move_list_clear(SLGMoveList* list)
{
if (list == 0) return;
list->count = 0;
}slg_move_list_init
关联走法记录的存储空间及容量,并将记录数设为零。
语法
void slg_move_list_init(SLGMoveList* list, SLGMove* items, u8 capacity);参数
list要操作的走法列表;初始化时指针不得为 NULL。
items由调用方持有的数组,至少容纳
capacity条SLGMove记录。capacity最多可保存的记录数;为零时不能追加。每条记录含三个字节:x、y 和 value。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
slg_move_list_init(&moves,move_data,2);result[5]=moves.count;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:40
Attach caller-owned move storage and reset the list length.
kitaqgb/lib/slg_board.c:75
void slg_move_list_init(SLGMoveList* list, SLGMove* items, u8 capacity)
{
list->items = items;
list->capacity = capacity;
list->count = 0;
}slg_move_list_push
向走法列表追加坐标和值,不改变棋盘。
语法
u8 slg_move_list_push(SLGMoveList* list, u8 x, u8 y, u8 value);参数
list要操作的走法列表;初始化时指针不得为 NULL。
x从左起的列坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
y从上起的行坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
value要写入的字节值;由游戏定义其含义,例如棋子种类或地形类型。
返回值
追加成功返回 1;存储空间为 NULL 或已满时返回 0。失败时保留记录数和现有记录。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
result[6]=slg_move_list_push(&moves,1,1,7);result[7]=slg_move_list_push(&moves,2,1,8);result[8]=slg_move_list_push(&moves,3,1,9);result[9]=moves.count;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:45
Append coordinates and a value to the bounded move list. Return zero on a null/full list without replacing any existing move.
kitaqgb/lib/slg_board.c:91
u8 slg_move_list_push(SLGMoveList* list, u8 x, u8 y, u8 value)
{
SLGMove* move;
if (list == 0) return 0;
if (list->count >= list->capacity) return 0;
move = &list->items[(__safe_index u8)list->count];
move->x = x;
move->y = y;
move->value = value;
list->count++;
return 1;
}slg_undo_init
关联撤销记录的存储空间及容量,并将栈初始化为空。
语法
void slg_undo_init(SLGUndoStack* stack, SLGMove* items, u8 capacity);参数
stack要操作的撤销栈;初始化时指针不得为 NULL。
items由调用方持有的数组,至少容纳
capacity条SLGMove记录。capacity最多可保存的记录数;为零时不能追加。每条记录含三个字节:x、y 和 value。
返回值
无返回值,本条说明受影响状态及生效时机。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
slg_undo_init(&undo,undo_data,2);result[12]=undo.count;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:47
Attach caller-owned undo records and initialize an empty stack.
kitaqgb/lib/slg_board.c:106
void slg_undo_init(SLGUndoStack* stack, SLGMove* items, u8 capacity)
{
stack->items = items;
stack->capacity = capacity;
stack->count = 0;
}slg_undo_pop
弹出并取得最近一条撤销记录;由调用方将其应用到棋盘。
语法
u8 slg_undo_pop(SLGUndoStack* stack, SLGMove* out_move);参数
stack要操作的撤销栈;初始化时指针不得为 NULL。
out_move接收弹出记录的目标位置;为 NULL 时直接丢弃记录,不复制内容。
返回值
弹出记录成功返回 1;栈为 NULL 或为空时返回 0。空栈不会改变输出内容。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
result[15]=slg_undo_pop(&undo,&old);result[16]=old.value;result[17]=slg_board_get(&board,1,1);slg_undo_pop(&undo,&old);slg_board_set(&board,old.x,old.y,old.value);result[18]=slg_board_get(&board,1,1);result[19]=slg_undo_pop(&undo,&old);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:53
Remove the newest undo record and optionally copy it out. A null output still discards the record; a null or empty stack returns zero.
kitaqgb/lib/slg_board.c:128
u8 slg_undo_pop(SLGUndoStack* stack, SLGMove* out_move)
{
if (stack == 0) return 0;
if (stack->count == 0) return 0;
stack->count--;
if (out_move != 0) {
out_move->x = stack->items[stack->count].x;
out_move->y = stack->items[stack->count].y;
out_move->value = stack->items[stack->count].value;
}
return 1;
}slg_undo_push
将格子的先前值压入撤销栈,不修改棋盘。
语法
u8 slg_undo_push(SLGUndoStack* stack, u8 x, u8 y, u8 old_value);参数
stack要操作的撤销栈;初始化时指针不得为 NULL。
x从左起的列坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
y从上起的行坐标,以零为起点;记录走法时不检查是否超出棋盘边界。
old_value格子先前的字节值;函数不会自动从棋盘读取该值。
返回值
追加成功返回 1;存储空间为 NULL 或已满时返回 0。失败时保留记录数和现有记录。
使用条件与限制
存储空间由调用方提供。初始化既不分配空间,也不清除内容。棋盘坐标以格子为单位。
使用示例
result[13]=slg_undo_push(&undo,1,1,slg_board_get(&board,1,1));slg_board_set(&board,1,1,8);slg_undo_push(&undo,1,1,8);result[14]=slg_undo_push(&undo,0,0,9);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
学习棋盘、走法列表和撤销记录各自的作用。棋盘初始化保留数值 9;向容量为两条的列表追加第三条记录时返回 0。仅弹出撤销记录,棋盘值仍为 8;明确应用取出的记录后才恢复为 7。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_board.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_board.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/slg.h:50
Record a cell's previous value for later undo. Return zero if the stack is null or full; this function does not change the board itself.
kitaqgb/lib/slg_board.c:115
u8 slg_undo_push(SLGUndoStack* stack, u8 x, u8 y, u8 old_value)
{
if (stack == 0) return 0;
if (stack->count >= stack->capacity) return 0;
stack->items[stack->count].x = x;
stack->items[stack->count].y = y;
stack->items[stack->count].value = old_value;
stack->count++;
return 1;
}sprite.h — OBJ 分配、定位、组合精灵与动画
精灵是可移动的硬件图形,通常为 8×8 或 8×16 像素,由对象属性存储器 OAM 的条目描述。先分配槽位,在工作副本中设置位置、图块和属性,再进行传输。元精灵将多个槽位组合成一个对象。GB 有 40 个槽位,每条扫描线最多 10 个;FC 有 64 个,每条扫描线最多 8 个。
GB 属性:位 4 选择 DMG 的 OBP1;位 5/6 控制 X/Y 翻转;位 7 将精灵置于非零背景色编号之后。CGB 的位 0..2 选择对象调色板,位 3 选择图块数据 VRAM 存储体;DMG 调色板选择位不用于原生 CGB 颜色。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
anim_update
推进 SpriteAnim 计时器并返回选中的图块编号。每调用 ticks_per_frame 次就切换到下一个连续图块,经过 frame_count 帧后回到首帧。调用方通过 sprite_set_tile 应用返回值。
语法
u8 anim_update(SpriteAnim* anim);参数
anim可写的 SpriteAnim 状态,包含 first_tile、frame_count、frame、ticks 和 ticks_per_frame。计数非零时,frame 应小于 frame_count,ticks 应小于 ticks_per_frame。
返回值
选中的 8 位图块编号;状态指针为空时返回 0。函数不写入 OAM。
使用条件与限制
frame_count 为零时不推进,直接返回 first_tile;ticks_per_frame 为零时冻结当前帧。计时器按调用次数而非视频帧推进。图块编号在 256 处回绕,8×16 模式下也不会隔一个编号递增。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_example_animation.first_tile=128;sprite_example_animation.frame_count=2;
sprite_example_animation.frame=0;sprite_example_animation.ticks=0;
sprite_example_animation.ticks_per_frame=2;
sprite_example_expect(anim_update(&sprite_example_animation)==128);
tile=anim_update(&sprite_example_animation);
sprite_example_expect(tile==129);sprite_set_tile(5,tile);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
设置 first_tile=128、两帧、每帧两次调用后,第一次更新返回箭头图块 128,第二次返回正方形图块 129。ANIMATION 旁的正方形证明返回值已应用到 OAM。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:86
Advance animation ticks/frame with wrapping frame selection and return the resulting tile. Zero ticks-per-frame freezes animation; null state returns zero.
kitaqgb/lib/sprite.c:211
u8 anim_update(SpriteAnim* anim)
{
if (anim == 0) return 0;
if (anim->frame_count == 0) return anim->first_tile;
if (anim->ticks_per_frame == 0) return (u8)(anim->first_tile + anim->frame);
anim->ticks++;
if (anim->ticks >= anim->ticks_per_frame) {
anim->ticks = 0;
anim->frame++;
if (anim->frame >= anim->frame_count) anim->frame = 0;
}
return (u8)(anim->first_tile + anim->frame);
}metasprite_draw
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
把指定数量的精灵部件放入连续槽位。每个部件包含有符号偏移、图块和属性。函数激活空闲槽位、覆盖已激活槽位,并在槽位上限处停止。
语法
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count);参数
first_id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
x以像素为单位的横向位置,使用无符号字节表示,不是图块坐标。
y屏幕 Y 坐标,单位为像素。GB 包装函数会加上 16 后写入 OAM。
parts至少含有 count 个可读 MetaSpritePart 的数组。每个 4 字节记录依次为有符号 dx/dy、图块和属性,没有终止标记。
count部件数量,以无符号字节表示。为零时不写入;连续槽位达到 SPRITE_MAX 时,只写入此前能容纳的部件。
返回值
返回实际写入的部件数,范围为 0..count。通过该值判断是否因槽位上限而截断。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
将有符号字节偏移加到基准坐标后,结果按 8 位回绕。整个调用期间部件数组必须可读。已激活的槽位也会被覆盖,函数不会保护其他对象使用的槽位。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_example_expect(metasprite_draw(10,88,SPRITE_EXAMPLE_Y(64),sprite_example_pair,2)==2);
sprite_example_expect(sprite_count_used()==9); // Sparse IDs count actual active slots.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:83
Place consecutive parts, counting each newly activated slot once. Existing active slots are overwritten without increasing the allocation count. Return the number written; an invalid first slot or zero count writes nothing.
kitaqgb/lib/sprite.c:191
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count)
{
u8 i = 0;
while (i < count) {
u8 id = (u8)(first_id + i);
if (id >= SPRITE_MAX) return i;
if (kq_sprite_active[id] == 0) {
kq_sprite_active[id] = 1;
kq_sprite_used = (u8)(kq_sprite_used + 1);
}
sprite_set_pos(id, (u8)(x + parts[i].dx), (u8)(y + parts[i].dy));
sprite_set_tile(id, parts[i].tile);
sprite_set_flags(id, parts[i].flags);
i++;
}
return count;
}sprite_alloc
分配第一个空闲精灵槽位,并先将其隐藏。隐藏不会解除分配;显示前请设置位置、图块和属性。
语法
u8 sprite_alloc();返回值
返回分配的槽位编号;全部已用时返回 0xFF。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
for(i=0;i<8;i++) sprite_example_expect(sprite_alloc()==i);
sprite_example_expect(sprite_count_used()==8);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:53
Claim the first free slot, hide it, and return its ID; return 0xFF when full. Previously stored tile/flag bytes are retained until the caller replaces them.
kitaqgb/lib/sprite.c:60
u8 sprite_alloc()
{
u8 i = 0;
while (i < SPRITE_MAX) {
if (kq_sprite_active[i] == 0) {
kq_sprite_active[i] = 1;
kq_sprite_used++;
sprite_hide(i);
return i;
}
i++;
}
return 0xFF;
}sprite_count_used
返回已分配槽位数,包括隐藏精灵和由 metasprite_draw 激活的槽位。这是分配数量,不是屏幕上的可见数量。
语法
u8 sprite_count_used();返回值
0 到 SPRITE_MAX 之间的无符号字节。
使用条件与限制
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_example_expect(sprite_count_used()==9); // After hiding, freeing and drawing the pair.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:73
Return the number of active slots, including slots activated by metasprite_draw. Hidden allocated slots remain active until sprite_free releases them.
kitaqgb/lib/sprite.c:148
u8 sprite_count_used()
{
return kq_sprite_used;
}sprite_flush_oam
等待 VBlank,然后通过 sprite_flush_oam_now 传输 OAM 工作副本。
语法
void sprite_flush_oam();返回值
无(void)。
使用条件与限制
GB 例程在 HRAM 中执行 DMA 代码,从对齐的 OAM 工作副本复制 160 字节。传输期间禁用中断,结束后始终启用中断,不恢复调用前可能禁用的状态。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
while(1) { sprite_flush_oam(); } // Transfer prepared sprites at the next frame boundary.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:70
Wait for VBlank, then transfer shadow OAM using the immediate DMA routine.
kitaqgb/lib/sprite.c:139
void sprite_flush_oam()
{
__wait_vblank();
sprite_flush_oam_now();
}sprite_flush_oam_now
立即把 OAM 工作副本传输到硬件。函数不等待安全的显示时段,调用方需要安排执行时机。
语法
void sprite_flush_oam_now();返回值
无(void)。
使用条件与限制
GB 例程在 HRAM 中执行 DMA 代码,从对齐的 OAM 工作副本复制 160 字节。传输期间禁用中断,结束后始终启用中断,不恢复调用前可能禁用的状态。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
// Rendering is disabled here; all shadow entries have been prepared.
sprite_flush_oam_now();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:68
Install the HRAM stub lazily and execute OAM DMA with interrupts disabled. This routine does not wait for VBlank and always enables interrupts afterward; it does not restore the caller's previous interrupt-enable state.
kitaqgb/lib/sprite.c:122
void sprite_flush_oam_now()
{
SpriteDmaRoutine dma;
if (kq_sprite_dma_ready == 0) {
sprite_install_dma_stub();
}
dma = (SpriteDmaRoutine)kq_sprite_dma_stub;
__asm {
DI
}
dma();
__asm {
EI
}
}sprite_free
释放已分配的槽位,将其隐藏,并把使用数减一。编号越界或槽位已经空闲时不执行操作。
语法
void sprite_free(u8 id);参数
id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
返回值
无(void)。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_free(7);sprite_free(7);
sprite_example_expect(sprite_count_used()==7); // Repeated release is ignored.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:55
Release an allocated valid slot and hide it. Repeated frees do not decrement the count.
kitaqgb/lib/sprite.c:78
void sprite_free(u8 id)
{
if (sprite_valid(id) == 0) return;
if (kq_sprite_active[id] == 0) return;
kq_sprite_active[id] = 0;
if (kq_sprite_used != 0) kq_sprite_used--;
sprite_hide(id);
}sprite_hide
通过修改工作副本中的位置来隐藏精灵。分配状态、图块和属性保持不变,因此重新设置可见位置即可显示,无须再次分配。
语法
void sprite_hide(u8 id);参数
id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
返回值
无(void)。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_hide(6);
sprite_example_expect(sprite_count_used()==8); // Hiding retains allocation.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:64
Move a valid shadow slot off screen without freeing it.
kitaqgb/lib/sprite.c:112
void sprite_hide(u8 id)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].y = 0;
kq_sprite_oam[id].x = 0;
}sprite_init
重置精灵分配状态,并在 OAM 工作副本中隐藏所有槽位。软件记录的高度设为 8 像素;硬件精灵尺寸和 OAM 传输需要另行设置。
语法
void sprite_init();返回值
无(void)。
使用条件与限制
GB 初始化会清除每个工作条目的全部 4 字节,并重置 HRAM DMA 例程的安装状态。除非自定义 DMA 源仍提供完整的 160 字节,否则请保持默认 SPRITE_MAX=40。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_init();
sprite_example_expect(sprite_count_used()==0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
初始化后分配数为零。八次分配返回编号 0..7。隐藏槽位 6 后仍为 8;两次释放槽位 7 后为 7;在 10 和 11 放置两个部件后增至 9。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:50
Clear allocation and shadow OAM state and reset the DMA-stub installation flag. The default height is eight pixels; the hardware display mode is configured elsewhere.
kitaqgb/lib/sprite.c:22
void sprite_init()
{
u8 i = 0;
kq_sprite_used = 0;
kq_sprite_height = 8;
kq_sprite_dma_ready = 0;
while (i < SPRITE_MAX) {
kq_sprite_active[i] = 0;
kq_sprite_oam[i].y = 0;
kq_sprite_oam[i].x = 0;
kq_sprite_oam[i].tile = 0;
kq_sprite_oam[i].flags = 0;
i++;
}
}sprite_max_scanline_count
根据记录的纵向位置和 kq_sprite_height,估算可见范围内同一扫描线上最多重叠多少个已激活精灵。
语法
u8 sprite_max_scanline_count();返回值
0 到 SPRITE_MAX 之间的无符号字节。
使用条件与限制
将 kq_sprite_height 设为硬件高度 8 或 16。估算不考虑透明像素、横向可见范围或硬件优先级。FC 读取库记录的坐标,因此直接用 OAM 内置函数改动的位置不会反映在估算中。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_example_expect(sprite_max_scanline_count()==SPRITE_EXAMPLE_LINE_COUNT);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:79
Estimate the largest overlap of active, non-hidden shadow sprites across visible scanlines using the configured sprite height. This is a software overlap count, not a rendered-pixel or hardware-priority simulation.
kitaqgb/lib/sprite.c:158
u8 sprite_max_scanline_count()
{
u8 line = 0;
u8 best = 0;
while (line < 144) {
u8 i = 0;
u8 count = 0;
u8 ly_oam = (u8)(line + 16);
while (i < SPRITE_MAX) {
u8 sy = kq_sprite_oam[i].y;
if (kq_sprite_active[i] != 0 && sy != 0) {
if (ly_oam >= sy && ly_oam < (u8)(sy + kq_sprite_height)) {
count++;
}
}
i++;
}
if (count > best) best = count;
line++;
}
return best;
}sprite_set_flags
替换整个精灵属性字节。组合对应平台的标志常量来选择翻转、调色板和背景优先级;未指定的位会清零。
语法
void sprite_set_flags(u8 id, u8 flags);参数
id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
flags使用本平台 SPRITE_FLAG_* 常量组成的完整属性字节。多个标志通过按位或(|)组合。
返回值
无(void)。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
GB 属性:位 4 选择 DMG 的 OBP1;位 5/6 控制 X/Y 翻转;位 7 将精灵置于非零背景色编号之后。CGB 的位 0..2 选择对象调色板,位 3 选择图块数据 VRAM 存储体;DMG 调色板选择位不用于原生 CGB 颜色。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_set_flags(1,SPRITE_FLAG_XFLIP);
sprite_set_flags(2,SPRITE_FLAG_YFLIP);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:62
Replace a valid shadow slot's complete hardware attribute byte.
kitaqgb/lib/sprite.c:105
void sprite_set_flags(u8 id, u8 flags)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].flags = flags;
}sprite_set_pos
用屏幕像素坐标修改 GB 精灵的 OAM 工作副本。函数会给 X 加 8、给 Y 加 16,转换为硬件坐标。
语法
void sprite_set_pos(u8 id, u8 x, u8 y);参数
id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
x以像素为单位的横向位置,使用无符号字节表示,不是图块坐标。
y屏幕 Y 坐标,单位为像素。GB 包装函数会加上 16 后写入 OAM。
返回值
无(void)。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
坐标加法采用 8 位运算,超过后按 256 回绕,不进行屏幕裁剪。只要槽位编号有效,即使未分配也能修改。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_set_pos(0,88,SPRITE_EXAMPLE_Y(16)); // Visible top-left: (88,16).这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:58
Convert screen coordinates to OAM coordinates by adding X=8 and Y=16. The byte additions wrap; this updates shadow storage even for an unallocated slot.
kitaqgb/lib/sprite.c:89
void sprite_set_pos(u8 id, u8 x, u8 y)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].x = (u8)(x + 8);
kq_sprite_oam[id].y = (u8)(y + 16);
}sprite_set_tile
替换一个 OAM 工作副本条目的图块编号。它只选择图块存储器中已有的图形,不上传图形,也不分配精灵。
语法
void sprite_set_tile(u8 id, u8 tile);参数
id槽位编号,范围为 0..SPRITE_MAX-1;默认 GB 为 0..39,FC 为 0..63。越界编号会被忽略。
tile8 位图块编号。须先加载图形。8×16 精灵仍遵循硬件规则,函数不会自动调整成成对图块编号。
返回值
无(void)。
使用条件与限制
位置、图块、属性和隐藏操作只修改工作副本,不会分配槽位,也不立即更新硬件 OAM。完成一帧的修改后,用 sprite_flush_oam 传输。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_set_tile(5,129); // Select the green square beside ANIMATION.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
设置 first_tile=128、两帧、每帧两次调用后,第一次更新返回箭头图块 128,第二次返回正方形图块 129。ANIMATION 旁的正方形证明返回值已应用到 OAM。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:60
Set a valid shadow slot's tile byte; allocation and hardware transfer are separate.
kitaqgb/lib/sprite.c:98
void sprite_set_tile(u8 id, u8 tile)
{
if (sprite_valid(id) == 0) return;
kq_sprite_oam[id].tile = tile;
}sprite_warn_scanline_overflow
将 sprite_max_scanline_count 与每条扫描线的精灵上限比较:GB 为 10,FC 为 8。它报告软件估算结果,不读取硬件溢出标志。
语法
u8 sprite_warn_scanline_overflow();返回值
估算值超过每条扫描线的上限时为 1,否则为 0。
使用条件与限制
将 kq_sprite_height 设为硬件高度 8 或 16。估算不考虑透明像素、横向可见范围或硬件优先级。FC 读取库记录的坐标,因此直接用 OAM 内置函数改动的位置不会反映在估算中。
示例使用完整的 GB 精灵库。定义 KQ_SPRITE_MINIMAL_RUNTIME 后,sprite.c 不再包含 sprite_free、sprite_set_pos、sprite_count_used、两个扫描线查询函数、metasprite_draw 和 anim_update。需要这些函数时请勿定义该宏。
使用示例
sprite_example_expect(sprite_warn_scanline_overflow()==1);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
SCAN LIMIT 下方一行从 (8,128) 开始。程序在同一行提交 11 个 GB 精灵或 9 个 FC 精灵,故意超过一个。GB 应显示 10 个箭头,FC 应显示 8 个,最后提交的箭头不出现。软件峰值与警告检查仍应分别得到 11/9 和 1。
本程序演示精灵分配、定位、翻转、复合对象、重叠、动画和扫描线上限。右上角两位数字应为 00,表示程序内的检查全部通过。每个箭头和正方形均占一个 8×8 图块。
NORMAL 在 (88,16) 显示向右箭头;FLIP X 在 (88,32) 向左;FLIP Y 在 (88,48) 上下翻转。META 在 (88,64) 和 (96,64) 放置两个箭头。OVERLAP 中,(88,80) 的箭头位于 (92,80) 的正方形之前,箭头的透明像素会露出正方形。ANIMATION 在 (88,96) 显示正方形。(120,16) 和 (120,32) 的槽位分别隐藏和释放后,应保持空白。
CGB 和 FC 的箭头采用绿色箭杆、红色轮廓和蓝色尖端细节,正方形为绿色。透明部分露出 GB 的白色背景或 FC 的黑色背景。DMG 用浅灰、深灰和黑色分别代替红、绿、蓝。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite.h:75
Report whether the software overlap estimate exceeds ten sprites on a scanline.
kitaqgb/lib/sprite.c:182
u8 sprite_warn_scanline_overflow()
{
return (u8)(sprite_max_scanline_count() > 10);
}sprite_order.h — 按优先级和轮换选择精灵
sprite_order 将重要对象排前,并在相同优先级对象间轮换 OAM 顺序。从最多 255 个候选对象输出最多 40 个,帮助管理每扫描行十对象的限制。sprite 库处理槽位分配及 DMA;sprite_order 处理候选选择顺序。
包含 sprite_order.h,并且只编译一次 sprite_order.c。初始化后,每帧先调用 begin,为每个候选对象调用 push,最后调用 build。排序库本身不要求 sprite.c 或任何全局变量定义。
硬件按 OAM 顺序选择与每条扫描行相交的前十个对象。将较小优先级值放前面,可以在线路拥挤时保护重要对象。轮换不保证同一优先级组内显示频率相等。若高优先级对象占满十个位置,该行的低优先级对象仍不可见。
SpriteOrderOamEntry 含四字节:Y、X、tile、flags。使用 sprite.h 的影子 OAM 时,写入 (SpriteOrderOamEntry*)kq_sprite_oam。保持标准 SPRITE_MAX 为 40,不要将分配器管理的槽位身份与重新排序的输出混用:build 不更新分配器记录。
sprite_order_begin
新一帧开始时清零提交数量,保留存储、高度和轮换相位。与每帧重新初始化不同,这允许轮换继续。
语法
void sprite_order_begin(SpriteOrder* order);参数
order由调用方持有的
SpriteOrder地址。使用前初始化,并在前台代码更新。
返回值
无返回值。order 为 null 时忽略。不擦除旧项目字节或已构建 OAM。
使用条件与限制
包含 sprite_order.h,并且只编译一次 sprite_order.c。初始化后,每帧先调用 begin,为每个候选对象调用 push,最后调用 build。排序库本身不要求 sprite.c 或任何全局变量定义。
硬件按 OAM 顺序选择与每条扫描行相交的前十个对象。将较小优先级值放前面,可以在线路拥挤时保护重要对象。轮换不保证同一优先级组内显示频率相等。若高优先级对象占满十个位置,该行的低优先级对象仍不可见。
使用示例
sprite_order_begin(&order_demo);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在同一行放置十四个对象,展示扫描行选择。HIGH 下两个菱形为高优先级,NORMAL 下十二个方块为普通优先级。CGB 用红色菱形和蓝色方块,DMG 用灰色和黑色。两个菱形始终可见,但只显示八个方块。标签 0..B 表示方块位置。PHASE 04 显示 4..B;PHASE 08 显示 8..B 及 0..3。正常构建每等待 60 帧推进 PHASE。发布截图在同一程序中固定相位;也检查了运行版本会改变方块选择。



构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite_order.h:42
Discard this frame's submissions while preserving storage, height and phase. A null state is ignored. Call once before submitting the next frame's sprites.
kitaqgb/lib/sprite_order.c:24
void sprite_order_begin(SpriteOrder* order)
{
if (order != 0) order->count = 0;
}sprite_order_build
按优先级和循环顺序,从提交候选构建 160 字节影子 OAM。将未使用槽位清零以隐藏,再推进相位供下次构建使用。不重新排列提交存储。
语法
u8 sprite_order_build(SpriteOrder* order, SpriteOrderOamEntry* output, u8 limit);参数
order由调用方持有的
SpriteOrder地址。使用前初始化,并在前台代码更新。output含 40 个
SpriteOrderOamEntry的可写数组。即使 limit 为零或小于 40,函数也会写全部 160 字节。limit最多输出数量,范围为 0..40;更大的值按 40 处理。限制的是整个 OAM,不是每条扫描行。
返回值
返回输出数量 0..40。有效空队列会清除全部槽位,并设 phase=0。否则先令 phase 对 count 取模,在各个按升序排列的优先级组中使用该循环起点,再递增并回绕 phase。limit 为零仍推进相位。order/output 为 null、状态停用、高度无效或 count>capacity 时,返回零,不改变输出或相位。
使用条件与限制
硬件按 OAM 顺序选择与每条扫描行相交的前十个对象。将较小优先级值放前面,可以在线路拥挤时保护重要对象。轮换不保证同一优先级组内显示频率相等。若高优先级对象占满十个位置,该行的低优先级对象仍不可见。
priority 参数控制选择顺序,与置于背景后方的属性无关。DMG 中,已选对象重叠时 X 较小者优先,X 相同时按 OAM 顺序。正常 CGB 彩色模式重叠使用 OAM 顺序,但通过 OPRI 选择 DMG 式顺序的配置仍然适用。本库不改变硬件优先级规则。
SpriteOrderOamEntry 含四字节:Y、X、tile、flags。使用 sprite.h 的影子 OAM 时,写入 (SpriteOrderOamEntry*)kq_sprite_oam。保持标准 SPRITE_MAX 为 40,不要将分配器管理的槽位身份与重新排序的输出混用:build 不更新分配器记录。
build 只写 RAM。提供不与状态或提交存储重叠的 160 字节输出数组。用于 OAM DMA 的缓冲区须从 256 字节边界开始。VBlank 传送前须完成构建;中断不得传送只构建了一半的缓冲区。示例使用 sprite_flush_oam。
每次提交的工作量有上限。构建会清除 160 字节影子 OAM,并最多扫描提交数组四遍;不对该数组排序,也不需要临时排序缓冲区。这不保证 255 个候选对象能满足游戏每帧预算;只提交场景所需候选对象。
使用示例
order_demo_state[1]=sprite_order_build(&order_demo,(SpriteOrderOamEntry*)kq_sprite_oam,40);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在同一行放置十四个对象,展示扫描行选择。HIGH 下两个菱形为高优先级,NORMAL 下十二个方块为普通优先级。CGB 用红色菱形和蓝色方块,DMG 用灰色和黑色。两个菱形始终可见,但只显示八个方块。标签 0..B 表示方块位置。PHASE 04 显示 4..B;PHASE 08 显示 8..B 及 0..3。正常构建每等待 60 帧推进 PHASE。发布截图在同一程序中固定相位;也检查了运行版本会改变方块选择。



构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite_order.h:62
Write a complete 160-byte shadow OAM: ascending priority, then cyclic order within each priority. Clamp limit to 40, return the number emitted and zero all unused entries (including Y) so they cannot consume scanline slots. Advance phase once for a nonempty valid queue, even when limit is zero. Empty queues reset phase to zero. Null/disabled/corrupt state or null output returns 0 without modifying output or phase. Output must provide 40 entries and must not overlap item/state storage. This only writes RAM. Build in foreground, then transfer during VBlank. DMG overlap priority also depends on X: this cannot override that hardware rule. OAM order chooses the first ten sprites per scanline on both DMG/CGB. It neither updates sprite_alloc's bookkeeping nor performs OAM DMA.
kitaqgb/lib/sprite_order.c:57
u8 sprite_order_build(SpriteOrder* order, SpriteOrderOamEntry* output, u8 limit)
{
u8 i;
u8 count;
u8 phase;
u8 priority;
u8 visited;
u8 written;
SpriteOrderItem* item;
if (order == 0 || output == 0) return 0;
if (order->items == 0 || order->capacity == 0) return 0;
if (order->count > order->capacity) return 0;
if (order->height != 8 && order->height != 16) return 0;
// Clear all hardware slots regardless of the requested emission limit.
for (i = 0; i < SPRITE_ORDER_OAM_COUNT; i++) {
output[i].y = 0;
output[i].x = 0;
output[i].tile = 0;
output[i].flags = 0;
}
count = order->count;
if (count == 0) {
order->phase = 0;
return 0;
}
if (limit > SPRITE_ORDER_OAM_COUNT) limit = SPRITE_ORDER_OAM_COUNT;
phase = order->phase;
while (phase >= count) phase = (u8)(phase - count);
written = 0;
for (priority = 0; priority < SPRITE_ORDER_LEVELS && written < limit; priority++) {
i = phase;
visited = 0;
while (visited < count && written < limit) {
item = &order->items[i];
if (item->priority == priority) {
output[written].y = item->oam.y;
output[written].x = item->oam.x;
output[written].tile = item->oam.tile;
output[written].flags = item->oam.flags;
written++;
}
visited++;
i++;
if (i >= count) i = 0;
}
}
phase++;
if (phase >= count) phase = 0;
order->phase = phase;
return written;
}sprite_order_init
将提交存储绑定到排序状态,重置数量和轮换相位。开始场景或更换存储时使用。
语法
u8 sprite_order_init(SpriteOrder* order, SpriteOrderItem* items, u8 capacity, u8 height);参数
order由调用方持有的
SpriteOrder地址。使用前初始化,并在前台代码更新。items可写
SpriteOrderItem数组,每项五字节。分配 capacity 项,并在使用期间保持存储有效。capacity数组容量,范围为 1..255 项;不是字节数,也不是硬件槽位数。
height所有对象共用高度。传入 8 或 16,与 LCDC 的 8×8/8×16 设置匹配;库不会配置 LCDC。8×16 模式下,硬件忽略图块编号最低位。
返回值
成功返回 1;items 为 null、容量为零、高度不是 8/16 或 order 为 null 时返回 0。order 非 null 时,失败会使其停用:items=NULL、capacity=count=phase=0、height=8。不改变项目数组。
使用条件与限制
包含 sprite_order.h,并且只编译一次 sprite_order.c。初始化后,每帧先调用 begin,为每个候选对象调用 push,最后调用 build。排序库本身不要求 sprite.c 或任何全局变量定义。
使用示例
sprite_init();sprite_order_init(&order_demo,order_demo_items,14,8);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在同一行放置十四个对象,展示扫描行选择。HIGH 下两个菱形为高优先级,NORMAL 下十二个方块为普通优先级。CGB 用红色菱形和蓝色方块,DMG 用灰色和黑色。两个菱形始终可见,但只显示八个方块。标签 0..B 表示方块位置。PHASE 04 显示 4..B;PHASE 08 显示 8..B 及 0..3。正常构建每等待 60 帧推进 PHASE。发布截图在同一程序中固定相位;也检查了运行版本会改变方块选择。



构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite_order.h:38
Bind writable item storage and select the hardware sprite height (8 or 16). Return 1 on success. Invalid storage, zero capacity or another height returns 0 and leaves a non-null state disabled. A null state returns 0 without writes.
kitaqgb/lib/sprite_order.c:6
u8 sprite_order_init(SpriteOrder* order, SpriteOrderItem* items, u8 capacity, u8 height)
{
if (order == 0) return 0;
order->items = 0;
order->capacity = 0;
order->count = 0;
order->height = 8;
order->phase = 0;
if (items == 0 || capacity == 0) return 0;
if (height != 8 && height != 16) return 0;
order->items = items;
order->capacity = capacity;
order->height = height;
return 1;
}sprite_order_push
提交一个至少有一像素与可见屏幕相交的精灵。完全在屏幕外的候选对象会先被拒绝,不占 OAM 选择槽位。
语法
u8 sprite_order_push(SpriteOrder* order, s16 x, s16 y, u8 tile, u8 flags, u8 priority);参数
order由调用方持有的
SpriteOrder地址。使用前初始化,并在前台代码更新。x从左上角计量的有符号屏幕 X。左缘 −7..159 可接受;≤−8 或≥160 表示完全在屏幕外。保存时加 8 转为 OAM 坐标。
y从左上角计量的有符号屏幕 Y。高度 8 时接受 −7..143,高度 16 时接受 −15..143。保存时加 16 转为 OAM 坐标。
tile图块编号,范围为 0..255,原样保存。8×16 模式请参见高度说明。
flags硬件属性字节,原样保存:翻转、背景后方优先级、DMG/CGB 调色板和 CGB VRAM 存储体位保留通常含义。
priority软件选择优先级,范围为 0..3,零最先。≥4 的值被拒绝。与硬件 flags 字节独立。
返回值
接受时返回 1 并递增 count。状态为 null/停用、高度无效、存储已满、优先级无效或对象完全在屏幕外时返回 0,不改变 items 或 count。
使用条件与限制
所有对象共用高度。传入 8 或 16,与 LCDC 的 8×8/8×16 设置匹配;库不会配置 LCDC。8×16 模式下,硬件忽略图块编号最低位。
硬件按 OAM 顺序选择与每条扫描行相交的前十个对象。将较小优先级值放前面,可以在线路拥挤时保护重要对象。轮换不保证同一优先级组内显示频率相等。若高优先级对象占满十个位置,该行的低优先级对象仍不可见。
priority 参数控制选择顺序,与置于背景后方的属性无关。DMG 中,已选对象重叠时 X 较小者优先,X 相同时按 OAM 顺序。正常 CGB 彩色模式重叠使用 OAM 顺序,但通过 OPRI 选择 DMG 式顺序的配置仍然适用。本库不改变硬件优先级规则。
使用示例
sprite_order_push(&order_demo,8,64,128,0,0);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在同一行放置十四个对象,展示扫描行选择。HIGH 下两个菱形为高优先级,NORMAL 下十二个方块为普通优先级。CGB 用红色菱形和蓝色方块,DMG 用灰色和黑色。两个菱形始终可见,但只显示八个方块。标签 0..B 表示方块位置。PHASE 04 显示 4..B;PHASE 08 显示 8..B 及 0..3。正常构建每等待 60 帧推进 PHASE。发布截图在同一程序中固定相位;也检查了运行版本会改变方块选择。



构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\sprite_order_demo.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\sprite_order_demo.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm实现与源码
kitaqgb/lib/sprite_order.h:49
Submit one sprite at signed, top-left SCREEN pixel coordinates. Preserve the tile and attribute bytes; use priority 0 (first) through 3 (last). Fully off-screen sprites, invalid priorities, disabled state and full storage return 0 without consuming a slot. Partly visible sprites are accepted. Return 1 on acceptance. Hardware 8x16 mode still ignores the tile index's low bit.
kitaqgb/lib/sprite_order.c:31
u8 sprite_order_push(SpriteOrder* order, s16 x, s16 y, u8 tile, u8 flags, u8 priority)
{
SpriteOrderItem* item;
if (order == 0) return 0;
if (order->items == 0 || order->capacity == 0) return 0;
if (order->height != 8 && order->height != 16) return 0;
if (order->count >= order->capacity || priority >= SPRITE_ORDER_LEVELS) return 0;
if (x <= -8 || x >= 160 || y >= 144) return 0;
if (order->height == 16) {
if (y <= -16) return 0;
} else {
if (y <= -8) return 0;
}
item = &order->items[order->count];
item->oam.y = (u8)(y + 16);
item->oam.x = (u8)(x + 8);
item->oam.tile = tile;
item->oam.flags = flags;
item->priority = priority;
order->count++;
return 1;
}system.h — 启动、帧计数、等待与中断
管理帧等待次数、等待后回调及可屏蔽 CPU 中断。
gb_disable_interrupts
gb_disable_interrupts 是 system_disable_interrupts 的别名。在 GB 上禁用 CPU 中断。
禁用可屏蔽 CPU 中断。
语法
void gb_disable_interrupts();返回值
无返回值。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
临界区应尽量短,结束后适当恢复中断。屏蔽中断与帧等待函数的运行条件是不同事项。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
gb_disable_interrupts();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例配对禁用和启用中断,专用验证程序还检查 CPU 中断状态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:32
#define gb_disable_interrupts system_disable_interruptskitaqgb/lib/system.c:52
void system_disable_interrupts()
{
__asm {
DI
RET
}
}gb_enable_interrupts
gb_enable_interrupts 是 system_enable_interrupts 的别名。在 GB 上启用 CPU 中断。
启用可屏蔽 CPU 中断。
语法
void gb_enable_interrupts();返回值
无返回值。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
启用前先准备好中断源及处理程序。FC 上此操作与 NMI 的启用或禁用独立。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
gb_enable_interrupts();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例恢复中断。FC 程序先禁用 APU 帧 IRQ 和 DMC IRQ 源,避免反复响应未确认的 IRQ。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:31
#define gb_enable_interrupts system_enable_interruptskitaqgb/lib/system.c:43
void system_enable_interrupts()
{
__asm {
EI
RET
}
}gb_frame_count
gb_frame_count 是 system_get_frame 的别名。读取该等待封装函数已完成次数的 16 位软件计数。
读取帧等待包装函数的 16 位计数器。
语法
u16 gb_frame_count();返回值
返回当前计数 0~65535。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
计数器统计通过此包装函数等待的次数,不自动测量实际经过的硬件帧;以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
result[3]=gb_frame_count();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在三次等待后读到三;回绕检查显式设置计数为 65535,确认下次等待后为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:30
#define gb_frame_count system_get_framekitaqgb/lib/system.c:31
u16 system_get_frame()
{
return kq_system_frame;
}gb_init
gb_init 是 system_init 的别名。重置软件帧计数和回调注册。
将计数器及已注册回调清零,初始化帧管理。
语法
void gb_init();返回值
无返回值。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
计数器统计通过此包装函数等待的次数,不自动测量实际经过的硬件帧;以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
gb_init();result[0]=gb_frame_count();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在注册回调前确认初始帧编号为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:27
#define gb_init system_initkitaqgb/lib/system.c:7
void system_init()
{
kq_system_frame = 0;
system_vblank_callback = 0;
}gb_set_vblank_callback
gb_set_vblank_callback 是 system_set_vblank_callback 的别名。注册或移除在帧等待结束后同步调用的回调。
注册、替换或移除帧等待后调用的函数。
语法
void gb_set_vblank_callback(SystemCallback callback);参数
callback要调用的函数;0 移除回调。
返回值
无返回值。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
这是无参数、无返回值的普通函数指针,不携带存储体编号;可能被调用期间,目标代码须保持映射可用。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
gb_set_vblank_callback(on_frame);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
回调执行三次后注册 0 以禁用。下一次等待将帧数推进到四,回调次数保持三。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:29
#define gb_set_vblank_callback system_set_vblank_callbackkitaqgb/lib/system.c:25
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}gb_wait_vblank
gb_wait_vblank 是 system_wait_vblank 的别名。等待帧边界,递增软件帧计数,并调用已注册的回调。
等待下一帧,递增计数器,再调用已注册回调。
语法
void gb_wait_vblank();返回值
无返回值。
使用条件与限制
头文件定义了展开为实现名称的对象式宏。参数、返回值和状态访问均与目标相同;别名不增加独立状态或处理。输入使用 input.h/input.c,帧服务使用 system.h/system.c,具体见完整示例及构建命令。
回调在此函数内部同步执行,注册不会安装中断处理程序。计数器仅统计此包装函数的等待次数,不自动测量硬件经过帧数,并以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
gb_wait_vblank();gb_wait_vblank();gb_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
等待三次调用回调三次,回调最后观察到的计数器为三。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\system_aliases.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\system_aliases.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:28
#define gb_wait_vblank system_wait_vblankkitaqgb/lib/system.c:15
void system_wait_vblank()
{
__wait_vblank();
kq_system_frame++;
if (system_vblank_callback != 0) {
system_vblank_callback();
}
}system_disable_interrupts
禁用可屏蔽 CPU 中断。
语法
void system_disable_interrupts();返回值
无返回值。
使用条件与限制
临界区应尽量短,结束后适当恢复中断。屏蔽中断与帧等待函数的运行条件是不同事项。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
system_disable_interrupts();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例配对禁用和启用中断,专用验证程序还检查 CPU 中断状态。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:25
Disable CPU interrupts and return; callers must explicitly re-enable them later.
kitaqgb/lib/system.c:52
void system_disable_interrupts()
{
__asm {
DI
RET
}
}system_enable_interrupts
启用可屏蔽 CPU 中断。
语法
void system_enable_interrupts();返回值
无返回值。
使用条件与限制
启用前先准备好中断源及处理程序。FC 上此操作与 NMI 的启用或禁用独立。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
system_enable_interrupts();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例恢复中断。FC 程序先禁用 APU 帧 IRQ 和 DMC IRQ 源,避免反复响应未确认的 IRQ。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:23
Enable CPU interrupts and return using the inline-assembly function body.
kitaqgb/lib/system.c:43
void system_enable_interrupts()
{
__asm {
EI
RET
}
}system_get_frame
读取帧等待包装函数的 16 位计数器。
语法
u16 system_get_frame();返回值
返回当前计数 0~65535。
使用条件与限制
计数器统计通过此包装函数等待的次数,不自动测量实际经过的硬件帧;以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
result[3]=system_get_frame();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在三次等待后读到三;回绕检查显式设置计数为 65535,确认下次等待后为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:19
Read the software count of completed system_wait_vblank calls.
kitaqgb/lib/system.c:31
u16 system_get_frame()
{
return kq_system_frame;
}system_get_frame8
读取帧计数器低八位,用于短周期重复。
语法
u8 system_get_frame8();返回值
返回低字节 0~255。
使用条件与限制
计数器统计通过此包装函数等待的次数,不自动测量实际经过的硬件帧;以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
result[4]=system_get_frame8();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
三次等待后读取三,16 位计数器回绕时读取零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:21
Return the low byte of the software frame counter for wrapping 8-bit timing.
kitaqgb/lib/system.c:37
u8 system_get_frame8()
{
return (u8)kq_system_frame;
}system_init
将计数器及已注册回调清零,初始化帧管理。
语法
void system_init();返回值
无返回值。
使用条件与限制
计数器统计通过此包装函数等待的次数,不自动测量实际经过的硬件帧;以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
system_init();result[0]=system_get_frame();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
示例在注册回调前确认初始帧编号为零。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:12
Reset the software frame counter and remove any registered callback.
kitaqgb/lib/system.c:7
void system_init()
{
kq_system_frame = 0;
system_vblank_callback = 0;
}system_set_vblank_callback
注册、替换或移除帧等待后调用的函数。
语法
void system_set_vblank_callback(SystemCallback callback);参数
callback要调用的函数;0 移除回调。
返回值
无返回值。
使用条件与限制
这是无参数、无返回值的普通函数指针,不携带存储体编号;可能被调用期间,目标代码须保持映射可用。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
system_set_vblank_callback(on_frame);这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
回调执行三次后注册 0 以禁用。下一次等待将帧数推进到四,回调次数保持三。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:17
Replace the callback used by system_wait_vblank; null disables the callback.
kitaqgb/lib/system.c:25
void system_set_vblank_callback(SystemCallback callback)
{
system_vblank_callback = callback;
}system_wait_vblank
等待下一帧,递增计数器,再调用已注册回调。
语法
void system_wait_vblank();返回值
无返回值。
使用条件与限制
回调在此函数内部同步执行,注册不会安装中断处理程序。计数器仅统计此包装函数的等待次数,不自动测量硬件经过帧数,并以 16 位回绕。
GB 上 LCD 关闭时等待立即返回,但包装函数仍递增计数并调用回调。仅初始化不配置 LCD 或中断源。
请用默认 Legacy ABI 构建完整示例。回调使用函数指针调用,编译器在 --abi=stack 下会以不支持为由拒绝。使用别名也受同一限制。
使用示例
system_wait_vblank();system_wait_vblank();system_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
等待三次调用回调三次,回调最后观察到的计数器为三。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch_services.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch_services.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k实现与源码
kitaqgb/lib/system.h:15
Wait for VBlank, count this wrapper call, then invoke the callback synchronously. The counter wraps at 16 bits and is not an independent hardware frame clock.
kitaqgb/lib/system.c:15
void system_wait_vblank()
{
__wait_vblank();
kq_system_frame++;
if (system_vblank_callback != 0) {
system_vblank_callback();
}
}vram.h — VRAM 队列更新与传输
将延后执行的VRAM写入命令队列初始化为空,并清除溢出标志。不会清屏、上传图块图形或分配硬件VRAM。
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
先用__wait_vblank等待,再调用vram_flush_now。适合批量执行少量屏幕更新。一次等待并不会限制传输量,也不保证整个批次能在一次VBlank内完成。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
vram_clear_queue
丢弃待执行的 VRAM 命令并清除溢出标志,不执行这些写入。
语法
void vram_clear_queue();返回值
无(void)。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
vram_clear_queue();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:41
Discard pending commands and clear the latched overflow flag without writing VRAM.
kitaqgb/lib/vram.c:41
void vram_clear_queue()
{
kq_vram_queue_used = 0;
kq_vram_queue_overflowed = 0;
}vram_flush
先用__wait_vblank等待,再调用vram_flush_now。适合批量执行少量屏幕更新。一次等待并不会限制传输量,也不保证整个批次能在一次VBlank内完成。
语法
void vram_flush();返回值
无(void)。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
VRAM中的目标字节地址(0x8000–0x9FFF),使用执行时选中的存储体。传输地址连续,不在图块映射表行末换行。整个目标范围都必须有效。LCD开启时,安全写入路径可能启用中断,不保留调用前的中断禁用状态。
使用示例
vram_flush(); // One small write through the VBlank/NMI path.
if (vram_get_queue_used()!=0) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:64
Wait for VBlank before executing the queue. Waiting once does not itself limit the queued workload to the available VBlank duration.
kitaqgb/lib/vram.c:148
void vram_flush()
{
__wait_vblank();
vram_flush_now();
}vram_flush_now
按加入顺序执行所有队列命令,然后将使用数置0。此函数本身不等待VBlank。LCD开启时,被调用的安全VRAM内置函数会等待可写时段,但大队列可能跨越多条扫描线或多帧。溢出标志保留。
语法
void vram_flush_now();返回值
无(void)。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
VRAM中的目标字节地址(0x8000–0x9FFF),使用执行时选中的存储体。传输地址连续,不在图块映射表行末换行。整个目标范围都必须有效。LCD开启时,安全写入路径可能启用中断,不保留调用前的中断禁用状态。
使用示例
// Rendering is stopped here. On FC, commit readiness before direct execution.
vram_flush_now();
after_flush=vram_get_queue_used();
if (after_flush!=0) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:61
Execute pending commands in insertion order without waiting for VBlank here. The caller must arrange suitable VRAM access timing and a sufficient transfer budget. Only the used count is cleared; overflow stays latched until an explicit reset.
kitaqgb/lib/vram.c:125
void vram_flush_now()
{
u8 i = 0;
while (i < kq_vram_queue_used) {
VramCommand* cmd = &kq_vram_queue[(__safe_index u8)i];
if (cmd->kind == VRAM_CMD_BG_TILE) {
__settile_xy(cmd->x, cmd->y, cmd->value);
} else if (cmd->kind == VRAM_CMD_BG_RECT) {
__settile_rect(cmd->x, cmd->y, cmd->w, cmd->h, cmd->value);
} else if (cmd->kind == VRAM_CMD_BG_BLOCK) {
__settilemap_rect(cmd->dst, cmd->x, cmd->y, cmd->w, cmd->h, cmd->src);
} else if (cmd->kind == VRAM_CMD_MEMCPY) {
__vram_copy(cmd->dst, cmd->src, cmd->len);
} else if (cmd->kind == VRAM_CMD_MEMSET) {
__vram_fill(cmd->dst, cmd->value, cmd->len);
}
i++;
}
kq_vram_queue_used = 0;
}vram_get_overflowed
读取队列溢出标志但不清除;它可能仍记录着较早的失败。
语法
u8 vram_get_overflowed();返回值
未记录溢出时为0;追加被拒绝后为1,直到显式重置。执行队列不会清除此标志。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
overflow_seen=vram_get_overflowed();
if (overflow_seen!=1) failures++;
// Reading the latch again does not clear it.
if (vram_get_overflowed()!=1) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:73
Read the overflow latch without clearing it.
kitaqgb/lib/vram.c:175
u8 vram_get_overflowed()
{
return kq_vram_queue_overflowed;
}vram_get_queue_capacity
此 API 在本地版中的差异,请参阅 2026-10-03 差异与验证。
返回命令缓冲区的总容量,与当前使用量无关。
语法
u8 vram_get_queue_capacity();返回值
以此处说明的平台单位表示的无符号八位值。读取不会修改或执行队列。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
capacity=vram_get_queue_capacity();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:69
Return the total number of command slots, independent of queue usage. This is command-buffer capacity, not free space in hardware VRAM.
kitaqgb/lib/vram.c:162
u8 vram_get_queue_capacity()
{
return VRAM_QUEUE_MAX;
}vram_get_queue_free
返回命令缓冲区总容量减去当前用量。追加前应确认下一条完整记录或整个批次能否放下;此查询不会预留空间。
语法
u8 vram_get_queue_free();返回值
以此处说明的平台单位表示的无符号八位值。读取不会修改或执行队列。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
available=vram_get_queue_free();
if (used+available!=capacity) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:71
Return the remaining command-slot capacity.
kitaqgb/lib/vram.c:169
u8 vram_get_queue_free()
{
return (u8)(VRAM_QUEUE_MAX - kq_vram_queue_used);
}vram_get_queue_used
返回待执行命令当前占用的命令缓冲区用量,不是将写入显存的数据字节数。
语法
u8 vram_get_queue_used();返回值
以此处说明的平台单位表示的无符号八位值。读取不会修改或执行队列。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
used=vram_get_queue_used();这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:66
Return the number of queued commands, not the number of transfer bytes.
kitaqgb/lib/vram.c:155
u8 vram_get_queue_used()
{
return kq_vram_queue_used;
}vram_init
将延后执行的VRAM写入命令队列初始化为空,并清除溢出标志。不会清屏、上传图块图形或分配硬件VRAM。
语法
void vram_init();返回值
无(void)。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
vram_init();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:39
Start with an empty command queue and a clear overflow flag.
kitaqgb/lib/vram.c:34
void vram_init()
{
kq_vram_queue_used = 0;
kq_vram_queue_overflowed = 0;
}vram_queue_bg_block
将从src读取w × h个图块编号并写入base地图矩形的操作加入队列。源数据每行紧密排列w字节,目标行相隔32字节。命令保留源指针,因此执行前修改源数据会改变最终显示的块。
语法
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);参数
base单图块和矩形命令在执行时根据LCDC位3选择背景映射表。CGB图块编号应使用VRAM存储体0。块传输使用指定的基址,通常为
0x9800或0x9C00。行跨度为32字节,不裁剪坐标。x, y以8×8图块为单位的左上角坐标,不是像素坐标。不执行裁剪。应满足
x + w <= 32;GB的y + h <= 32,FC名称表图块区域的y + h <= 30。单个图块按宽高均为1计算。w, h以无符号八位值指定图块宽高。源块按行排列,共
w * h字节。零尺寸不产生可见写入,但队列存储用量遵循平台的记录规则。src指向可读字节的近指针,保留到执行时。整个执行期间必须保持数据有效且其内存或ROM银行可访问;不保存银行号或数据副本。非空传输需要有效源数据,不可使用生命周期已结束的局部数组。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
单图块和矩形命令在执行时根据LCDC位3选择背景映射表。CGB图块编号应使用VRAM存储体0。块传输使用指定的基址,通常为0x9800或0x9C00。行跨度为32字节,不裁剪坐标。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_bg_block(MAP_BASE,10,4,3,2,block);
block[0]=1; // The queued pointer sees this change when the queue executes.
if (check_result!=1) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:50
Queue a tile-map rectangle while retaining src rather than copying its bytes. Keep the source data and its ROM bank accessible until the queue is flushed.
kitaqgb/lib/vram.c:79
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_BLOCK);
if (cmd == 0) return 0;
cmd->dst = base;
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->src = src;
return 1;
}vram_queue_bg_rect
将使用同一图块编号填充背景地图矩形的操作加入队列。左上角图块坐标为(x, y),大小为w × h个图块。纯色图块会形成实心矩形,有图案的图块则会在每个格子重复图案。
语法
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);参数
x, y以8×8图块为单位的左上角坐标,不是像素坐标。不执行裁剪。应满足
x + w <= 32;GB的y + h <= 32,FC名称表图块区域的y + h <= 30。单个图块按宽高均为1计算。w, h以无符号八位值指定图块宽高。源块按行排列,共
w * h字节。零尺寸不产生可见写入,但队列存储用量遵循平台的记录规则。tile0至255的图块地图字节。应另行加载对应图形并选择合适的图案表或寻址模式。图块编号不是指针或颜色值。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
单图块和矩形命令在执行时根据LCDC位3选择背景映射表。CGB图块编号应使用VRAM存储体0。块传输使用指定的基址,通常为0x9800或0x9C00。行跨度为32字节,不裁剪坐标。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_bg_rect(2,4,4,3,1); // A filled 32x24 rectangle at (16,32).
if (check_result!=1) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:47
Queue a rectangle filled with one tile value; coordinates and dimensions are in tiles.
kitaqgb/lib/vram.c:65
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_RECT);
if (cmd == 0) return 0;
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->value = tile;
return 1;
}vram_queue_bg_tile
将一个向背景(x, y)写入图块编号的命令加入队列。可见图块在队列执行时才改变。此命令写入地图项,不上传图块像素数据,也不改变调色板属性。
语法
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);参数
x, y以8×8图块为单位的左上角坐标,不是像素坐标。不执行裁剪。应满足
x + w <= 32;GB的y + h <= 32,FC名称表图块区域的y + h <= 30。单个图块按宽高均为1计算。tile0至255的图块地图字节。应另行加载对应图形并选择合适的图案表或寻址模式。图块编号不是指针或颜色值。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
单图块和矩形命令在执行时根据LCDC位3选择背景映射表。CGB图块编号应使用VRAM存储体0。块传输使用指定的基址,通常为0x9800或0x9C00。行跨度为32字节,不裁剪坐标。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_bg_tile(2,2,1); // A solid 8x8 tile at pixel (16,16).
if (check_result!=1) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:43
Queue a single background tile write; return zero if no command slot is available.
kitaqgb/lib/vram.c:48
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile)
{
VramCommand* cmd = vram_push(VRAM_CMD_BG_TILE);
if (cmd == 0) return 0;
cmd->x = x;
cmd->y = y;
cmd->value = tile;
return 1;
}vram_queue_memcpy
将从src向dst复制连续len字节的操作加入队列。源和目标均逐字节递增,不使用图块行跨度。可传输地图字节或可写图块图案字节,但不会转换格式或推算每个图块的字节数。
语法
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);参数
dstVRAM中的目标字节地址(
0x8000–0x9FFF),使用执行时选中的存储体。传输地址连续,不在图块映射表行末换行。整个目标范围都必须有效。LCD开启时,安全写入路径可能启用中断,不保留调用前的中断禁用状态。src指向可读字节的近指针,保留到执行时。整个执行期间必须保持数据有效且其内存或ROM银行可访问;不保存银行号或数据副本。非空传输需要有效源数据,不可使用生命周期已结束的局部数组。
len连续字节数,范围0至65535,不是图块数。整个范围应位于有效源数据和可写目标区域内;API不检查边界,也不防止十六位地址回绕。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_memcpy(MAP_BASE+8*32+2,strip,3);
if (check_result!=1) failures++; // Copy three map bytes, not three tile patterns.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:53
Queue a byte transfer and retain the source pointer. The source must remain valid and visible until flush; len is measured in bytes, not tiles.
kitaqgb/lib/vram.c:94
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len)
{
VramCommand* cmd = vram_push(VRAM_CMD_MEMCPY);
if (cmd == 0) return 0;
cmd->dst = dst;
cmd->src = (const u8*)src;
cmd->len = len;
return 1;
}vram_queue_memset
将从dst开始的连续len字节填为单字节value的操作加入队列。重复的是一个字节,不是多字节图块图案,也不会自动插入32字节的地图行跨度。
语法
u8 vram_queue_memset(u16 dst, u8 value, u16 len);参数
dstVRAM中的目标字节地址(
0x8000–0x9FFF),使用执行时选中的存储体。传输地址连续,不在图块映射表行末换行。整个目标范围都必须有效。LCD开启时,安全写入路径可能启用中断,不保留调用前的中断禁用状态。value重复写入0至255的单字节。显示含义取决于目标是图块索引、像素位平面还是其他字节结构。
len连续字节数,范围0至65535,不是图块数。整个范围应位于有效源数据和可写目标区域内;API不检查边界,也不防止十六位地址回绕。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_memset(MAP_BASE+8*32+10,1,5);
if (check_result!=1) failures++; // Five solid tiles form a 40x8 horizontal bar.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:57
Queue a byte fill and return zero if the command queue is full.
kitaqgb/lib/vram.c:111
u8 vram_queue_memset(u16 dst, u8 value, u16 len)
{
VramCommand* cmd = vram_push(VRAM_CMD_MEMSET);
if (cmd == 0) return 0;
cmd->dst = dst;
cmd->value = value;
cmd->len = len;
return 1;
}vram_queue_tile
vram_queue_tile用相同参数直接调用vram_queue_bg_tile并返回相同结果。这是写入单个背景地图项的简写名称,不是精灵操作或图块图形上传。
语法
u8 vram_queue_tile(u8 x, u8 y, u8 tile);参数
x, y以8×8图块为单位的左上角坐标,不是像素坐标。不执行裁剪。应满足
x + w <= 32;GB的y + h <= 32,FC名称表图块区域的y + h <= 30。单个图块按宽高均为1计算。tile0至255的图块地图字节。应另行加载对应图形并选择合适的图案表或寻址模式。图块编号不是指针或颜色值。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
单图块和矩形命令在执行时根据LCDC位3选择背景映射表。CGB图块编号应使用VRAM存储体0。块传输使用指定的基址,通常为0x9800或0x9C00。行跨度为32字节,不裁剪坐标。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_tile(4,2,1); // The short alias draws at pixel (32,16).
if (check_result!=1) failures++;这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:45
Provide the short alias for a queued background tile write.
kitaqgb/lib/vram.c:59
u8 vram_queue_tile(u8 x, u8 y, u8 tile)
{
return vram_queue_bg_tile(x, y, tile);
}vram_queue_tiles
vram_queue_tiles直接调用vram_queue_memcpy。len仍是字节数,不是图块数。保留的源指针、队列记录、返回值和执行时机均相同。
语法
u8 vram_queue_tiles(u16 dst, const void* src, u16 len);参数
dstVRAM中的目标字节地址(
0x8000–0x9FFF),使用执行时选中的存储体。传输地址连续,不在图块映射表行末换行。整个目标范围都必须有效。LCD开启时,安全写入路径可能启用中断,不保留调用前的中断禁用状态。src指向可读字节的近指针,保留到执行时。整个执行期间必须保持数据有效且其内存或ROM银行可访问;不保存银行号或数据副本。非空传输需要有效源数据,不可使用生命周期已结束的局部数组。
len连续字节数,范围0至65535,不是图块数。整个范围应位于有效源数据和可写目标区域内;API不检查边界,也不防止十六位地址回绕。
返回值
本次命令入队成功返回1,没有空槽返回0。失败时设置溢出标志并保留已有命令。之前的溢出本身不会使后续成功入队返回0。
使用条件与限制
GB以命令槽计数,默认容量32。每个入队函数使用一个槽,大传输或零长度传输也一样。VRAM_QUEUE_MAX应在所有编译单元中一致设为1至255。空队列为used=0、free=capacity。
应协调队列构建与执行阶段。不要在中断执行队列时追加、直接修改管理变量或意外覆盖保留的源数据。这些API不提供锁或双缓冲。容量指命令缓冲区,不是硬件VRAM空闲字节数。
使用示例
check_result=vram_queue_tiles(MAP_BASE+8*32+6,strip+1,2);
if (check_result!=1) failures++; // This alias also measures length in bytes.这是一段调用示例。放入完整程序前,请满足所述初始化和缓冲区要求。
预期结果
完整程序演示延迟写入、队列容量管理和溢出恢复。7次绘制入队在GB上使用32个槽中的7个,在FC上使用128字节中的52字节。CAPACITY/USED/FREE分别显示032/007/025或128/052/076。AFTER FLUSH和FAILED CHECKS应为000,OVERFLOW SEEN应为001。程序故意向满队列追加以检查溢出标志;读取或执行不会清除该标志,清空队列才会。FC还检查未提交时直接执行不会改变待处理队列。
图像坐标以左上角为原点,单位为像素。应在(16,16)和(32,16)看到8×8实心正方形,在(16,32)看到32×24实心矩形,在(80,64)看到40×8横条。最后一次VBlank写入在(144,80)添加8×8正方形。被取消的写入位置(144,24)必须保持空白。DMG为白底黑图形,CGB为白底红、蓝、绿图形,FC为黑底红、蓝、绿图形。这些模拟器图像不代表实机验证。
使用三种自制8×8图案区分复制操作:图块1全部填满,2只填左侧四列,3为左对齐的三角形,从顶部1像素逐行加宽至底部8像素。(80,32)处的3×2图块块应为[1,2,3] / [3,0,2],0表示空白。源数据首字节在入队后修改,因此左上角出现实心图块可证明保留的是源指针。(16,64)的复制显示[1,2,3],(48,64)的tiles别名显示[2,3]。三者大小依次为24×16、24×8和16×8像素。
CGB模式下,上方两个正方形、三图案复制和最后通过VBlank写入的正方形为红色;32×24矩形和双图案复制为蓝色;3×2图块块和40×8横条为绿色。文字仍为白底黑字。除形状外,还检查每个绘制像素的颜色。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaqgb\lib\vram.c .\kitaq-docs\samples\api-examples\gb\vram_queue_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm实现与源码
kitaqgb/lib/vram.h:55
Alias the queued byte-copy operation; this does not convert a tile count to bytes.
kitaqgb/lib/vram.c:105
u8 vram_queue_tiles(u16 dst, const void* src, u16 len)
{
return vram_queue_memcpy(dst, src, len);
}wire3d.h — DMG 线框
单色三维线条绘制。提供模型顶点、边和面,以及物体位置、旋转及缩放,按初始化、开始帧、绘制、上传的顺序使用。物理模拟另行处理。角度每周 16 档,模型比例 256 为原比例。
Vec3 保存 x/y/z,Edge 保存顶点索引 a/b,Face 保存三角形索引 a/b/c。Model 的 vertices、edges、faces 指向这些数组,各计数表示数组长度。edge_faces 保存邻接面索引 f0/f1,缺少的面用 FACE_NONE。flags 中的 MODEL_HIDDEN_LINES 请求按面选择边。投影后的有符号面积决定面朝向,因此顶点顺序有意义。
Object 保存模型引用、位置 x/y/z、角度 rx/ry/rz、scale_q8 和 visible。多个物体可共享模型,但绘制不复制数据表,也不自动切换其 ROM 存储体。绘制期间引用数据须保持可读。CGB Object 还包含颜色索引。
96 行配置的 Model 还包含 edge_masks 和 edge_mask_count。可提供按物体偏航角选择的 RAM 中 16 位边掩码表,不使用时将两字段设为零。
Wire3D_BeginFrame
开始绘制一帧并禁用遮挡检测。
语法
void Wire3D_BeginFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:清空暂存像素和遮挡遮罩,禁用遮挡过滤,保留已显示的 VRAM 帧及背景图块待写队列。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:34
WIRE3D_DMG_HEIGHT = 96. Clear staged pixels and occlusion mask and disable occlusion filtering. This leaves the displayed VRAM frame and pending BG tile queue unchanged.
kitaqgb/lib/wire3d_dmg.c:2641
void Wire3DDMG_BeginFrame()
{
w3ddmg_clear_stage_asm();
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 0;
}Wire3D_DrawLine2D
向暂存区绘制含端点的四连通屏幕线段。
语法
void Wire3D_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:向 128×96 暂存区绘制含端点的连通线。DrawScene 中使用五点采样的粗略遮挡拒绝,否则直接绘制。端点须在视口内,VRAM 仅由 EndFrame 更新。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:35
WIRE3D_DMG_HEIGHT = 96. Draw a connected line into the 128x96 stage, including endpoints. During DrawScene, use the coarse five-sample occlusion rejection; otherwise draw directly. Supply viewport coordinates. VRAM is updated only by EndFrame.
kitaqgb/lib/wire3d_dmg.c:2685
void Wire3DDMG_DrawLine2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by)
{
if (w3ddmg_occlusion_active)
{
w3ddmg_line_stage_masked_c(ax, ay, bx, by);
return;
}
w3ddmg_line_x0 = ax;
w3ddmg_line_y0 = ay;
w3ddmg_line_x1 = bx;
w3ddmg_line_y1 = by;
if (ax < 128 && bx < 128 && ay < WIRE3D_DMG_SCREEN_H && by < WIRE3D_DMG_SCREEN_H)
w3ddmg_line_inside_asm();
else
w3ddmg_line_stage_asm();
}Wire3D_DrawLine3D
投影两个世界坐标端点并连接成线。
语法
void Wire3D_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz);参数
ax起点的世界坐标 X 分量。
ay起点的世界坐标 Y 分量。
az起点的世界坐标 Z 分量。
bx终点的世界坐标 X 分量。
by终点的世界坐标 Y 分量。
bz终点的世界坐标 Z 分量。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:投影两个世界端点,仅在两者都通过深度测试时绘制。不求跨界线段与近/远裁剪面的交点,投影端点各自限制在屏幕边界内。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:36
WIRE3D_DMG_HEIGHT = 96. Project both world endpoints and draw only when both pass the depth range. Do not intersect a crossing segment with near/far planes; projected endpoints are independently clamped to the screen edges.
kitaqgb/lib/wire3d_dmg.c:2808
void Wire3DDMG_DrawLine3D(w3ddmg_i16 ax, w3ddmg_i16 ay, w3ddmg_i16 az, w3ddmg_i16 bx, w3ddmg_i16 by, w3ddmg_i16 bz)
{
w3ddmg_u8 sx0;
w3ddmg_u8 sy0;
w3ddmg_u8 sx1;
w3ddmg_u8 sy1;
if (w3ddmg_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3ddmg_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DDMG_DrawLine2D(sx0, sy0, sx1, sy1);
}Wire3D_DrawModel
以指定位置、旋转和原比例绘制顶点/边模型。
语法
void Wire3D_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:用比例 256 调用 DrawModelScaled,绘制未缩放模型。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:37
WIRE3D_DMG_HEIGHT = 96. Draw an unscaled model by delegating to DrawModelScaled with scale 256.
kitaqgb/lib/wire3d_dmg.c:2825
void Wire3DDMG_DrawModel(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
Wire3DDMG_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}Wire3D_DrawModelScaled
缩放模型,按 Y/X/Z 旋转,再平移、投影顶点并绘制边。
语法
void Wire3D_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:缩放最多 24 个顶点,按 Y/X/Z 旋转、平移和投影,然后绘制最多 16 条经偏航掩码及可选正面过滤的边。比例非正时按原比例 256。忽略空模型、顶点或边指针,各引用表须保持可读。缩放乘积和平移结果须在有符号 16 位范围内。跨越深度边界的边直接丢弃,不做裁剪。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:38
WIRE3D_DMG_HEIGHT = 96. Scale, rotate in Y/X/Z order, translate and project at most 24 vertices; then draw up to 16 edges filtered by yaw masks and optional front-facing faces. Nonpositive scale becomes unity (256). Null model/vertex/edge pointers are ignored; referenced tables must remain readable. Keep scale products and translations in signed 16-bit range. Depth-crossing edges are dropped, not clipped.
kitaqgb/lib/wire3d_dmg.c:2965
void Wire3DDMG_DrawModelScaled(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 edge_count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3ddmg_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
if (edge_count > WIRE3D_DMG_MODEL_EDGE_LIMIT) edge_count = WIRE3D_DMG_MODEL_EDGE_LIMIT;
w3ddmg_build_edge_flags(model, edge_count, face_count, rx, ry, rz);
i = 0;
while (i < edge_count)
{
const Wire3DDMG_Edge* e;
e = &model->edges[(__safe_index w3ddmg_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)e->b] &&
w3ddmg_edge_flags[(__safe_index w3ddmg_u8)i])
{
Wire3DDMG_DrawLine2D(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)e->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)e->b]);
}
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3D_DrawScene
从近到远绘制最多八个物体,用较近的面隐藏后续边。
语法
void Wire3D_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count);参数
objects包含位置、旋转、缩放和可见标志的物体数组。
count提供的数组元素数;具体处理上限见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:最多处理前八个物体,按变换后原点深度从近到远排序。绘制可见有效模型,再标记正面包围矩形,以拒绝后续采样点全部被覆盖的线条。这是物体间的近似遮挡,不是深度缓冲。清空遮罩但保留暂存像素。空数组被忽略,物体和引用数据须保持可读。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:39
WIRE3D_DMG_HEIGHT = 96. Process at most the first eight objects, sorted near to far by transformed origin depth. Draw visible valid models, then mark their front-face bounding rectangles to reject fully sampled later lines. This approximates inter-object occlusion, not a depth buffer. It clears its mask but retains existing staged pixels. Null arrays are ignored; objects and their data must remain readable.
kitaqgb/lib/wire3d_dmg.c:2723
void Wire3DDMG_DrawScene(Wire3DDMG_Object* objects, w3ddmg_u8 count)
{
w3ddmg_u8 scene_count;
w3ddmg_u8 i;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3D_DMG_SCENE_OBJECT_LIMIT) scene_count = WIRE3D_DMG_SCENE_OBJECT_LIMIT;
i = 0;
while (i < scene_count)
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = i;
w3ddmg_scene_depth[(__safe_index w3ddmg_u8)i] = w3ddmg_scene_object_depth(&objects[(__safe_index w3ddmg_u8)i]);
i = (w3ddmg_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3ddmg_u8 j;
j = (w3ddmg_u8)(i + 1);
while (j < scene_count)
{
w3ddmg_u8 oi;
w3ddmg_u8 oj;
oi = w3ddmg_scene_order[(__safe_index w3ddmg_u8)i];
oj = w3ddmg_scene_order[(__safe_index w3ddmg_u8)j];
// Put the nearer origin first so its face bounds can suppress later lines.
// The array itself stays in caller order; only the scratch index list is sorted.
if (w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oj] < w3ddmg_scene_depth[(__safe_index w3ddmg_u8)oi])
{
w3ddmg_scene_order[(__safe_index w3ddmg_u8)i] = oj;
w3ddmg_scene_order[(__safe_index w3ddmg_u8)j] = oi;
}
j = (w3ddmg_u8)(j + 1);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_clear_occlusion_mask();
w3ddmg_occlusion_active = 1;
i = 0;
while (i < scene_count)
{
Wire3DDMG_Object* obj;
obj = &objects[(__safe_index w3ddmg_u8)w3ddmg_scene_order[(__safe_index w3ddmg_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
Wire3DDMG_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, (w3ddmg_i8)obj->rx, (w3ddmg_i8)obj->ry, (w3ddmg_i8)obj->rz, obj->scale_q8);
w3ddmg_mark_model_occluder(obj->model);
}
i = (w3ddmg_u8)(i + 1);
}
w3ddmg_occlusion_active = 0;
}Wire3D_EndFrame
向 VRAM 提交背景待写项目及暂存图形。
语法
void Wire3D_EndFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:等待下一次 VBlank,提交背景待写项目,再轮询访问窗口将暂存区复制到 VRAM。复制会消耗暂存像素,可能延续到 VBlank 之外,不是原子换帧。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:42
WIRE3D_DMG_HEIGHT = 96. Wait for the next VBlank, flush pending BG map writes, then copy the stage to VRAM while polling access windows. Copying consumes the staged pixels. The transfer may extend beyond VBlank; this is not an atomic frame swap.
kitaqgb/lib/wire3d_dmg.c:3081
void Wire3DDMG_EndFrame()
{
w3ddmg_wait_vblank_start();
w3ddmg_flush_bg_queue();
w3ddmg_transfer_stage_asm();
}Wire3D_EraseModelFaces
投影模型正面三角形,并擦除内部暂存像素。
语法
void Wire3D_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:对有隐藏线标志及面数据的模型,投影最多 24 个顶点,擦除最多 16 个正面三角形内部的暂存像素。比例非正按原比例 256,跳过跨越深度边界的面。替换共享投影缓存,不更新 VRAM。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3D_Face faces[2]={{0,2,1},{0,3,2}};
Wire3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;plane.edge_masks=0;plane.edge_mask_count=0;
Wire3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3D_DrawLine2D(8,i,32,i);
for(i=24;i<=72;i++) Wire3D_DrawLine2D(40,i,88,i);
Wire3D_EraseTriangle2D(12,12,28,12,12,28);
Wire3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
投影面擦除已填充区域,从而显示保留区域与清除区域的区别。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_faces.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:43
WIRE3D_DMG_HEIGHT = 96. Project up to 24 vertices and erase front-facing triangle interiors from the stage for a model with hidden-line flags and face data. Nonpositive scale means 256 (unity). Up to 16 faces are considered; depth-crossing faces are skipped. This also replaces shared projection caches, but does not update VRAM.
kitaqgb/lib/wire3d_dmg.c:2841
void Wire3DDMG_EraseModelFaces(const Wire3DDMG_Model* model, w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz, w3ddmg_i16 scale_q8)
{
w3ddmg_u8 i;
w3ddmg_u8 count;
w3ddmg_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3D_DMG_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3D_DMG_MODEL_VERTEX_LIMIT) count = WIRE3D_DMG_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DDMG_Vec3* v;
w3ddmg_i16 vx;
w3ddmg_i16 vy;
w3ddmg_i16 vz;
w3ddmg_u8 sx;
w3ddmg_u8 sy;
v = &model->vertices[(__safe_index w3ddmg_u8)i];
vx = (w3ddmg_i16)((v->x * scale_q8) >> 8);
vy = (w3ddmg_i16)((v->y * scale_q8) >> 8);
vz = (w3ddmg_i16)((v->z * scale_q8) >> 8);
w3ddmg_rotate_y(&vx, &vz, (w3ddmg_i8)ry);
w3ddmg_rotate_x(&vy, &vz, (w3ddmg_i8)rx);
w3ddmg_rotate_z(&vx, &vy, (w3ddmg_i8)rz);
vx = (w3ddmg_i16)(vx + x);
vy = (w3ddmg_i16)(vy + y);
vz = (w3ddmg_i16)(vz + z);
if (w3ddmg_project_world(vx, vy, vz, &sx, &sy))
{
w3ddmg_screen_x[(__safe_index w3ddmg_u8)i] = sx;
w3ddmg_screen_y[(__safe_index w3ddmg_u8)i] = sy;
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 1;
}
else
{
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)i] = 0;
}
i = (w3ddmg_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3D_DMG_MODEL_FACE_LIMIT) face_count = WIRE3D_DMG_MODEL_FACE_LIMIT;
w3ddmg_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DDMG_Face* f;
f = &model->faces[(__safe_index w3ddmg_u8)i];
if (w3ddmg_face_visible[(__safe_index w3ddmg_u8)i] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->a] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->b] &&
w3ddmg_screen_visible[(__safe_index w3ddmg_u8)f->c])
{
w3ddmg_clear_triangle(
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->a],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->b],
w3ddmg_screen_x[(__safe_index w3ddmg_u8)f->c],
w3ddmg_screen_y[(__safe_index w3ddmg_u8)f->c]);
}
i = (w3ddmg_u8)(i + 1);
}
}Wire3D_EraseSpan2D
擦除一行暂存像素中包含端点的水平区间。
语法
void Wire3D_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1);参数
y待擦除行的像素 Y 坐标。
x0视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
x1视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:排列 X 端点,擦除包含端点的暂存水平区间。两个 X 均须在 0~127 内,无效 Y 被忽略,不自动裁剪 X。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:44
WIRE3D_DMG_HEIGHT = 96. Order the X endpoints and erase the inclusive staged span. Both endpoints must be in 0..127; invalid Y is ignored. The routine does not clip X for you.
kitaqgb/lib/wire3d_dmg.c:2943
void Wire3DDMG_EraseSpan2D(w3ddmg_u8 y, w3ddmg_u8 x0, w3ddmg_u8 x1)
{
w3ddmg_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
w3ddmg_stage_clear_span(y, x0, x1);
}Wire3D_EraseTriangle2D
使用水平扫描线区间,从暂存区擦除屏幕三角形。
语法
void Wire3D_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
cx顶点 C 的视口像素 X 坐标。
cy顶点 C 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:通过整数扫描线求交擦除暂存三角形。请提供视口坐标,忽略退化三角形,遮挡遮罩和 VRAM 不变。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Erase a screen triangle and a projected model face from separate filled panels.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[4]={{-24,-24,0},{24,-24,0},{24,24,0},{-24,24,0}};
__prg_rom Wire3D_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom Wire3D_Face faces[2]={{0,2,1},{0,3,2}};
Wire3D_Model plane;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
plane.vertices=vertices;plane.edges=edges;plane.faces=faces;plane.edge_faces=0;
plane.vertex_count=4;plane.edge_count=4;plane.face_count=2;plane.flags=1;plane.edge_masks=0;plane.edge_mask_count=0;
Wire3D_BeginFrame();
// Separate panels show a direct screen triangle and a projected square face.
for(i=8;i<=32;i++) Wire3D_DrawLine2D(8,i,32,i);
for(i=24;i<=72;i++) Wire3D_DrawLine2D(40,i,88,i);
Wire3D_EraseTriangle2D(12,12,28,12,12,28);
Wire3D_EraseModelFaces(&plane,0,0,64,0,0,0,256);
Wire3D_EndFrame(); result[7]=0xA55A; while(1) {}
}预期结果
投影面擦除已填充区域,从而显示保留区域与清除区域的区别。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_faces.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:45
WIRE3D_DMG_HEIGHT = 96. Erase a triangle from staged pixels using integer scanline intersections. Supply viewport coordinates. Degenerate triangles are ignored; the occlusion mask and VRAM are unchanged.
kitaqgb/lib/wire3d_dmg.c:2933
void Wire3DDMG_EraseTriangle2D(w3ddmg_u8 ax, w3ddmg_u8 ay, w3ddmg_u8 bx, w3ddmg_u8 by, w3ddmg_u8 cx, w3ddmg_u8 cy)
{
w3ddmg_clear_triangle(ax, ay, bx, by, cx, cy);
}Wire3D_Init
为单色线框视口初始化 LCD、图块和背景图。
语法
void Wire3D_Init();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:等待 VBlank 后关闭 LCD,清空 6144 字节图块和背景图,载入 HUD 及采用有符号图块编号的 128×96 视口,再以零滚动和 BGP=0xB4 开启背景。重置相机、背景队列及暂存区。预留 0xD000~0xDFFF 并保持 WRAM 映射,绘制前还须调用 BeginFrame 初始化遮挡状态。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:47
WIRE3D_DMG_HEIGHT = 96. Take over the LCD: wait for VBlank, switch it off, clear 6144 tile bytes and the BG map, install HUD tiles and the 128x96 signed-tile viewport, then enable BG display with scroll zero and BGP=0xB4. Reset camera, BG queue and stage. Reserve 0xD000..0xDFFF for staging and retain its WRAM bank mapping. Call BeginFrame before drawing to initialize the occlusion state as well.
kitaqgb/lib/wire3d_dmg.c:2572
void Wire3DDMG_Init()
{
w3ddmg_u8 row;
w3ddmg_u8 col;
w3ddmg_u8 tid;
w3ddmg_u16 off;
w3ddmg_wait_vblank_start();
w3ddmg_reg_lcdc = 0;
w3ddmg_clear_vram_asm();
w3ddmg_load_hud_tiles();
w3ddmg_fill_bg_map_asm();
row = 0;
tid = 0x90;
while (row < WIRE3D_DMG_TILE_H)
{
off = (w3ddmg_u16)((w3ddmg_u16)row << 5);
col = 0;
while (col < WIRE3D_DMG_TILE_W)
{
w3ddmg_bg_map_9800[(w3ddmg_u16)(off + (w3ddmg_u16)col)] = tid;
tid = (w3ddmg_u8)(tid + WIRE3D_DMG_TILE_H);
col = (w3ddmg_u8)(col + 1);
}
row = (w3ddmg_u8)(row + 1);
tid = (w3ddmg_u8)(0x90 + row);
}
w3ddmg_reg_scx = 0;
w3ddmg_reg_scy = 0;
w3ddmg_reg_bgp = 0xB4;
w3ddmg_reg_lcdc = 0x81;
w3ddmg_bgq_count = 0;
Wire3DDMG_SetCamera(0, 0, 0, 0, 0, 0);
w3ddmg_clear_stage_asm();
}Wire3D_ProjectPoint
通过相机将世界坐标点投影为视口坐标。
语法
w3ddmg_u8 Wire3D_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy);参数
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
sx接收投影 X 坐标的有效可写 u8 指针。
sy接收投影 Y 坐标的有效可写 u8 指针。
返回值
投影成功返回 1,深度被拒绝返回 0。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:通过当前相机投影世界点。深度被拒绝返回零,输出不变;成功写入限制在 128×96 内的坐标。输出指针须有效。坐标限制不等于几何裁剪。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:50
WIRE3D_DMG_HEIGHT = 96. Project a world point with the current camera. Return zero for rejected depth, leaving outputs unchanged; success writes clamped 128x96 coordinates. Output pointers must be valid. Screen clamping is not geometric clipping.
kitaqgb/lib/wire3d_dmg.c:549
w3ddmg_u8 Wire3DDMG_ProjectPoint(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_u8* sx, w3ddmg_u8* sy)
{
return w3ddmg_project_world(x, y, z, sx, sy);
}Wire3D_PutBgTile
为一个背景图格排队一次图块编号写入。
语法
void Wire3D_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile);参数
x32×32 背景图中的图块 X 坐标。
y32×32 背景图中的图块 Y 坐标。
tile背景图格的八位图块编号。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:为 0x9800 的 32×32 背景图排队一次写入,交由 EndFrame 提交。无效坐标和超过 48 条待写上限的提交会被静默忽略。此队列独立于通用 vram 库队列。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:51
WIRE3D_DMG_HEIGHT = 96. Queue a tile write to the 32x32 map at 0x9800 for EndFrame. Invalid map coordinates and submissions beyond 48 pending writes are silently ignored. This queue is separate from the general vram library queue.
kitaqgb/lib/wire3d_dmg.c:582
void Wire3DDMG_PutBgTile(w3ddmg_u8 x, w3ddmg_u8 y, w3ddmg_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3ddmg_bgq_count >= WIRE3D_DMG_BG_QUEUE_LIMIT) return;
w3ddmg_bgq_x[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = x;
w3ddmg_bgq_y[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = y;
w3ddmg_bgq_tile[(__safe_index w3ddmg_u8)w3ddmg_bgq_count] = tile;
w3ddmg_bgq_count = (w3ddmg_u8)(w3ddmg_bgq_count + 1);
}Wire3D_SelectEdgeMask
根据物体偏航角选择预计算的边可见掩码。
语法
w3ddmg_u16 Wire3D_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
返回值
16 位边可见掩码;未提供表时为 0xFFFF。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:根据物体偏航角低四位选择预计算掩码,忽略俯仰、滚转及相机朝向。使用 16、8、4、2 档;其他正数量选择条目零,数量大于等于 16 使用前 16 项。缺少数据返回 0xFFFF,当前数据指针映射须能读取表。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Compare direct model scaling with an object-array scene.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
__prg_rom Wire3D_Vec3 vertices[8]={{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}};
__prg_rom Wire3D_Edge edges[12]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
Wire3D_Model cube;
Wire3D_Object objects[2];
u16 masks[2];
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
cube.vertices=vertices;cube.edges=edges;cube.faces=0;cube.edge_faces=0;
cube.vertex_count=8;cube.edge_count=12;cube.face_count=0;cube.flags=0;
cube.edge_masks=0; cube.edge_mask_count=0;
Wire3D_BeginFrame();
// Upper pair: direct model calls at unity and half scale.
Wire3D_DrawModel(&cube,-32,32,96,0,0,0);
Wire3D_DrawModelScaled(&cube,32,32,96,0,0,0,128);
// Lower pair: the caller owns both object records and the model tables.
for(i=0;i<2;i++) {
objects[i].model=&cube;objects[i].x=-32+(s16)i*64;
objects[i].y=-32;objects[i].z=96;
objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256-(s16)i*128;objects[i].visible=1;
}
Wire3D_DrawScene(objects,2);
// Numeric checks cover the operation supplied by this viewport profile.
masks[0]=3;masks[1]=12;cube.edge_masks=masks; cube.edge_mask_count=2;
result[1]=Wire3D_SelectEdgeMask(&cube,0,0,0);
result[2]=Wire3D_SelectEdgeMask(&cube,0,8,0);
Wire3D_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
上方显示原比例和半比例模型,下方用物体数组绘制相同比例的场景,展示投影与缩放。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_models.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:52
WIRE3D_DMG_HEIGHT = 96. Select a precomputed mask from the low four bits of object yaw; pitch, roll and camera orientation are ignored. Use 16, 8, 4 or 2 bins; other positive counts select entry zero (counts >=16 use the first 16). Missing data returns 0xFFFF. Keep the mask table readable through the current data-pointer mapping.
kitaqgb/lib/wire3d_dmg.c:1109
w3ddmg_u16 Wire3DDMG_SelectEdgeMask(const Wire3DDMG_Model* model, w3ddmg_i8 rx, w3ddmg_i8 ry, w3ddmg_i8 rz)
{
w3ddmg_u8 count;
w3ddmg_u8 bin;
if (model == 0) return 0xFFFF;
if (model->edge_masks == 0) return 0xFFFF;
count = model->edge_mask_count;
if (count == 0) return 0xFFFF;
(void)rx;
(void)rz;
bin = (w3ddmg_u8)(((w3ddmg_u8)ry) & 15);
if (count >= 16) return model->edge_masks[(__safe_index w3ddmg_u8)bin];
if (count == 8) bin = (w3ddmg_u8)(bin >> 1);
else if (count == 4) bin = (w3ddmg_u8)(bin >> 2);
else if (count == 2) bin = (w3ddmg_u8)(bin >> 3);
else bin = 0;
if (bin >= count) bin = (w3ddmg_u8)(count - 1);
return model->edge_masks[(__safe_index w3ddmg_u8)bin];
}Wire3D_SetCamera
保存后续投影使用的相机位置与朝向。
语法
void Wire3D_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll);参数
x相机的世界坐标 X 分量。
y相机的世界坐标 Y 分量。
z相机的世界坐标 Z 分量。
pitch绕 X 轴旋转,一周为 16 档。
yaw绕 Y 轴旋转,一周为 16 档。
roll绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:保存后续绘制使用的相机位置和角度,一周 16 档。不重新投影已有图形,也不检查输入范围。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:53
WIRE3D_DMG_HEIGHT = 96. Store camera position and angles for later draws; a full turn is 16 angle steps. No existing drawing is reprojected and no input range validation occurs.
kitaqgb/lib/wire3d_dmg.c:2659
void Wire3DDMG_SetCamera(w3ddmg_i16 x, w3ddmg_i16 y, w3ddmg_i16 z, w3ddmg_i8 pitch, w3ddmg_i8 yaw, w3ddmg_i8 roll)
{
w3ddmg_cam_x = x;
w3ddmg_cam_y = y;
w3ddmg_cam_z = z;
w3ddmg_cam_pitch = pitch;
w3ddmg_cam_yaw = yaw;
w3ddmg_cam_roll = roll;
}Wire3D_SetPalette
写入单色背景调色板寄存器 BGP 的四阶灰度映射。
语法
void Wire3D_SetPalette(w3ddmg_u8 bgp);参数
bgp包含四个两位灰度分配字段的 BGP 寄存器值。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
WIRE3D_DMG_HEIGHT=96:立即写入 DMG BGP 原始调色板编码,不进行帧同步。
本例使用 128×96 配置,视口起点为屏幕 (0,0)。预留 WRAM D000~DFFF,在 CGB 上也须保持其存储体映射稳定。共享状态不可重入。
使用示例
#pragma bank 0
// Draw a frame with a gap, a projected diagonal, and a copied corner tile.
// The selected 96-line profile determines the stage layout and camera center.
#define WIRE3D_DMG_HEIGHT 96
#include "wire3d.h"
__location(0xC110) u16 result[8];
__location(0xFF47) u8 sample_bgp;
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4F) u8 sample_vbk;
__location(0xFF68) u8 sample_bgpi;
__location(0xFF69) u8 sample_bgpd;
u8 projected_x;
u8 projected_y;
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3D_Init();
// A CGB-compatible ROM also needs CGB palette and tile-map attributes.
if(__cgb_is_cgb()) {
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0; sample_vbk=1;
__vram_memset_unsafe(0x9800,0,1024); sample_vbk=0;
sample_bgpi=0x80; sample_bgpd=255; sample_bgpd=127;
for(i=0;i<6;i++) sample_bgpd=0;
sample_lcdc=0x81;
}
Wire3D_SetPalette(0xFC); // White background; every nonzero shade is black.
result[0]=sample_bgp;
Wire3D_SetCamera(8,0,0,0,0,0);
result[1]=Wire3D_ProjectPoint(0,0,96,&projected_x,&projected_y);
result[2]=projected_x; result[3]=projected_y;
projected_x=91; projected_y=92;
result[4]=Wire3D_ProjectPoint(0,0,7,&projected_x,&projected_y);
result[5]=projected_x; result[6]=projected_y;
Wire3D_SetCamera(0,0,0,0,0,0);
Wire3D_BeginFrame();
Wire3D_DrawLine2D(8,8,56,8);
Wire3D_DrawLine2D(56,8,56,40);
Wire3D_DrawLine2D(56,40,8,40);
Wire3D_DrawLine2D(8,40,8,8);
Wire3D_EraseSpan2D(8,40,24); // Inclusive endpoints are ordered internally.
Wire3D_DrawLine3D(-24,-24,96,24,24,96);
// Copy the viewport's top-left frame corner to a separate map cell.
Wire3D_PutBgTile(18,1,157);
Wire3D_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内展示直线、点、擦除和投影。左上边框的上边中段被擦除,中心附近出现投影对角线。投影被拒绝时,输出保持 91 和 92。


构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_wire3d_raster.c .\kitaqgb\lib\wire3d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_wire3d_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d.h:54
WIRE3D_DMG_HEIGHT = 96. Write the raw DMG BGP palette encoding immediately; no frame synchronization occurs.
kitaqgb/lib/wire3d_dmg.c:597
void Wire3DDMG_SetPalette(w3ddmg_u8 bgp)
{
w3ddmg_reg_bgp = bgp;
}wire3d_cgb.h — CGB 彩色线框
使用快速渲染路径
普通 128×96 渲染器增量更新绘图地址。无遮挡线每条只选择一次颜色位平面,将像素计数和误差保存在 CPU 寄存器中。有遮挡线也每条选择一次颜色位平面,同时增量更新绘图及掩码地址。每像素检查掩码位,仅未覆盖时写入。160×144 渲染器和 FastMap 各有专用路径。0、90、180、270 度旋转先限制输入,再交换坐标并改变符号。整体游戏性能还取决于顶点数、边长、遮挡和 VRAM 传送。应分别测量各操作与完成一幅显示图像的时间。
CGB 彩色线框。128×96 像素渲染器、160×144 图块分配器和轮廓图块 FastMap 各有独立的初始化及传输顺序。调用方持有模型顶点、边和三角面,本模块不模拟物理。
Vec3 保存 x/y/z,Edge 保存顶点索引 a/b,Face 保存三角形索引 a/b/c。Model 的 vertices、edges、faces 指向这些数组,各计数表示数组长度。edge_faces 保存邻接面索引 f0/f1,缺少的面用 FACE_NONE。flags 中的 MODEL_HIDDEN_LINES 请求按面选择边。投影后的有符号面积决定面朝向,因此顶点顺序有意义。
Object 保存模型引用、位置 x/y/z、角度 rx/ry/rz、scale_q8 和 visible。多个物体可共享模型,但绘制不复制数据表,也不自动切换其 ROM 存储体。绘制期间引用数据须保持可读。CGB Object 还包含颜色索引。
FastCube 的 x/y 是相对屏幕中心的偏移,z 选择尺寸或绘制顺序。FastCubes 忽略角度,FastCubeFrame 则用 rx/ry/rz 选择四种贴片变体。这些简化显示与通过相机投影的 DrawModel 用途不同。
Wire3DCGB_BeginFrame
在所选渲染模式下开始一个空白帧。
语法
void Wire3DCGB_BeginFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
普通模式下选择整块暂存区传输,清空所有暂存像素和位图遮罩,重置遮罩边界并禁用遮挡。全屏模式下改为重置该模式的分配器和扫描线区间。排队中的背景写入仍保留。需要完整运行时。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:122
kitaqgb/lib/wire3d_cgb.c:8154
void Wire3DCGB_BeginFrame()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 1;
w3dcgb_clear_stage_asm();
w3dcgb_clear_occlusion_mask_asm();
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_BeginFrameFast
开始清空的新帧,并将之前的稀疏范围失效标记扩展到整个视口。
语法
void Wire3DCGB_BeginFrameFast();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
执行与 BeginFrame 相同的普通/全屏帧重置,但先将前两次稀疏历史范围都标记为完整的 192 图块视口,防止后续稀疏上传误用过期的存储体内容。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:123
kitaqgb/lib/wire3d_cgb.c:8175
void Wire3DCGB_BeginFrameFast()
{
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 1;
w3dcgb_clear_stage_asm();
w3dcgb_clear_occlusion_mask_asm();
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_BeginFrameSparse
开始一个采用稀疏范围传输的帧。
语法
void Wire3DCGB_BeginFrameSparse();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
选择稀疏传输并清空整个 128×96 暂存区,避免待更新范围扩大时露出旧像素。重置遮罩边界并禁用遮挡;PAINTER_ONLY 构建跳过未使用的位图遮罩清除。全屏模式转交其自身分配器重置。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:124
kitaqgb/lib/wire3d_cgb.c:8200
void Wire3DCGB_BeginFrameSparse()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_begin_asm();
return;
}
w3dcgb_full_frame_transfer = 0;
w3dcgb_dirty_count = 0;
/* One ASM pass is faster than repeated range-growth clears and guarantees
that newly exposed dirty tiles can never copy stale stage pixels. */
w3dcgb_clear_stage_asm();
#ifndef WIRE3DCGB_PAINTER_ONLY
/* A Z-sorted painter erases silhouettes directly in the stage and never
reads the separate front-to-back coverage mask. */
w3dcgb_clear_occlusion_mask_asm();
#endif
w3dcgb_occ_min_x = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
w3dcgb_occ_min_y = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
w3dcgb_occ_max_x = 0;
w3dcgb_occ_max_y = 0;
w3dcgb_occlusion_active = 0;
}Wire3DCGB_ClearFrameTiles
清空显示中的 128×96 帧图块及暂存区。
语法
void Wire3DCGB_ClearFrameTiles();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
用于 128×96 渲染器:等待 VBlank,关闭 LCD,清空暂存区及两个存储体的帧图块区域,重置背景属性,再以 LCDC 0x81 开启显示。清除待更新标志和计数,但不清除稀疏传输的最小/最大历史范围。不重置 full_mode,不能用作全屏模式的重新初始化。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:121
kitaqgb/lib/wire3d_cgb.c:8240
void Wire3DCGB_ClearFrameTiles()
{
w3dcgb_u8 i;
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_stage_asm();
w3dcgb_clear_frame_tiles_vram_asm();
w3dcgb_fill_attr_map_asm();
w3dcgb_reg_lcdc = 0x81;
i = 0;
while (i < W3DCGB_DIRTY_TILE_LIMIT)
{
w3dcgb_dirty_flags[(__safe_index w3dcgb_u8)i] = 0;
w3dcgb_prev_dirty_flags[(__safe_index w3dcgb_u8)i] = 0;
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
}Wire3DCGB_ClearOcclusionMask
清空遮挡位图或全屏扫描线区间历史。
语法
void Wire3DCGB_ClearOcclusionMask();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
full_mode 非零时清空全屏扫描线区间历史,否则清空 128×96 位图遮罩及边界。不擦除显示或暂存像素,也不改变遮挡检测的启用状态。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形遮罩隐藏红色扫描线的中间部分,紧凑轮廓遮罩隐藏蓝色线条;绿色轮廓标出遮挡区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:148
kitaqgb/lib/wire3d_cgb.c:1962
void Wire3DCGB_ClearOcclusionMask()
{
if (w3dcgb_full_mode != 0) w3dcgb_full_clear_spans_asm();
else w3dcgb_clear_occlusion_mask();
}Wire3DCGB_ClearSparseStageFast
清空整个暂存区,将完整 128×96 视口标记为待上传。
语法
void Wire3DCGB_ClearSparseStageFast();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
清空存储体 2 的整个暂存区,并将完整 128×96 视口标记为待更新。虽然名称含 Fast,此操作并非局部清除,也不保留暂存像素;它不清除遮挡状态,也不选择传输模式。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:125
kitaqgb/lib/wire3d_cgb.c:8227
void Wire3DCGB_ClearSparseStageFast()
{
w3dcgb_clear_stage_asm();
w3dcgb_mark_dirty_rect(0, 0,
(w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1),
(w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
}Wire3DCGB_DrawEdgeList2D
用预投影的带符号字节顶点偏移绘制最多 64 条边。
语法
void Wire3DCGB_DrawEdgeList2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count, const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y, w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);参数
edges每条边的顶点索引,格式为声明的结构体或字节对。
edge_count边数。
vertex_x预投影顶点的 X 偏移数组。
vertex_y预投影顶点的 Y 偏移数组。
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
使用 (cx,cy) 周围的带符号字节偏移绘制最多 64 条索引边。空数组被忽略,索引须指向可读顶点。平移后端点须在字节转换前位于视口内,否则会回绕。按模式分派并保留原颜色,不检查顶点数量。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:183
kitaqgb/lib/wire3d_cgb.c:8524
void Wire3DCGB_DrawEdgeList2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 old_color;
if (edges == 0) return;
if (vertex_x == 0) return;
if (vertex_y == 0) return;
if (edge_count > WIRE3DCGB_MODEL_EDGE_LIMIT) edge_count = WIRE3DCGB_MODEL_EDGE_LIMIT;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
if (w3dcgb_full_mode == 0)
{
w3dcgb_edge_list_ptr = edges;
w3dcgb_edge_x_ptr = vertex_x;
w3dcgb_edge_y_ptr = vertex_y;
w3dcgb_edge_remaining = edge_count;
w3dcgb_edge_center_x = x;
w3dcgb_edge_center_y = y;
w3dcgb_edge_list_asm();
w3dcgb_line_color = old_color;
return;
}
i = 0;
while (i < edge_count)
{
a = edges[(__safe_index w3dcgb_u8)i].a;
b = edges[(__safe_index w3dcgb_u8)i].b;
w3dcgb_line_x0 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_y0 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_x1 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)b]);
w3dcgb_line_y1 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)b]);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawEdgeListClipped2D
验证带符号投影顶点和边,再裁剪、绘制边列表。
语法
void Wire3DCGB_DrawEdgeListClipped2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count, const w3dcgb_i16* vx, const w3dcgb_i16* vy, w3dcgb_u8 vertex_count, w3dcgb_i16 cx, w3dcgb_i16 cy, w3dcgb_u8 color);参数
edges每条边的顶点索引,格式为声明的结构体或字节对。
edge_count边数。
vx裁剪前的带符号 16 位 X 偏移数组。
vy裁剪前的带符号 16 位 Y 偏移数组。
vertex_count顶点数组的元素数。
cx中心的视口像素 X 坐标。
cy中心的视口像素 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
绘制前验证指针、1~24 个顶点、最多 64 条边、所有索引,以及 ±2047 内的坐标和中心。完全可见的几何使用带符号字节临时区及快速索引路径,其余边使用带符号线段裁剪。即使各输入单独有效,平移后的端点超出 ±2047 仍可能被拒绝。会替换共享屏幕临时区,并通过调用的线条 API 保留原颜色。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:138
kitaqgb/lib/wire3d_cgb.c:9806
void Wire3DCGB_DrawEdgeListClipped2D(const Wire3DCGB_Edge* edges, w3dcgb_u8 edge_count,
const w3dcgb_i16* vx, const w3dcgb_i16* vy, w3dcgb_u8 vertex_count,
w3dcgb_i16 cx, w3dcgb_i16 cy, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 inside;
w3dcgb_i16 x;
w3dcgb_i16 y;
w3dcgb_i16 min_x;
w3dcgb_i16 min_y;
w3dcgb_i16 max_x;
w3dcgb_i16 max_y;
if ((edges == 0) || (vx == 0) || (vy == 0)) return;
if ((cx < -2047) || (cx > 2047) || (cy < -2047) || (cy > 2047)) return;
if ((vertex_count == 0) || (vertex_count > WIRE3DCGB_MODEL_VERTEX_LIMIT) ||
(edge_count > WIRE3DCGB_MODEL_EDGE_LIMIT)) return;
i = 0;
while (i < edge_count)
{
if ((edges[(__safe_index w3dcgb_u8)i].a >= vertex_count) ||
(edges[(__safe_index w3dcgb_u8)i].b >= vertex_count)) return;
i = (w3dcgb_u8)(i + 1);
}
min_x = 32767;
min_y = 32767;
max_x = -32767;
max_y = -32767;
inside = 1;
i = 0;
while (i < vertex_count)
{
if ((vx[(__safe_index w3dcgb_u8)i] < -2047) ||
(vx[(__safe_index w3dcgb_u8)i] > 2047) ||
(vy[(__safe_index w3dcgb_u8)i] < -2047) ||
(vy[(__safe_index w3dcgb_u8)i] > 2047)) return;
x = (w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)i]);
y = (w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)i]);
if (x < min_x) min_x = x;
if (x > max_x) max_x = x;
if (y < min_y) min_y = y;
if (y > max_y) max_y = y;
/* Reuse projected-vertex scratch; drawing is non-reentrant. */
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = (w3dcgb_u8)(x - 64);
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = (w3dcgb_u8)(y - 48);
i = (w3dcgb_u8)(i + 1);
}
if ((w3dcgb_clip_outcode(min_x, min_y) | w3dcgb_clip_outcode(max_x, max_y)) != 0) inside = 0;
if (inside != 0)
{
if (w3dcgb_full_mode == 0) w3dcgb_mark_dirty_rect((w3dcgb_u8)min_x,
(w3dcgb_u8)min_y, (w3dcgb_u8)max_x, (w3dcgb_u8)max_y);
Wire3DCGB_DrawEdgeList2D(edges, edge_count, (const w3dcgb_i8*)w3dcgb_screen_x,
(const w3dcgb_i8*)w3dcgb_screen_y, 64, 48, color);
return;
}
i = 0;
while (i < edge_count)
{
a = edges[(__safe_index w3dcgb_u8)i].a;
b = edges[(__safe_index w3dcgb_u8)i].b;
Wire3DCGB_DrawLineClipped2D((w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)a]),
(w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)a]),
(w3dcgb_i16)(cx + vx[(__safe_index w3dcgb_u8)b]),
(w3dcgb_i16)(cy + vy[(__safe_index w3dcgb_u8)b]), color);
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_DrawFastCubeFrame
替换上一帧立方体贴片,上传最多三个贴片及状态指示。
语法
void Wire3DCGB_DrawFastCubeFrame(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);参数
cubes用于快速立方体绘制的位置、角度和颜色数组。
count提供的数组元素数;具体处理上限见本函数说明。
hidden_enabled零表示包含隐藏边,非零表示采用简化的隐藏边去除。
selected选中物体在原数组中的索引;贴片 API 用它选择状态指示格。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
准备背景图,擦除之前的边界和状态格,按原始 Z 从远到近排列最多三个立方体贴片,再于 VBlank 绘制并上传编号和属性。不改变调用方数组。非空数组且 count 为零时清除旧贴片,空指针则完全不处理。selected & 3 只选择状态格,不为立方体高亮。须已载入立方体贴片、使用完整运行时并保持 FastMap WRAM 映射一致;本函数不负责初始化这些条件。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// A complete cube-stamp frame removes the preceding stamp's old map cells.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cube[1];
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors();
cube[0].x=-24;cube[0].y=0;cube[0].z=96;
cube[0].rx=0;cube[0].ry=0;cube[0].rz=0;cube[0].color=1;
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_DrawFastCubeFrame(cube,1,1,0);
cube[0].x=24;
Wire3DCGB_DrawFastCubeFrame(cube,1,1,0);
sample_svbk=bank;result[0]=cube[0].x;result[7]=0xA55A;
while(1) {}
}预期结果
旧立方体贴片消失,一个红色 32×32 立方体从 (72,32) 开始显示,顶部有绿色状态指示。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_stamp.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_stamp.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:158
kitaqgb/lib/wire3d_cgb.c:10800
void Wire3DCGB_DrawFastCubeFrame(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 i;
w3dcgb_u8 scene_count;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 c;
w3dcgb_u8 x;
if (cubes == 0) return;
scene_count = count;
if (scene_count > 3) scene_count = 3;
w3dcgb_fast_map_prepare();
i = 0;
while (i < w3dcgb_fast_prev_count)
{
w3dcgb_fast_map_clear_rect(
w3dcgb_fast_prev_x[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_y[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_w[(__safe_index w3dcgb_u8)i],
w3dcgb_fast_prev_h[(__safe_index w3dcgb_u8)i]);
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_fast_map_clear_rect(0, 0, 4, 1);
w3dcgb_fast_map_clear_rect(14, 0, 2, 1);
if (scene_count == 0)
{
w3dcgb_fast_prev_count = 0;
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
return;
}
a = 0;
b = 1;
c = 2;
if (scene_count == 1)
{
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
w3dcgb_fast_prev_count = 1;
}
else if (scene_count == 2)
{
if (cubes[0].z < cubes[1].z)
{
w3dcgb_fast_map_draw_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1), 1);
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
}
else
{
w3dcgb_fast_map_draw_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0), 0);
w3dcgb_fast_map_draw_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1), 1);
}
w3dcgb_fast_prev_count = 2;
}
else
{
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
if (cubes[b].z < cubes[c].z)
{
w3dcgb_u8 t;
t = b;
b = c;
c = t;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
w3dcgb_fast_map_draw_cube_one(&cubes[a], hidden_enabled, (w3dcgb_u8)(selected == a), a);
w3dcgb_fast_map_draw_cube_one(&cubes[b], hidden_enabled, (w3dcgb_u8)(selected == b), b);
w3dcgb_fast_map_draw_cube_one(&cubes[c], hidden_enabled, (w3dcgb_u8)(selected == c), c);
w3dcgb_fast_prev_count = 3;
}
x = (w3dcgb_u8)(selected & 3);
w3dcgb_fast_map_or_cell((w3dcgb_i16)x, (w3dcgb_i16)0, WIRE3DCGB_COLOR_HIGH, 15);
if (hidden_enabled != 0) w3dcgb_fast_map_or_cell((w3dcgb_i16)15, (w3dcgb_i16)0, WIRE3DCGB_COLOR_HIGH, 15);
else w3dcgb_fast_map_or_cell((w3dcgb_i16)14, (w3dcgb_i16)0, WIRE3DCGB_COLOR_LOW, 15);
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
}Wire3DCGB_DrawFastCubes
按深度决定尺寸,将最多三个立方体绘制为二维轮廓。
语法
void Wire3DCGB_DrawFastCubes(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);参数
cubes用于快速立方体绘制的位置、角度和颜色数组。
count提供的数组元素数;具体处理上限见本函数说明。
hidden_enabled零表示包含隐藏边,非零表示采用简化的隐藏边去除。
selected选中物体在原数组中的索引;贴片 API 用它选择状态指示格。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
按原始 Z 从远到近绘制最多三个二维近似立方体,不改变调用方数组顺序。忽略空指针或空输入。selected 为原数组索引。不使用相机或旋转,也不恢复颜色。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Fast cube approximations use screen offsets and depth size bands, not a camera.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cubes[2];
void main() {
u8 i;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(i=0;i<2;i++){cubes[i].rx=0;cubes[i].ry=0;cubes[i].rz=0;}
cubes[0].x=-32;cubes[0].y=-8;cubes[0].z=90;cubes[0].color=1;
cubes[1].x=32;cubes[1].y=8;cubes[1].z=160;cubes[1].color=2;
Wire3DCGB_SetCamera(100,0,0,4,4,4); // FastCubes intentionally ignores this.
Wire3DCGB_DrawFastCubes(cubes,2,1,0);
Wire3DCGB_DrawFastStatus(1,0);
result[0]=Wire3DCGB_GetLineColor();
result[1]=cubes[0].z;result[2]=cubes[1].z;
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
左侧较近的红色立方体大于右侧较远的蓝色立方体。绿色十字标记选中的红色立方体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_cubes.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_cubes.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:156
kitaqgb/lib/wire3d_cgb.c:8765
void Wire3DCGB_DrawFastCubes(Wire3DCGB_FastCube* cubes, w3dcgb_u8 count, w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 scene_count;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 c;
w3dcgb_u8 x;
if (cubes == 0) return;
scene_count = count;
if (scene_count == 0) return;
if (scene_count > 3) scene_count = 3;
a = 0;
b = 1;
c = 2;
if (scene_count == 1)
{
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
return;
}
if (scene_count == 2)
{
if (cubes[0].z < cubes[1].z)
{
w3dcgb_draw_fast_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1));
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
}
else
{
w3dcgb_draw_fast_cube_one(&cubes[0], hidden_enabled, (w3dcgb_u8)(selected == 0));
w3dcgb_draw_fast_cube_one(&cubes[1], hidden_enabled, (w3dcgb_u8)(selected == 1));
}
return;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
if (cubes[b].z < cubes[c].z)
{
w3dcgb_u8 t;
t = b;
b = c;
c = t;
}
if (cubes[a].z < cubes[b].z)
{
w3dcgb_u8 t;
t = a;
a = b;
b = t;
}
w3dcgb_draw_fast_cube_one(&cubes[a], hidden_enabled, (w3dcgb_u8)(selected == a));
w3dcgb_draw_fast_cube_one(&cubes[b], hidden_enabled, (w3dcgb_u8)(selected == b));
w3dcgb_draw_fast_cube_one(&cubes[c], hidden_enabled, (w3dcgb_u8)(selected == c));
}Wire3DCGB_DrawFastProjectile
在已投影的中心绘制六边弹体,根据深度和相位选择尺寸及朝向。
语法
void Wire3DCGB_DrawFastProjectile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_i16 z, w3dcgb_u8 phase, w3dcgb_u8 color);参数
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
z用于选择尺寸或计算投影的深度。
phase旋转相位,每增加八个计数切换一种朝向。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
在已投影的中心绘制六条边的弹体,使用四档深度尺寸和四种相位朝向。拒绝 X=10~117 或 Y=10~85 之外的中心,标记待更新范围,并保留所请求的颜色为当前颜色。使用普通光栅器,不应用相机。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:159
kitaqgb/lib/wire3d_cgb.c:8833
void Wire3DCGB_DrawFastProjectile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_i16 z,
w3dcgb_u8 phase, w3dcgb_u8 color)
{
w3dcgb_u8 radius;
w3dcgb_u8 half;
w3dcgb_u8 depth;
w3dcgb_u8 ax;
w3dcgb_u8 ay;
w3dcgb_u8 bx;
w3dcgb_u8 by;
w3dcgb_u8 cx;
w3dcgb_u8 cy;
w3dcgb_u8 dx;
w3dcgb_u8 dy;
w3dcgb_u8 spin;
if ((x < 10) || (x > 117) || (y < 10) || (y > 85)) return;
radius = 3;
depth = 1;
if (z <= 160) { radius = 4; depth = 2; }
if (z <= 112) { radius = 5; depth = 3; }
if (z <= 64) { radius = 7; depth = 4; }
half = (w3dcgb_u8)(radius >> 1);
ax = x;
ay = (w3dcgb_u8)(y - radius);
bx = (w3dcgb_u8)(x - radius);
by = (w3dcgb_u8)(y + half);
cx = (w3dcgb_u8)(x + radius);
cy = by;
spin = (w3dcgb_u8)((phase >> 3) & 3);
dx = (w3dcgb_u8)(x + depth);
dy = (w3dcgb_u8)(y - depth);
if (spin == 1) dy = (w3dcgb_u8)(y + depth);
else if (spin == 2)
{
dx = (w3dcgb_u8)(x - depth);
dy = (w3dcgb_u8)(y + depth);
}
else if (spin == 3) dx = (w3dcgb_u8)(x - depth);
w3dcgb_mark_dirty_rect((w3dcgb_u8)(x - radius), (w3dcgb_u8)(y - radius),
(w3dcgb_u8)(x + radius), (w3dcgb_u8)(y + radius));
w3dcgb_line_color = color;
w3dcgb_draw_line_raw(ax, ay, bx, by);
w3dcgb_draw_line_raw(bx, by, cx, cy);
w3dcgb_draw_line_raw(cx, cy, ax, ay);
w3dcgb_draw_line_raw(ax, ay, dx, dy);
w3dcgb_draw_line_raw(bx, by, dx, dy);
w3dcgb_draw_line_raw(cx, cy, dx, dy);
}Wire3DCGB_DrawFastStatus
用短线绘制物体选择及隐藏线模式指示。
语法
void Wire3DCGB_DrawFastStatus(w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected);参数
hidden_enabled零表示包含隐藏边,非零表示采用简化的隐藏边去除。
selected选中物体在原数组中的索引;贴片 API 用它选择状态指示格。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
用颜色 3 绘制小型选中状态和隐藏线模式指示,并标记普通模式的待更新范围。selected 应为 0~2,不作检查。指示由线条构成,不使用字形图块,原颜色不会恢复。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Fast cube approximations use screen offsets and depth size bands, not a camera.
#include "wire_cgb_example.h"
Wire3DCGB_FastCube cubes[2];
void main() {
u8 i;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(i=0;i<2;i++){cubes[i].rx=0;cubes[i].ry=0;cubes[i].rz=0;}
cubes[0].x=-32;cubes[0].y=-8;cubes[0].z=90;cubes[0].color=1;
cubes[1].x=32;cubes[1].y=8;cubes[1].z=160;cubes[1].color=2;
Wire3DCGB_SetCamera(100,0,0,4,4,4); // FastCubes intentionally ignores this.
Wire3DCGB_DrawFastCubes(cubes,2,1,0);
Wire3DCGB_DrawFastStatus(1,0);
result[0]=Wire3DCGB_GetLineColor();
result[1]=cubes[0].z;result[2]=cubes[1].z;
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
左侧较近的红色立方体大于右侧较远的蓝色立方体。绿色十字标记选中的红色立方体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_cubes.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_cubes.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:157
kitaqgb/lib/wire3d_cgb.c:8888
void Wire3DCGB_DrawFastStatus(w3dcgb_u8 hidden_enabled, w3dcgb_u8 selected)
{
w3dcgb_u8 x;
w3dcgb_mark_dirty_rect(0, 0, 31, 7);
w3dcgb_mark_dirty_rect(116, 0, 127, 7);
x = (w3dcgb_u8)(4 + (selected * 8));
w3dcgb_line_color = WIRE3DCGB_COLOR_HIGH;
w3dcgb_draw_line_raw(x, 3, (w3dcgb_u8)(x + 5), 3);
w3dcgb_draw_line_raw(x, 6, (w3dcgb_u8)(x + 5), 6);
if (hidden_enabled != 0)
{
w3dcgb_draw_line_raw(118, 3, 124, 3);
w3dcgb_draw_line_raw(118, 6, 124, 6);
}
else
{
w3dcgb_draw_line_raw(118, 3, 124, 6);
w3dcgb_draw_line_raw(118, 6, 124, 3);
}
}Wire3DCGB_DrawLine2D
用当前渲染器绘制包含端点的屏幕空间线段。
语法
void Wire3DCGB_DrawLine2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
保存字节端点并调用普通或全屏线条光栅器。坐标须位于当前视口内;带符号或屏幕外几何应使用 DrawLineClipped2D。普通线条对颜色位平面执行按位或,不标记稀疏边界,稀疏上传前应使用裁剪 API 或 InvalidateFrameHistory。全屏线条替换颜色并分配图块。两条有效路径都将颜色零按 3 处理,不能擦除。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:177
kitaqgb/lib/wire3d_cgb.c:8363
void Wire3DCGB_DrawLine2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by)
{
w3dcgb_line_x0 = ax;
w3dcgb_line_y0 = ay;
w3dcgb_line_x1 = bx;
w3dcgb_line_y1 = by;
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
}Wire3DCGB_DrawLine2DColor
用临时颜色绘制屏幕空间线段,并保留原选定颜色。
语法
void Wire3DCGB_DrawLine2DColor(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 color);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
临时设置 color & 3 调用 DrawLine2D,再恢复原颜色。遵循该函数的坐标、颜色组合及稀疏范围规则;两个有效渲染器中的颜色零都不会擦除线条。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:178
kitaqgb/lib/wire3d_cgb.c:8376
void Wire3DCGB_DrawLine2DColor(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawLine2D(ax, ay, bx, by);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawLine3D
通过相机投影世界坐标端点,并用当前颜色绘制线段。
语法
void Wire3DCGB_DrawLine3D(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz);参数
ax起点的世界坐标 X 分量。
ay起点的世界坐标 Y 分量。
az起点的世界坐标 Z 分量。
bx终点的世界坐标 X 分量。
by终点的世界坐标 Y 分量。
bz终点的世界坐标 Z 分量。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
通过相机将两个端点投影到 128×96。任一端点深度不合格则丢弃整条线,不裁剪穿越近裁剪面的线段。DrawLine2D 会按模式分派,但投影始终为 128×96。普通线条累加颜色位平面,不标记稀疏边界;须完整上传,或在稀疏上传前使帧历史失效。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:172
kitaqgb/lib/wire3d_cgb.c:8992
void Wire3DCGB_DrawLine3D(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz)
{
w3dcgb_u8 sx0;
w3dcgb_u8 sy0;
w3dcgb_u8 sx1;
w3dcgb_u8 sy1;
if (w3dcgb_project_world(ax, ay, az, &sx0, &sy0) == 0) return;
if (w3dcgb_project_world(bx, by, bz, &sx1, &sy1) == 0) return;
Wire3DCGB_DrawLine2D(sx0, sy0, sx1, sy1);
}Wire3DCGB_DrawLine3DColor
用临时颜色绘制三维线段。
语法
void Wire3DCGB_DrawLine3DColor(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz, w3dcgb_u8 color);参数
ax起点的世界坐标 X 分量。
ay起点的世界坐标 Y 分量。
az起点的世界坐标 Z 分量。
bx终点的世界坐标 X 分量。
by终点的世界坐标 Y 分量。
bz终点的世界坐标 Z 分量。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
临时选择 color & 3 调用 DrawLine3D,随后恢复原颜色。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:173
kitaqgb/lib/wire3d_cgb.c:9005
void Wire3DCGB_DrawLine3DColor(w3dcgb_i16 ax, w3dcgb_i16 ay, w3dcgb_i16 az, w3dcgb_i16 bx, w3dcgb_i16 by, w3dcgb_i16 bz, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawLine3D(ax, ay, az, bx, by, bz);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawLineClipped2D
将带符号屏幕坐标裁剪到视口,并用指定颜色绘制线段。
语法
void Wire3DCGB_DrawLineClipped2D(w3dcgb_i16 x0, w3dcgb_i16 y0, w3dcgb_i16 x1, w3dcgb_i16 y1, w3dcgb_u8 color);参数
x0视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
y0视口像素坐标中的较小 Y 端点;排序与裁剪规则见本函数说明。
x1视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
y1视口像素坐标中的较大 Y 端点;排序与裁剪规则见本函数说明。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
带符号端点先裁剪,再转换为光栅器的 u8 调用格式。将二维线段裁剪到当前视口后,用临时颜色绘制。拒绝超出 ±2047 的输入。完全位于 128×96 内的线段使用快速路径;其他线段使用区域码、受限整数求交及最多八次迭代。普通模式下被接受的线段会标记待更新范围。仅做屏幕裁剪,不进行三维投影或深度裁剪。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:136
Signed endpoints, clipped before conversion to the rasterizer's u8 ABI.
kitaqgb/lib/wire3d_cgb.c:9745
void Wire3DCGB_DrawLineClipped2D(w3dcgb_i16 x0, w3dcgb_i16 y0,
w3dcgb_i16 x1, w3dcgb_i16 y1, w3dcgb_u8 color)
{
w3dcgb_u8 c0;
w3dcgb_u8 c1;
w3dcgb_u8 outside;
w3dcgb_u8 guard;
w3dcgb_i16 x;
w3dcgb_i16 y;
c0 = 0;
c1 = 0;
if ((((x0 | y0 | x1 | y1) & 0xFF80) != 0) || (y0 >= WIRE3DCGB_SCREEN_H) || (y1 >= WIRE3DCGB_SCREEN_H))
{
if ((x0 < -2047) || (x0 > 2047) || (y0 < -2047) || (y0 > 2047) ||
(x1 < -2047) || (x1 > 2047) || (y1 < -2047) || (y1 > 2047)) return;
c0 = w3dcgb_clip_outcode(x0, y0);
c1 = w3dcgb_clip_outcode(x1, y1);
}
guard = 0;
while (guard < 8)
{
if ((c0 | c1) == 0)
{
/* The existing ASM rasterizer handles the hot, in-window path. */
if (w3dcgb_full_mode == 0) w3dcgb_mark_dirty_line((w3dcgb_u8)x0, (w3dcgb_u8)y0,
(w3dcgb_u8)x1, (w3dcgb_u8)y1);
Wire3DCGB_DrawLine2DColor((w3dcgb_u8)x0, (w3dcgb_u8)y0,
(w3dcgb_u8)x1, (w3dcgb_u8)y1, color);
return;
}
if ((c0 & c1) != 0) return;
outside = c0;
if (outside == 0) outside = c1;
if ((outside & 12) != 0)
{
y = 0;
if ((outside & 8) != 0) y = WIRE3DCGB_SCREEN_H - 1;
if (((outside & 8) != 0) && (w3dcgb_full_mode != 0)) y = WIRE3DCGB_FULL_SCREEN_H - 1;
x = (w3dcgb_i16)(x0 + w3dcgb_clip_muldiv((w3dcgb_i16)(x1 - x0),
(w3dcgb_i16)(y - y0), (w3dcgb_i16)(y1 - y0)));
}
else
{
x = 0;
if ((outside & 2) != 0) x = WIRE3DCGB_SCREEN_W - 1;
if (((outside & 2) != 0) && (w3dcgb_full_mode != 0)) x = WIRE3DCGB_FULL_SCREEN_W - 1;
y = (w3dcgb_i16)(y0 + w3dcgb_clip_muldiv((w3dcgb_i16)(y1 - y0),
(w3dcgb_i16)(x - x0), (w3dcgb_i16)(x1 - x0)));
}
if (outside == c0) { x0 = x; y0 = y; c0 = w3dcgb_clip_outcode(x0, y0); }
else { x1 = x; y1 = y; c1 = w3dcgb_clip_outcode(x1, y1); }
guard = (w3dcgb_u8)(guard + 1);
}
}Wire3DCGB_DrawLineList2DColor
用指定颜色绘制四字节屏幕线段记录列表。
语法
void Wire3DCGB_DrawLineList2DColor(const w3dcgb_u8* line_xy, w3dcgb_u8 line_count, w3dcgb_u8 color);参数
line_xy由 x0、y0、x1、y1 记录组成的字节数组。
line_count线段记录数,最多处理 64 条。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
绘制最多 64 条四字节记录 (x0,y0,x1,y1),忽略空列表。端点须位于当前视口内。保留原颜色,每条线按模式交给普通或全屏光栅器。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:186
kitaqgb/lib/wire3d_cgb.c:8571
void Wire3DCGB_DrawLineList2DColor(const w3dcgb_u8* line_xy,
w3dcgb_u8 line_count, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 offset;
w3dcgb_u8 old_color;
if (line_xy == 0) return;
if (line_count > WIRE3DCGB_MODEL_EDGE_LIMIT) line_count = WIRE3DCGB_MODEL_EDGE_LIMIT;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
i = 0;
offset = 0;
while (i < line_count)
{
w3dcgb_line_x0 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_y0 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_x1 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
w3dcgb_line_y1 = line_xy[(__safe_index w3dcgb_u8)offset];
offset = (w3dcgb_u8)(offset + 1);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawMaskedModel2D
根据预投影顶点和边可见位掩码,绘制启用的模型边。
语法
void Wire3DCGB_DrawMaskedModel2D(const Wire3DCGB_Model* model, const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y, const w3dcgb_u8* edge_mask, w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
vertex_x预投影顶点的 X 偏移数组。
vertex_y预投影顶点的 Y 偏移数组。
edge_mask每条边占一位的可见性字节数组,从最低位开始。
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
使用已投影的带符号字节偏移,在 (x,y) 周围绘制启用的模型边。每字节表示八条边,从最低位开始。所有指针和顶点索引须有效;即使边数为零,也会读取 edge_mask[0]。不限制各计数上限。平移后端点须在转换为字节前处于视口内,负值会回绕。保留原线条颜色,并按当前模式分派。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:179
kitaqgb/lib/wire3d_cgb.c:8392
void Wire3DCGB_DrawMaskedModel2D(const Wire3DCGB_Model* model,
const w3dcgb_i8* vertex_x, const w3dcgb_i8* vertex_y,
const w3dcgb_u8* edge_mask,
w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 color)
{
w3dcgb_u8 i;
w3dcgb_u8 bit;
w3dcgb_u8 mask_index;
w3dcgb_u8 mask;
w3dcgb_u8 a;
w3dcgb_u8 b;
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
i = 0;
bit = 1;
mask_index = 0;
mask = edge_mask[0];
while (i < model->edge_count)
{
if ((mask & bit) != 0)
{
a = model->edges[(__safe_index w3dcgb_u8)i].a;
b = model->edges[(__safe_index w3dcgb_u8)i].b;
w3dcgb_line_x0 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_y0 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)a]);
w3dcgb_line_x1 = (w3dcgb_u8)((w3dcgb_i16)x + (w3dcgb_i16)vertex_x[(__safe_index w3dcgb_u8)b]);
w3dcgb_line_y1 = (w3dcgb_u8)((w3dcgb_i16)y + (w3dcgb_i16)vertex_y[(__safe_index w3dcgb_u8)b]);
if (w3dcgb_full_mode != 0) w3dcgb_full_line_fast_asm();
else w3dcgb_line_stage_asm();
}
i = (w3dcgb_u8)(i + 1);
bit = (w3dcgb_u8)(bit << 1);
if ((bit == 0) && (i < model->edge_count))
{
bit = 1;
mask_index = (w3dcgb_u8)(mask_index + 1);
mask = edge_mask[(__safe_index w3dcgb_u8)mask_index];
}
}
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawModel
以原比例、指定位置和旋转绘制三维模型。
语法
void Wire3DCGB_DrawModel(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
以 Q8 原比例 256 绘制模型,遵循 DrawModelScaled 的使用条件。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:128
kitaqgb/lib/wire3d_cgb.c:9015
void Wire3DCGB_DrawModel(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz)
{
Wire3DCGB_DrawModelScaled(model, x, y, z, rx, ry, rz, 256);
}Wire3DCGB_DrawModelColor
用临时颜色绘制原比例模型。
语法
void Wire3DCGB_DrawModelColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_u8 color);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
临时改变颜色,以 Q8 原比例绘制模型,随后恢复原颜色。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:130
kitaqgb/lib/wire3d_cgb.c:9021
void Wire3DCGB_DrawModelColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_u8 color)
{
Wire3DCGB_DrawModelScaledColor(model, x, y, z, rx, ry, rz, 256, color);
}Wire3DCGB_DrawModelScaled
缩放、旋转并投影模型,再用当前颜色绘制选中的边。
语法
void Wire3DCGB_DrawModelScaled(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
以正 Q8 比例投影最多 24 个顶点;比例非正时按原比例处理。原比例时跳过乘法,角度为零时跳过旋转。中间值须能用 s16 表示。可用邻接过滤时建立最多 16 个正面标志,再绘制索引有效且深度有效的边。模型边数不限于 64。没有近裁剪面裁剪。会替换共享投影缓存,并将线条颜色按位或写入普通 128×96 暂存区。本路径不标记稀疏边界,应完整上传,或在稀疏上传前显式使帧历史失效。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:129
kitaqgb/lib/wire3d_cgb.c:9396
void Wire3DCGB_DrawModelScaled(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8)
{
w3dcgb_u8 i;
w3dcgb_u8 count;
w3dcgb_u8 edge_count;
w3dcgb_u8 face_count;
w3dcgb_u8 camera_unrotated;
w3dcgb_u8 unit_scale;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->edges == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3DCGB_MODEL_VERTEX_LIMIT) count = WIRE3DCGB_MODEL_VERTEX_LIMIT;
unit_scale = 0;
if (scale_q8 == 256) unit_scale = 1;
camera_unrotated = 0;
if ((w3dcgb_cam_yaw == 0) && (w3dcgb_cam_pitch == 0) && (w3dcgb_cam_roll == 0))
{
camera_unrotated = 1;
}
i = 0;
while (i < count)
{
const Wire3DCGB_Vec3* v;
w3dcgb_i16 vx;
w3dcgb_i16 vy;
w3dcgb_i16 vz;
w3dcgb_u8 sx;
w3dcgb_u8 sy;
w3dcgb_u8 projected;
v = &model->vertices[(__safe_index w3dcgb_u8)i];
if (unit_scale != 0)
{
vx = v->x;
vy = v->y;
vz = v->z;
}
else
{
vx = (w3dcgb_i16)((v->x * scale_q8) >> 8);
vy = (w3dcgb_i16)((v->y * scale_q8) >> 8);
vz = (w3dcgb_i16)((v->z * scale_q8) >> 8);
}
if (ry != 0) w3dcgb_rotate_y(&vx, &vz, (w3dcgb_i8)ry);
if (rx != 0) w3dcgb_rotate_x(&vy, &vz, (w3dcgb_i8)rx);
if (rz != 0) w3dcgb_rotate_z(&vx, &vy, (w3dcgb_i8)rz);
vx = (w3dcgb_i16)(vx + x);
vy = (w3dcgb_i16)(vy + y);
vz = (w3dcgb_i16)(vz + z);
if (camera_unrotated != 0)
{
vx = (w3dcgb_i16)(vx - w3dcgb_cam_x);
vy = (w3dcgb_i16)(vy - w3dcgb_cam_y);
vz = (w3dcgb_i16)(vz - w3dcgb_cam_z);
projected = w3dcgb_project_camera_space(vx, vy, vz, &sx, &sy);
}
else
{
projected = w3dcgb_project_world(vx, vy, vz, &sx, &sy);
}
if (projected != 0)
{
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = sx;
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = sy;
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 1;
}
else
{
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 0;
}
i = (w3dcgb_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3DCGB_MODEL_FACE_LIMIT) face_count = WIRE3DCGB_MODEL_FACE_LIMIT;
if ((model->flags & WIRE3DCGB_MODEL_HIDDEN_LINES) && (model->faces != 0) && (model->edge_faces != 0))
{
w3dcgb_build_face_visibility(model, count, face_count);
}
edge_count = model->edge_count;
i = 0;
while (i < edge_count)
{
const Wire3DCGB_Edge* e;
e = &model->edges[(__safe_index w3dcgb_u8)i];
if ((e->a < count) && (e->b < count))
{
if (w3dcgb_screen_visible[(__safe_index w3dcgb_u8)e->a] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)e->b] &&
w3dcgb_is_edge_visible(model, i, face_count))
{
w3dcgb_draw_model_line(
w3dcgb_screen_x[(__safe_index w3dcgb_u8)e->a],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)e->a],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)e->b],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)e->b]);
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_DrawModelScaledColor
用指定缩放比例和临时线条颜色绘制模型。
语法
void Wire3DCGB_DrawModelScaledColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8, w3dcgb_u8 color);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
在 DrawModelScaled 调用前选择 color & 3,结束后恢复进入时的颜色。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:131
kitaqgb/lib/wire3d_cgb.c:9510
void Wire3DCGB_DrawModelScaledColor(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8, w3dcgb_u8 color)
{
w3dcgb_u8 old_color;
old_color = w3dcgb_line_color;
Wire3DCGB_SetLineColor(color);
Wire3DCGB_DrawModelScaled(model, x, y, z, rx, ry, rz, scale_q8);
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawPoint2D
用当前颜色写入一个像素。
语法
void Wire3DCGB_DrawPoint2D(w3dcgb_u8 x, w3dcgb_u8 y);参数
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
普通模式拒绝 128×96 之外的坐标,标记稀疏范围,并用当前颜色替换暂存像素,包括颜色零。全屏模式改为绘制两端重合的快速线段,因此颜色零按 3 处理,无效坐标会锁存本帧溢出标志。显示传输在之后执行。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:174
kitaqgb/lib/wire3d_cgb.c:8283
void Wire3DCGB_DrawPoint2D(w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 tile;
if (w3dcgb_full_mode != 0)
{
w3dcgb_line_x0 = x;
w3dcgb_line_y0 = y;
w3dcgb_line_x1 = x;
w3dcgb_line_y1 = y;
w3dcgb_full_line_fast_asm();
return;
}
if (x >= WIRE3DCGB_SCREEN_W) return;
if (y >= WIRE3DCGB_SCREEN_H) return;
if (w3dcgb_full_frame_transfer == 0)
{
tile = (w3dcgb_u8)(((x >> 3) * W3DCGB_TILE_H) + (y >> 3));
if (tile < w3dcgb_dirty_min_tile) w3dcgb_dirty_min_tile = tile;
if (tile > w3dcgb_dirty_max_tile) w3dcgb_dirty_max_tile = tile;
}
w3dcgb_plot_x = x;
w3dcgb_plot_y = y;
w3dcgb_plot_stage_asm();
}Wire3DCGB_DrawScene
从近到远绘制最多八个物体,用先前模型的面遮挡后续线条。
语法
void Wire3DCGB_DrawScene(Wire3DCGB_Object* objects, w3dcgb_u8 count);参数
objects包含位置、旋转、缩放和可见标志的物体数组。
count提供的数组元素数;具体处理上限见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
按相机空间原点深度将最多八个物体从近到远排序,不改变调用方数组。清空遮罩,依据先前覆盖绘制可见模型,再将其面加入遮挡区域。模型数组和面索引须有效。颜色零沿用当前颜色,可能是前一个物体的颜色。结束时恢复进入时颜色并禁用遮挡。本模型路径面向普通 128×96 暂存区。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// A near square hides the middle of a farther blue line, regardless of array order.
#include "wire_cgb_example.h"
#include "wire_cgb_plane.h"
__prg_rom Wire3DCGB_Vec3 back_vertices[2]={{-96,0,0},{96,0,0}};
__prg_rom Wire3DCGB_Edge back_edges[1]={{0,1}};
Wire3DCGB_Model back;
Wire3DCGB_Object objects[2];
void main() {
u8 i;
wire_results_clear();wire_plane_setup();
back.vertices=back_vertices;back.edges=back_edges;back.faces=0;back.edge_faces=0;
back.vertex_count=2;back.edge_count=1;back.face_count=0;back.flags=0;
for(i=0;i<2;i++) {
objects[i].x=0;objects[i].y=0;objects[i].rx=0;objects[i].ry=0;objects[i].rz=0;
objects[i].scale_q8=256;objects[i].visible=1;
}
objects[0].model=&back;objects[0].z=128;objects[0].color=2;
objects[1].model=&plane;objects[1].z=64;objects[1].color=1;
Wire3DCGB_Init();wire_colors();Wire3DCGB_BeginFrame();Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawScene(objects,2);
result[0]=objects[0].z;result[1]=objects[1].z;result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
近处的红色方框位于 (46,30)~(82,66),遮住远处蓝色水平线的中段,只留下左右两段。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_scene.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_scene.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:155
kitaqgb/lib/wire3d_cgb.c:8918
void Wire3DCGB_DrawScene(Wire3DCGB_Object* objects, w3dcgb_u8 count)
{
w3dcgb_u8 scene_count;
w3dcgb_u8 i;
w3dcgb_u8 old_color;
if (objects == 0) return;
scene_count = count;
if (scene_count > WIRE3DCGB_SCENE_OBJECT_LIMIT) scene_count = WIRE3DCGB_SCENE_OBJECT_LIMIT;
i = 0;
while (i < scene_count)
{
w3dcgb_scene_order[(__safe_index w3dcgb_u8)i] = i;
w3dcgb_scene_depth[(__safe_index w3dcgb_u8)i] = w3dcgb_scene_object_depth(&objects[(__safe_index w3dcgb_u8)i]);
i = (w3dcgb_u8)(i + 1);
}
i = 0;
while (i < scene_count)
{
w3dcgb_u8 j;
j = (w3dcgb_u8)(i + 1);
while (j < scene_count)
{
w3dcgb_u8 oi;
w3dcgb_u8 oj;
oi = w3dcgb_scene_order[(__safe_index w3dcgb_u8)i];
oj = w3dcgb_scene_order[(__safe_index w3dcgb_u8)j];
if (w3dcgb_scene_depth[(__safe_index w3dcgb_u8)oj] < w3dcgb_scene_depth[(__safe_index w3dcgb_u8)oi])
{
w3dcgb_scene_order[(__safe_index w3dcgb_u8)i] = oj;
w3dcgb_scene_order[(__safe_index w3dcgb_u8)j] = oi;
}
j = (w3dcgb_u8)(j + 1);
}
i = (w3dcgb_u8)(i + 1);
}
old_color = w3dcgb_line_color;
w3dcgb_clear_occlusion_mask();
w3dcgb_occlusion_active = 0;
i = 0;
while (i < scene_count)
{
Wire3DCGB_Object* obj;
obj = &objects[(__safe_index w3dcgb_u8)w3dcgb_scene_order[(__safe_index w3dcgb_u8)i]];
if ((obj->visible != 0) && (obj->model != 0))
{
if (i == 0) w3dcgb_occlusion_active = 0;
else w3dcgb_occlusion_active = 1;
if (obj->color != 0) Wire3DCGB_SetLineColor(obj->color);
Wire3DCGB_DrawModelScaled(obj->model, obj->x, obj->y, obj->z, (w3dcgb_i8)obj->rx, (w3dcgb_i8)obj->ry, (w3dcgb_i8)obj->rz, obj->scale_q8);
w3dcgb_occlusion_active = 0;
w3dcgb_mark_model_occluder(obj->model);
}
i = (w3dcgb_u8)(i + 1);
}
w3dcgb_occlusion_active = 0;
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawTinyModel2D
绘制六像素标记,用头尾位置表示方向。
语法
void Wire3DCGB_DrawTinyModel2D(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 turn, w3dcgb_u8 color);参数
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
turn低五位用于选择微型标记方向的数值。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
通过 128×96 点绘制函数画出六像素标记,并保留线条颜色。中心不在 X=3~124、Y=3~92 时忽略。turn 的低五位选择左/直/右的头尾偏移;这不是通用三维变换,也不会分派到全屏渲染器。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:175
kitaqgb/lib/wire3d_cgb.c:8313
void Wire3DCGB_DrawTinyModel2D(w3dcgb_u8 x, w3dcgb_u8 y,
w3dcgb_u8 turn, w3dcgb_u8 color)
{
w3dcgb_u8 nose_x;
w3dcgb_u8 tail_x;
w3dcgb_u8 old_color;
if ((x < 3) || (x > 124) || (y < 3) || (y > (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 4))) return;
nose_x = x;
tail_x = x;
turn = (w3dcgb_u8)(turn & 31);
if (turn < 12)
{
nose_x = (w3dcgb_u8)(x + 1);
tail_x = (w3dcgb_u8)(x - 1);
}
else if (turn >= 20)
{
nose_x = (w3dcgb_u8)(x - 1);
tail_x = (w3dcgb_u8)(x + 1);
}
old_color = w3dcgb_line_color;
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
w3dcgb_plot_x = nose_x;
w3dcgb_plot_y = (w3dcgb_u8)(y - 2);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = x;
w3dcgb_plot_y = (w3dcgb_u8)(y - 1);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = (w3dcgb_u8)(x - 2);
w3dcgb_plot_y = (w3dcgb_u8)(y + 1);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = (w3dcgb_u8)(x + 2);
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = x;
w3dcgb_plot_y = y;
w3dcgb_plot_stage_asm();
w3dcgb_plot_x = tail_x;
w3dcgb_plot_y = (w3dcgb_u8)(y + 2);
w3dcgb_plot_stage_asm();
w3dcgb_line_color = old_color;
}Wire3DCGB_DrawWhiteBorderFast
在 128×96 暂存区周围绘制一像素宽的颜色索引 2 边框。
语法
void Wire3DCGB_DrawWhiteBorderFast();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
在普通暂存区周围写入一像素宽、颜色索引为 2 的边框,保留内部像素和 WRAM 存储体选择。实际颜色由调色板决定。不标记待更新范围,稀疏传输调用方须安排上传。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// A cleared 24-pixel strip and a palette-index-2 border have distinct purposes.
#include "wire_cgb_example.h"
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=0;y<96;y++) Wire3DCGB_DrawLine2D(0,y,31,y);
Wire3DCGB_EraseLeftGuard24Fast();
// With this example's palette, color index 2 is blue rather than white.
Wire3DCGB_DrawWhiteBorderFast();
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
左侧 24 列被清空,仍保留红色条带,颜色索引 2 的外边框为蓝色。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_guard.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_guard.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:147
kitaqgb/lib/wire3d_cgb.c:9369
void Wire3DCGB_DrawWhiteBorderFast()
{
w3dcgb_draw_white_border_asm();
}Wire3DCGB_EnableAtomicMaps
设置 128×96 显示,让两张背景图交替呈现画面。
语法
void Wire3DCGB_EnableAtomicMaps();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
仅在启动时使用:占用两个背景图,排队的 HUD 图块编号会镜像写入。128×96 初始化时只启用一次背景图翻转;全屏模式或已启用时忽略。LCD 开启时先等待 VBlank,再关闭 LCD,将 9800 的两个存储体复制到 9C00,并让两张视口背景图分别引用图块存储体 0/1。恢复 LCDC,选中 9C00,并令 VBK 为零。之后两张背景图均被保留使用。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:144
Startup-only: reserves both BG maps; queued HUD tiles are mirrored.
kitaqgb/lib/wire3d_cgb.c:7912
void Wire3DCGB_EnableAtomicMaps()
{
if ((w3dcgb_full_mode != 0) || (w3dcgb_atomic_maps != 0)) return;
__asm {
w3dam_wait:
LDH_A_MEM 68
CP_IMM 144
JR_C w3dam_wait
LDH_A_MEM 64
PUSH_AF
XOR_A
LDH_MEM_A 64
LDH_MEM_A 79
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 4
w3dam_copy_page:
LD_C_IMM 0
w3dam_copy_byte:
LD_A_HL
INC_HL
LD_DE_A
INC_DE
DEC_C
JR_NZ w3dam_copy_byte
DEC_B
JR_NZ w3dam_copy_page
LDH_A_MEM 79
AND_IMM 1
JR_NZ w3dam_attrs
LD_A_IMM 1
LDH_MEM_A 79
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 4
JR w3dam_copy_page
w3dam_attrs:
LD_HL_IMM 0x9800
LD_DE_IMM 0x9C00
LD_B_IMM 12
w3dam_row:
LD_C_IMM 16
w3dam_cell:
XOR_A
LDI_HL_A
LD_A_IMM 8
LD_DE_A
INC_DE
DEC_C
JR_NZ w3dam_cell
LD_A_L
ADD_A_IMM 16
LD_L_A
JR_NC w3dam_hl_ready
INC_H
w3dam_hl_ready:
LD_A_E
ADD_A_IMM 16
LD_E_A
JR_NC w3dam_de_ready
INC_D
w3dam_de_ready:
DEC_B
JR_NZ w3dam_row
LD_A_IMM 1
LD_MEM_A w3dcgb_atomic_maps
XOR_A
LDH_MEM_A 79
POP_AF
OR_IMM 8
LDH_MEM_A 64
RET
}
}Wire3DCGB_EnableDoubleSpeed
将 CGB CPU 切换到双倍速。
语法
void Wire3DCGB_EnableDoubleSpeed();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
在 CGB 上,若 KEY1 已报告双倍速则直接返回,否则请求切换速度并调用 STOP 辅助函数。此 API 假定硬件为 CGB,不提供初代 GB 回退路径。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:116
kitaqgb/lib/wire3d_cgb.c:1081
void Wire3DCGB_EnableDoubleSpeed()
{
if ((w3dcgb_reg_key1 & 0x80) != 0) return;
w3dcgb_reg_key1 = 1;
w3dcgb_cgb_speed_switch_asm();
}Wire3DCGB_EndFrame
上传并显示完成的帧。
语法
void Wire3DCGB_EndFrame();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
普通模式提交背景写入队列,传输整个暂存区,并显示完成的图块存储体。全屏模式传输、显示已分配图块,但不提交 HUD 队列。要求 LCD 已开启,并遵守传输辅助函数的存储体和中断条件;可能跨越多个帧。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:189
kitaqgb/lib/wire3d_cgb.c:9881
void Wire3DCGB_EndFrame()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
if (w3dcgb_bgq_count != 0)
{
w3dcgb_wait_vblank_start();
w3dcgb_flush_bg_queue();
}
w3dcgb_transfer_stage_asm();
w3dcgb_present_stage_asm();
}Wire3DCGB_EndFrameFast
上传并显示完整帧,不提交排队中的背景写入。
语法
void Wire3DCGB_EndFrameFast();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
传输并显示当前模式的完整帧,不提交背景队列,也不消耗 HUD 待写项目。LCD、存储体和中断条件与 EndFrame 相同,需要完整运行时。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:190
kitaqgb/lib/wire3d_cgb.c:9902
void Wire3DCGB_EndFrameFast()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
w3dcgb_transfer_stage_asm();
w3dcgb_present_stage_asm();
}Wire3DCGB_EndFrameSparse
等待新的 VBlank,再上传并显示所需的稀疏范围。
语法
void Wire3DCGB_EndFrameSparse();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
普通模式等待新的 VBlank,再调用 EndFrameSparseNow;全屏模式使用自身完整传输和呈现路径。需要完整运行时及已开启的 LCD,不保证在一帧内完成。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:191
kitaqgb/lib/wire3d_cgb.c:9917
void Wire3DCGB_EndFrameSparse()
{
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
w3dcgb_wait_vblank_start();
Wire3DCGB_EndFrameSparseNow();
}Wire3DCGB_EndFrameSparseNow
跳过起始的新 VBlank 等待,提交背景写入及当前/N−2 帧稀疏范围。
语法
void Wire3DCGB_EndFrameSparseNow();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
提交背景写入,将当前/N−2 帧的稀疏图块范围上传到后台存储体并显示。跳过起始的新 VBlank 等待,但普通背景图模式恢复空白 HUD 图块时仍会等待,DMA 和呈现也可能等待。完整运行时的全屏模式转交自身路径。要求 LCD 已开启,DMA 活动期间保持 SVBK/VBK 稳定。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:192
kitaqgb/lib/wire3d_cgb.c:9935
void Wire3DCGB_EndFrameSparseNow()
{
#ifndef WIRE3DCGB_MINIMAL_RUNTIME
if (w3dcgb_full_mode != 0)
{
w3dcgb_full_transfer_asm();
w3dcgb_full_present_asm();
return;
}
#endif
/* Atomic mode never writes a displayed tile and does not need a
frame-long VBlank wait before starting an off-screen upload. */
if (w3dcgb_atomic_maps == 0) w3dcgb_restore_hud_blank_tile_asm();
if (w3dcgb_bgq_count != 0) w3dcgb_flush_bg_queue();
w3dcgb_transfer_dirty_tiles();
w3dcgb_present_stage_asm();
}Wire3DCGB_EraseLeftGuard24Fast
清空普通暂存区最左侧 24 列。
语法
void Wire3DCGB_EraseLeftGuard24Fast();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
通过保存存储体选择的汇编代码清除暂存区最左侧 24 列。不标记待更新范围,稀疏传输调用方须自行包含这些图块。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// A cleared 24-pixel strip and a palette-index-2 border have distinct purposes.
#include "wire_cgb_example.h"
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=0;y<96;y++) Wire3DCGB_DrawLine2D(0,y,31,y);
Wire3DCGB_EraseLeftGuard24Fast();
// With this example's palette, color index 2 is blue rather than white.
Wire3DCGB_DrawWhiteBorderFast();
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
左侧 24 列被清空,仍保留红色条带,颜色索引 2 的外边框为蓝色。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_guard.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_guard.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:146
kitaqgb/lib/wire3d_cgb.c:9200
void Wire3DCGB_EraseLeftGuard24Fast()
{
w3dcgb_erase_left_guard24_asm();
}Wire3DCGB_EraseModelFaces
擦除投影后朝向观察者的模型面内部的暂存像素。
语法
void Wire3DCGB_EraseModelFaces(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8);参数
model包含顶点、边、面数组及其数量的模型;调用期间各表必须保持可读。
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
rx绕 X 轴旋转,一周为 16 档。
ry绕 Y 轴旋转,一周为 16 档。
rz绕 Z 轴旋转,一周为 16 档。
scale_q8缩放比例,256 为原比例;非正值也按原比例处理。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
对具有顶点和面的隐藏线模型,变换最多 24 个顶点,并擦除最多 16 个面中朝向观察者的三角形。比例非正时按原比例处理;本路径在原比例下仍执行乘法,零角度时仍调用三个旋转辅助函数。跳过无效或深度不合格的面。会替换共享投影缓存,并使用普通 C 暂存区清除函数,调用方或内存布局须保证 WRAM 映射正确。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Projected model faces erase a square opening; redraw its outline in green.
#include "wire_cgb_example.h"
#include "wire_cgb_plane.h"
void main() {
u8 y;u8 bank;
wire_results_clear();wire_plane_setup();Wire3DCGB_Init();wire_colors();Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
for(y=8;y<=88;y++) Wire3DCGB_DrawLine2D(24,y,104,y);
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_EraseModelFaces(&plane,0,0,64,0,0,0,256);
sample_svbk=bank;
Wire3DCGB_DrawModelColor(&plane,0,0,64,0,0,0,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
投影面擦除已填充区域,从而显示保留区域与清除区域的区别。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_faces.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_faces.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:132
kitaqgb/lib/wire3d_cgb.c:9033
void Wire3DCGB_EraseModelFaces(const Wire3DCGB_Model* model, w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 rx, w3dcgb_i8 ry, w3dcgb_i8 rz, w3dcgb_i16 scale_q8)
{
w3dcgb_u8 i;
w3dcgb_u8 count;
w3dcgb_u8 face_count;
if (model == 0) return;
if (model->vertices == 0) return;
if (model->faces == 0) return;
if ((model->flags & WIRE3DCGB_MODEL_HIDDEN_LINES) == 0) return;
if (scale_q8 <= 0) scale_q8 = 256;
count = model->vertex_count;
if (count > WIRE3DCGB_MODEL_VERTEX_LIMIT) count = WIRE3DCGB_MODEL_VERTEX_LIMIT;
i = 0;
while (i < count)
{
const Wire3DCGB_Vec3* v;
w3dcgb_i16 vx;
w3dcgb_i16 vy;
w3dcgb_i16 vz;
w3dcgb_u8 sx;
w3dcgb_u8 sy;
v = &model->vertices[(__safe_index w3dcgb_u8)i];
vx = (w3dcgb_i16)((v->x * scale_q8) >> 8);
vy = (w3dcgb_i16)((v->y * scale_q8) >> 8);
vz = (w3dcgb_i16)((v->z * scale_q8) >> 8);
w3dcgb_rotate_y(&vx, &vz, (w3dcgb_i8)ry);
w3dcgb_rotate_x(&vy, &vz, (w3dcgb_i8)rx);
w3dcgb_rotate_z(&vx, &vy, (w3dcgb_i8)rz);
vx = (w3dcgb_i16)(vx + x);
vy = (w3dcgb_i16)(vy + y);
vz = (w3dcgb_i16)(vz + z);
if (w3dcgb_project_world(vx, vy, vz, &sx, &sy))
{
w3dcgb_screen_x[(__safe_index w3dcgb_u8)i] = sx;
w3dcgb_screen_y[(__safe_index w3dcgb_u8)i] = sy;
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 1;
}
else
{
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)i] = 0;
}
i = (w3dcgb_u8)(i + 1);
}
face_count = model->face_count;
if (face_count > WIRE3DCGB_MODEL_FACE_LIMIT) face_count = WIRE3DCGB_MODEL_FACE_LIMIT;
w3dcgb_build_face_visibility(model, count, face_count);
i = 0;
while (i < face_count)
{
const Wire3DCGB_Face* f;
f = &model->faces[(__safe_index w3dcgb_u8)i];
if (w3dcgb_face_visible[(__safe_index w3dcgb_u8)i] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->a] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->b] &&
w3dcgb_screen_visible[(__safe_index w3dcgb_u8)f->c])
{
w3dcgb_clear_triangle(
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->a],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->a],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->b],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->b],
w3dcgb_screen_x[(__safe_index w3dcgb_u8)f->c],
w3dcgb_screen_y[(__safe_index w3dcgb_u8)f->c]);
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_ErasePackedSilhouette2D
从普通暂存区两个颜色位平面中擦除紧凑轮廓。
语法
void Wire3DCGB_ErasePackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count, w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);参数
profile每行一个字节的轮廓数组,0xFF 表示跳过该行。
count提供的数组元素数;具体处理上限见本函数说明。
y0从中心 Y 到第一行轮廓的带符号偏移。
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
从两个暂存位平面擦除最多 29 行紧凑轮廓。格式、位置、两像素保护边距及裁剪与 MarkPackedSilhouette2D 相同;忽略空指针。汇编区间写入会选择并恢复 WRAM 存储体 2,但不标记待更新图块。稀疏模式下调用方须保证擦除区域纳入上传范围。本 API 面向 128×96 暂存区。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形、水平区间、矩形和紧凑轮廓在红、蓝、绿填充面板中擦出黑色区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:153
kitaqgb/lib/wire3d_cgb.c:2038
void Wire3DCGB_ErasePackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 i;
w3dcgb_u8 packed;
w3dcgb_u8 half_width;
w3dcgb_i16 center;
w3dcgb_i16 left;
w3dcgb_i16 right;
w3dcgb_i16 row;
if (profile == 0) return;
if (count > 29) count = 29;
i = 0;
while (i < count)
{
packed = profile[(__safe_index w3dcgb_u8)i];
if (packed != 0xFF)
{
row = (w3dcgb_i16)((w3dcgb_i16)y + (w3dcgb_i16)y0 + (w3dcgb_i16)i);
if ((row >= 0) && (row < WIRE3DCGB_SCREEN_H))
{
center = (w3dcgb_i16)((w3dcgb_i16)x + (w3dcgb_i16)(packed >> 4) - 7);
half_width = (w3dcgb_u8)(packed & 15);
left = (w3dcgb_i16)(center - (w3dcgb_i16)half_width - 2);
right = (w3dcgb_i16)(center + (w3dcgb_i16)half_width + 2);
if ((right >= 0) && (left < WIRE3DCGB_SCREEN_W))
{
if (left < 0) left = 0;
if (right >= WIRE3DCGB_SCREEN_W) right = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mask_span_y = (w3dcgb_u8)row;
w3dcgb_mask_span_min_x = (w3dcgb_u8)left;
w3dcgb_mask_span_max_x = (w3dcgb_u8)right;
w3dcgb_stage_clear_span_asm();
}
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_EraseRect2D
从普通暂存区擦除矩形。
语法
void Wire3DCGB_EraseRect2D(w3dcgb_u8 x0, w3dcgb_u8 y0, w3dcgb_u8 x1, w3dcgb_u8 y1);参数
x0视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
y0视口像素坐标中的较小 Y 端点;排序与裁剪规则见本函数说明。
x1视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
y1视口像素坐标中的较大 Y 端点;排序与裁剪规则见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
排列矩形端点并裁剪到 128×96。左侧宽 16 或 24 像素的全高条带使用会保存存储体的汇编清除及待更新标记。其他矩形使用 C 行清除,调用方须保证暂存区 WRAM 映射正确。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形、水平区间、矩形和紧凑轮廓在红、蓝、绿填充面板中擦出黑色区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:145
kitaqgb/lib/wire3d_cgb.c:9146
void Wire3DCGB_EraseRect2D(w3dcgb_u8 x0, w3dcgb_u8 y0, w3dcgb_u8 x1, w3dcgb_u8 y1)
{
w3dcgb_u8 t;
w3dcgb_u8 y;
if (x0 > x1)
{
t = x0;
x0 = x1;
x1 = t;
}
if (y0 > y1)
{
t = y0;
y0 = y1;
y1 = t;
}
if (x0 >= WIRE3DCGB_SCREEN_W) return;
if (y0 >= WIRE3DCGB_SCREEN_H) return;
if (x1 >= WIRE3DCGB_SCREEN_W) x1 = (w3dcgb_u8)(WIRE3DCGB_SCREEN_W - 1);
if (y1 >= WIRE3DCGB_SCREEN_H) y1 = (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1);
if ((x0 == 0) &&
(x1 == 15) &&
(y0 == 0) &&
(y1 == (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1)))
{
w3dcgb_erase_left_guard16_asm();
w3dcgb_mark_dirty_rect(0, 0, 15, (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
return;
}
if ((x0 == 0) &&
(x1 == 23) &&
(y0 == 0) &&
(y1 == (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1)))
{
w3dcgb_erase_left_guard24_asm();
w3dcgb_mark_dirty_rect(0, 0, 23, (w3dcgb_u8)(WIRE3DCGB_SCREEN_H - 1));
return;
}
y = y0;
while (y <= y1)
{
w3dcgb_stage_clear_span(y, x0, x1);
if (y == y1) break;
y = (w3dcgb_u8)(y + 1);
}
}Wire3DCGB_EraseSpan2D
擦除水平区间,并保存、恢复 WRAM 存储体选择。
语法
void Wire3DCGB_EraseSpan2D(w3dcgb_u8 y, w3dcgb_u8 x0, w3dcgb_u8 x1);参数
y待擦除行的像素 Y 坐标。
x0视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
x1视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
排列 X 端点,拒绝屏幕外的行或左边界,将右端限制为 127。标记范围为待更新,并使用会选择、恢复 WRAM 存储体 2 的汇编区间清除函数。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形、水平区间、矩形和紧凑轮廓在红、蓝、绿填充面板中擦出黑色区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:134
kitaqgb/lib/wire3d_cgb.c:9123
void Wire3DCGB_EraseSpan2D(w3dcgb_u8 y, w3dcgb_u8 x0, w3dcgb_u8 x1)
{
w3dcgb_u8 tx;
if (x0 > x1)
{
tx = x0;
x0 = x1;
x1 = tx;
}
if ((y >= WIRE3DCGB_SCREEN_H) || (x0 >= WIRE3DCGB_SCREEN_W)) return;
if (x1 >= WIRE3DCGB_SCREEN_W) x1 = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mark_dirty_rect(x0, y, x1, y);
w3dcgb_mask_span_y = y;
w3dcgb_mask_span_min_x = x0;
w3dcgb_mask_span_max_x = x1;
w3dcgb_stage_clear_span_asm();
}Wire3DCGB_EraseTriangle2D
从普通暂存区的两个颜色位平面中擦除三角形。
语法
void Wire3DCGB_EraseTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
cx顶点 C 的视口像素 X 坐标。
cy顶点 C 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
通过普通 C 暂存区清除函数擦除三角形。裁剪后的扫描线区间会标记涉及的图块为待更新;调用方或内存布局须保证暂存区 WRAM 映射正确。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Colored panels expose triangle, span, rectangle and packed-silhouette erasure.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
u8 bank;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(8,y,40,y,1);
for(y=8;y<=40;y++) Wire3DCGB_DrawLine2DColor(48,y,80,y,2);
for(y=64;y<=72;y++) Wire3DCGB_DrawLine2DColor(88,y,120,y,3);
// The C triangle/rectangle clearers access the stage through the current
// WRAM window. Select bank 2 around them and restore the caller's bank.
bank=sample_svbk; sample_svbk=2;
Wire3DCGB_EraseTriangle2D(12,12,36,12,12,36);
Wire3DCGB_EraseSpan2D(36,40,24);
Wire3DCGB_EraseRect2D(56,16,72,32);
Wire3DCGB_ErasePackedSilhouette2D(silhouette,5,-2,104,68);
sample_svbk=bank;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形、水平区间、矩形和紧凑轮廓在红、蓝、绿填充面板中擦出黑色区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_erase.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_erase.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:133
kitaqgb/lib/wire3d_cgb.c:9113
void Wire3DCGB_EraseTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy)
{
w3dcgb_clear_triangle(ax, ay, bx, by, cx, cy);
}Wire3DCGB_FastMapBegin
清空 16×12 FastMap 视口的图块编号和属性。
语法
void Wire3DCGB_FastMapBegin();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
标记 FastMap 已准备,并清空其 16×12 视口的图块编号和属性。影子缓冲中的第 16~31 列保留,尽管提交时每行会复制全部 32 列。需要完整运行时及稳定的 FastMap WRAM 映射;不初始化硬件,也不上传背景图。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:160
kitaqgb/lib/wire3d_cgb.c:10137
void Wire3DCGB_FastMapBegin()
{
w3dcgb_fast_map_ready = 1;
w3dcgb_fast_map_clear_rect(0, 0, 16, 12);
}Wire3DCGB_FastMapBeginTilesOnly
仅清空 16×12 FastMap 视口的图块编号。
语法
void Wire3DCGB_FastMapBeginTilesOnly();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
标记 FastMap 已准备,仅清空其 16×12 视口的图块编号,保留属性及第 16~31 列。需要完整运行时和正确的 WRAM 映射;硬件初始化及上传须另行完成。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:161
kitaqgb/lib/wire3d_cgb.c:10146
void Wire3DCGB_FastMapBeginTilesOnly()
{
w3dcgb_fast_map_ready = 1;
w3dcgb_fm_x0 = 0;
w3dcgb_fm_y0 = 0;
w3dcgb_fm_count_x = 16;
w3dcgb_fm_count_y = 12;
w3dcgb_fast_map_clear_rect_asm();
}Wire3DCGB_FastMapCell
为一个背景图格添加选定的上、下、左、右轮廓边。
语法
void Wire3DCGB_FastMapCell(w3dcgb_u8 tx, w3dcgb_u8 ty, w3dcgb_u8 color, w3dcgb_u8 mask);参数
tx图块单位的 X 坐标。
ty图块单位的 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
mask图格边掩码:1=上、2=下、4=左、8=右。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
为一个图格添加轮廓边,位值 1/2/4/8 分别为上/下/左/右。同色掩码会合并,另一颜色则替换旧掩码。忽略零掩码,颜色零表示 MAIN,大于 3 时限制为 3。操作 16×12 影子缓冲,须保持 WRAM 映射稳定,并使用完整运行时。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:162
kitaqgb/lib/wire3d_cgb.c:10160
void Wire3DCGB_FastMapCell(w3dcgb_u8 tx, w3dcgb_u8 ty, w3dcgb_u8 color, w3dcgb_u8 mask)
{
w3dcgb_fast_map_or_cell((w3dcgb_i16)tx, (w3dcgb_i16)ty, color, mask);
}Wire3DCGB_FastMapFlush
将影子缓冲前十二个完整行的图块编号和属性上传到 VRAM。
语法
void Wire3DCGB_FastMapFlush();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
LCD 开启时等待新的 VBlank,再将影子缓冲前十二个完整行的图块编号及属性上传到 9800 背景图。LCD 关闭时立即执行。需要完整运行时、稳定的 WRAM 映射,以及足够完成两次立即 GDMA 传输的安全时间。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:164
kitaqgb/lib/wire3d_cgb.c:10208
void Wire3DCGB_FastMapFlush()
{
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_gdma_asm();
}Wire3DCGB_FastMapFlushTilesOnly
仅上传影子背景图前十二个完整行的图块编号。
语法
void Wire3DCGB_FastMapFlushTilesOnly();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
LCD 开启时等待新的 VBlank,仅将影子缓冲前十二个完整行的图块编号上传到 9800 背景图,属性保持不变。需要完整运行时、稳定的 WRAM 映射及安全的 GDMA 时段。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:165
kitaqgb/lib/wire3d_cgb.c:10217
void Wire3DCGB_FastMapFlushTilesOnly()
{
w3dcgb_wait_vblank_start();
w3dcgb_fast_map_flush_tiles_gdma_asm();
}Wire3DCGB_FastMapRect
沿图块坐标矩形放置选定的轮廓边。
语法
void Wire3DCGB_FastMapRect(w3dcgb_u8 tx0, w3dcgb_u8 ty0, w3dcgb_u8 tx1, w3dcgb_u8 ty1, w3dcgb_u8 color, w3dcgb_u8 sides);参数
tx0图块单位的 X 坐标。
ty0图块单位的 Y 坐标。
tx1图块单位的 X 坐标。
ty1图块单位的 Y 坐标。
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
sides矩形边掩码:1=上、2=下、4=左、8=右。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
排列字节型图块坐标并裁剪到 16×12,再替换选定边界图格。边掩码 1/2/4/8 表示上/下/左/右。颜色零表示 MAIN,大于 3 时限制为 3。保留属性及内部图格。若要四角互不重合,至少使用两行两列;重合的角会按顺序覆盖。需要完整运行时及稳定的 WRAM 映射。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:163
kitaqgb/lib/wire3d_cgb.c:10170
void Wire3DCGB_FastMapRect(w3dcgb_u8 tx0, w3dcgb_u8 ty0, w3dcgb_u8 tx1, w3dcgb_u8 ty1, w3dcgb_u8 color, w3dcgb_u8 sides)
{
w3dcgb_u8 t;
if (tx0 > tx1)
{
t = tx0;
tx0 = tx1;
tx1 = t;
}
if (ty0 > ty1)
{
t = ty0;
ty0 = ty1;
ty1 = t;
}
if (tx0 >= 16) return;
if (ty0 >= 12) return;
if (tx1 >= 16) tx1 = 15;
if (ty1 >= 12) ty1 = 11;
if (color == 0) color = WIRE3DCGB_COLOR_MAIN;
if (color > 3) color = 3;
w3dcgb_fm_x0 = tx0;
w3dcgb_fm_y0 = ty0;
w3dcgb_fm_x1 = tx1;
w3dcgb_fm_y1 = ty1;
w3dcgb_fm_count_x = (w3dcgb_u8)(tx1 - tx0 + 1);
w3dcgb_fm_count_y = (w3dcgb_u8)(ty1 - ty0 + 1);
w3dcgb_fm_tile_base = (w3dcgb_u8)(W3DCGB_FAST_TILE_BASE + ((color - 1) << 4));
w3dcgb_fm_sides = (w3dcgb_u8)(sides & 15);
w3dcgb_fast_map_rect_asm();
}Wire3DCGB_GetFullScreenOverflow
报告全屏绘制中的无效坐标或图块分配耗尽。
语法
w3dcgb_u8 Wire3DCGB_GetFullScreenOverflow();返回值
无故障时为零,分配耗尽或坐标无效后为一。
使用条件与限制
返回全屏帧故障标志:快速线条路径遇到图块耗尽或无效坐标时置位。之后的快速像素写入受到抑制,直到 BeginFrame 重置。此值不是通用硬件状态,读取函数需要完整运行时。
全屏图块分配发生在遮挡判断和溢出抑制写入之前。已分配图块可能仍为黑色,出现故障后继续绘制仍可能增加分配计数。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// One point per tile exposes the 127-tile limit without relying on text alone.
#include "wire_cgb_example.h"
void main() {
u8 i;
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame(); Wire3DCGB_SetLineColor(3);
for(i=0;i<128;i++) Wire3DCGB_DrawPoint2D((i%20)*8+4,(i/20)*8+4);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
127 个红点占据不同图块。尝试分配第 128 个图块时计数仍为 127,溢出标志变为 1。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_capacity.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_capacity.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:194
kitaqgb/lib/wire3d_cgb.c:9965
w3dcgb_u8 Wire3DCGB_GetFullScreenOverflow()
{
return w3dcgb_full_overflow;
}Wire3DCGB_GetFullScreenTileCount
返回当前帧全屏绘制已分配的图块数。
语法
w3dcgb_u8 Wire3DCGB_GetFullScreenTileCount();返回值
已分配图块数,范围 0~127。
使用条件与限制
返回本帧全屏已分配图块数,最多 127。不是字节数,也不是可见或非零像素数。仅完整运行时可用。
全屏图块分配发生在遮挡判断和溢出抑制写入之前。已分配图块可能仍为黑色,出现故障后继续绘制仍可能增加分配计数。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// One point per tile exposes the 127-tile limit without relying on text alone.
#include "wire_cgb_example.h"
void main() {
u8 i;
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame(); Wire3DCGB_SetLineColor(3);
for(i=0;i<128;i++) Wire3DCGB_DrawPoint2D((i%20)*8+4,(i/20)*8+4);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
127 个红点占据不同图块。尝试分配第 128 个图块时计数仍为 127,溢出标志变为 1。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_capacity.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_capacity.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:193
kitaqgb/lib/wire3d_cgb.c:9956
w3dcgb_u8 Wire3DCGB_GetFullScreenTileCount()
{
return w3dcgb_full_tile_count;
}Wire3DCGB_GetLineColor
读取当前保存的线条颜色索引。
语法
w3dcgb_u8 Wire3DCGB_GetLineColor();返回值
当前保存的线条颜色值。
使用条件与限制
返回当前保存的两位线条颜色,不改变绘图状态。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:120
kitaqgb/lib/wire3d_cgb.c:1147
w3dcgb_u8 Wire3DCGB_GetLineColor()
{
return w3dcgb_line_color;
}Wire3DCGB_Init
初始化 CGB 的 128×96 像素渲染器。
语法
void Wire3DCGB_Init();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
初始化仅用于 CGB 的 128×96 渲染器:启用双倍速,在 VBlank 关闭 LCD,清空两个图块存储体,载入 HUD 和生成的图块,并建立按列排列的 16×12 视口。初始显示空白图块存储体 1,滚动为零,使用默认背景调色板、颜色 2 和零值相机。重置队列并清空存储体 2 的暂存区。绘图前调用 BeginFrame 初始化遮挡状态。初始化会整体替换 LCD、图块和背景图状态。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:113
kitaqgb/lib/wire3d_cgb.c:7994
void Wire3DCGB_Init()
{
w3dcgb_u8 row;
w3dcgb_u8 col;
w3dcgb_u8 tid;
w3dcgb_u8 tile_index;
w3dcgb_u16 off;
w3dcgb_full_mode = 0;
w3dcgb_atomic_maps = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_load_hud_tiles();
#ifndef WIRE3DCGB_MINIMAL_RUNTIME
w3dcgb_load_fast_map_tiles();
w3dcgb_load_fast_stamp_tiles();
#endif
w3dcgb_fill_bg_map_asm();
w3dcgb_fill_attr_map_asm();
row = 0;
while (row < W3DCGB_TILE_H)
{
off = (w3dcgb_u16)((w3dcgb_u16)row << 5);
tile_index = row;
col = 0;
while (col < W3DCGB_TILE_W)
{
if (tile_index < 112) tid = (w3dcgb_u8)(0x90 + tile_index);
else tid = (w3dcgb_u8)(tile_index - 112);
w3dcgb_bg_map_9800[(w3dcgb_u16)(off + (w3dcgb_u16)col)] = tid;
tile_index = (w3dcgb_u8)(tile_index + W3DCGB_TILE_H);
col = (w3dcgb_u8)(col + 1);
}
row = (w3dcgb_u8)(row + 1);
}
w3dcgb_select_blank_render_bank_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_reg_lcdc = 0x81;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_dirty_min_tile = 0xFF;
w3dcgb_dirty_max_tile = 0;
// Both back banks may contain generated fast-map/stamp tiles. Refresh
// the whole pixel viewport on their first sparse use, including empty rows.
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_display_tile_bank = 1;
w3dcgb_pending_tile_bank = 1;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_clear_stage_asm();
}Wire3DCGB_InitFastMapLite
使用预计算轮廓图块初始化 FastMap 渲染。
语法
void Wire3DCGB_InitFastMapLite();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
初始化带 HUD 和 48 个预计算轮廓图块的 CGB 背景图渲染器。清空图块 VRAM 和背景图,重置滚动、调色板和相机,选择普通模式。不建立普通像素视口布局,也不载入生成的立方体贴片;此模式应使用 FastMap API。清空存储体 2 的暂存区,标记 FastMap 准备工作待执行。需要完整运行时构建。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// FastMap uses precomputed outline tiles rather than a pixel stage upload.
#include "wire_cgb_example.h"
void main() {
u8 bank;
wire_results_clear(); Wire3DCGB_InitFastMapLite(); wire_colors();
bank=sample_svbk;sample_svbk=2;
Wire3DCGB_FastMapBegin();
Wire3DCGB_FastMapCell(1,1,1,15); // This first red square must disappear.
Wire3DCGB_FastMapFlush();
Wire3DCGB_FastMapBeginTilesOnly();
Wire3DCGB_FastMapCell(13,1,1,1);
Wire3DCGB_FastMapCell(13,1,1,4); // Same-color top and left sides combine.
Wire3DCGB_FastMapRect(4,2,9,7,2,15);
Wire3DCGB_FastMapCell(2,9,3,15);
Wire3DCGB_FastMapFlushTilesOnly();
sample_svbk=bank;result[7]=0xA55A;
while(1) {}
}预期结果
红色拐角、大蓝色矩形和单图块绿色边框展示轮廓图格。最初左上角的红色图格会消失;仅更新图块编号时,属性保持不变。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_fastmap.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_fastmap.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:115
kitaqgb/lib/wire3d_cgb.c:8108
void Wire3DCGB_InitFastMapLite()
{
w3dcgb_atomic_maps = 0;
w3dcgb_full_mode = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_load_hud_tiles();
w3dcgb_load_fast_map_tiles_lite_asm();
w3dcgb_fill_bg_map_asm();
w3dcgb_fill_attr_map_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_reg_lcdc = 0x81;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_dirty_min_tile = 0xFF;
w3dcgb_dirty_max_tile = 0;
w3dcgb_prev_dirty_min_tile = 0xFF;
w3dcgb_prev_dirty_max_tile = 0;
w3dcgb_fast_map_ready = 0;
w3dcgb_display_tile_bank = 0;
w3dcgb_pending_tile_bank = 0;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_clear_stage_asm();
}Wire3DCGB_InitFullScreen
初始化 160×144 渲染器,在绘制像素时按需分配图块。
语法
void Wire3DCGB_InitFullScreen();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
初始化仅用于 CGB 的 160×144 图块分配器。清空两个图块存储体及 9800 背景图,设置滚动、调色板、颜色和相机,重置图块计数、查找表及扫描线区间状态,再启用无符号背景图块寻址。不载入 HUD 图块。须预留固定的 WRAM 查找表、像素数据和地址列表,全屏绘制期间保持同一 WRAM 存储体映射。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// A large frame demonstrates the 160x144 renderer and its allocated-tile count.
#include "wire_cgb_example.h"
void main() {
wire_results_clear();
Wire3DCGB_InitFullScreen(); wire_colors();
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,151,8);
Wire3DCGB_DrawLine2D(8,8,8,135);
Wire3DCGB_DrawLine2DColor(8,135,151,135,2);
Wire3DCGB_DrawLine2DColor(151,8,151,135,3);
result[0]=Wire3DCGB_GetFullScreenTileCount();
result[1]=Wire3DCGB_GetFullScreenOverflow();
Wire3DCGB_EndFrameFast();
result[7]=0xA55A;
while(1) {}
}预期结果
大边框覆盖 (8,8)~(151,135):上边和左边为红色,下边为蓝色,右边为绿色。使用 64 个图块,溢出计数为零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_full.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_full.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:114
kitaqgb/lib/wire3d_cgb.c:8070
void Wire3DCGB_InitFullScreen()
{
w3dcgb_atomic_maps = 0;
Wire3DCGB_EnableDoubleSpeed();
w3dcgb_wait_vblank_start();
w3dcgb_reg_lcdc = 0;
w3dcgb_clear_vram_asm();
w3dcgb_full_clear_map_asm();
w3dcgb_reg_scx = 0;
w3dcgb_reg_scy = 0;
w3dcgb_reg_bgp = 0xB4;
Wire3DCGB_SetPaletteRGB15(
WIRE3DCGB_RGB15(0, 0, 2),
WIRE3DCGB_RGB15(10, 20, 31),
WIRE3DCGB_RGB15(31, 31, 31),
WIRE3DCGB_RGB15(31, 14, 4));
w3dcgb_line_color = WIRE3DCGB_COLOR_DEFAULT;
w3dcgb_bgq_count = 0;
w3dcgb_dirty_count = 0;
w3dcgb_prev_dirty_count = 0;
w3dcgb_display_tile_bank = 0;
w3dcgb_pending_tile_bank = 0;
w3dcgb_full_tile_count = 0;
w3dcgb_full_prev_tile_count = 0;
w3dcgb_full_overflow = 0;
w3dcgb_full_mode = 1;
Wire3DCGB_SetCamera((w3dcgb_i16)0, (w3dcgb_i16)0, (w3dcgb_i16)0, 0, 0, 0);
w3dcgb_full_begin_asm();
w3dcgb_reg_lcdc = 0x91;
}Wire3DCGB_InvalidateFrameHistory
将当前及前两次传输范围扩展到普通模式全部 192 个图块。
语法
void Wire3DCGB_InvalidateFrameHistory();返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
将当前及前两次稀疏范围都标记为普通模式全部 192 个图块。不清除像素,后续上传不再依赖旧范围。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:142
kitaqgb/lib/wire3d_cgb.c:9712
void Wire3DCGB_InvalidateFrameHistory()
{
w3dcgb_dirty_min_tile = 0;
w3dcgb_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_prev_dirty_min_tile = 0;
w3dcgb_prev_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
w3dcgb_older_dirty_min_tile = 0;
w3dcgb_older_dirty_max_tile = (w3dcgb_u8)(WIRE3DCGB_TILE_COUNT - 1);
}Wire3DCGB_MarkDirtyRect2D
将一个像素矩形加入后续需要上传的范围。
语法
void Wire3DCGB_MarkDirtyRect2D(w3dcgb_u8 min_x, w3dcgb_u8 min_y, w3dcgb_u8 max_x, w3dcgb_u8 max_y);参数
min_x视口像素坐标中的较小 X 端点;排序与裁剪规则见本函数说明。
min_y视口像素坐标中的较小 Y 端点;排序与裁剪规则见本函数说明。
max_x视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
max_y视口像素坐标中的较大 X 端点;排序与裁剪规则见本函数说明。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
标记一个包含端点的像素矩形供后续传输,不实际绘制。端点须按大小顺序提供;超出 127/95 的值会限制在边界内。此函数跟踪 128×96 图块,不操作全屏图块分配器。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Reuse both VRAM banks and remove old geometry while presenting sparse frames.
#include "wire_cgb_example.h"
void main() {
wire_results_clear(); Wire3DCGB_Init();
Wire3DCGB_ClearFrameTiles();
Wire3DCGB_EnableAtomicMaps(); wire_colors();
// First frame: red on the left. Fast begin invalidates both bank histories.
Wire3DCGB_BeginFrameFast(); wire_frame(8,8,32,32,1);
Wire3DCGB_EndFrameFast();
// Second frame: blue in the middle, with the old red frame removed.
Wire3DCGB_BeginFrameSparse(); wire_frame(40,40,64,64,2);
Wire3DCGB_EndFrameSparse();
// Third frame: explicitly clearing a stage discards its first green point.
Wire3DCGB_BeginFrameSparse();
Wire3DCGB_SetLineColor(3); Wire3DCGB_DrawPoint2D(4,4);
Wire3DCGB_ClearSparseStageFast();
// The final image contains only this green frame on the right.
wire_frame(80,8,112,32,3);
Wire3DCGB_MarkDirtyRect2D(80,8,112,32);
Wire3DCGB_InvalidateFrameHistory();
Wire3DCGB_EndFrameSparseNow();
result[0]=Wire3DCGB_GetLineColor();
result[7]=0xA55A;
while(1) {}
}预期结果
之前的红色和蓝色边框消失,只保留 (80,8)~(112,32) 的绿色边框,展示稀疏上传和历史范围管理。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_sparse.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_sparse.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:126
kitaqgb/lib/wire3d_cgb.c:1288
void Wire3DCGB_MarkDirtyRect2D(w3dcgb_u8 min_x, w3dcgb_u8 min_y, w3dcgb_u8 max_x, w3dcgb_u8 max_y)
{
w3dcgb_mark_dirty_rect(min_x, min_y, max_x, max_y);
}Wire3DCGB_MarkPackedSilhouette2D
在 128×96 遮挡位图中逐行标记紧凑编码的轮廓。
语法
void Wire3DCGB_MarkPackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count, w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y);参数
profile每行一个字节的轮廓数组,0xFF 表示跳过该行。
count提供的数组元素数;具体处理上限见本函数说明。
y0从中心 Y 到第一行轮廓的带符号偏移。
x中心的视口像素 X 坐标。
y中心的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
在 128×96 位图遮罩中标记最多 29 行轮廓。每字节高半字节表示中心偏移(数值减 7),低半字节表示半宽,0xFF 跳过该行。行坐标为 y+y0+i,左右扩展两像素后裁剪到视口。profile 至少有 count 个可读字节;空指针被忽略。不更新全屏区间,也不启用遮挡。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形遮罩隐藏红色扫描线的中间部分,紧凑轮廓遮罩隐藏蓝色线条;绿色轮廓标出遮挡区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:151
kitaqgb/lib/wire3d_cgb.c:1991
void Wire3DCGB_MarkPackedSilhouette2D(const w3dcgb_u8* profile, w3dcgb_u8 count,
w3dcgb_i8 y0, w3dcgb_u8 x, w3dcgb_u8 y)
{
w3dcgb_u8 i;
w3dcgb_u8 packed;
w3dcgb_u8 half_width;
w3dcgb_i16 center;
w3dcgb_i16 left;
w3dcgb_i16 right;
w3dcgb_i16 row;
if (profile == 0) return;
if (count > 29) count = 29;
i = 0;
while (i < count)
{
packed = profile[(__safe_index w3dcgb_u8)i];
if (packed != 0xFF)
{
row = (w3dcgb_i16)((w3dcgb_i16)y + (w3dcgb_i16)y0 + (w3dcgb_i16)i);
if ((row >= 0) && (row < WIRE3DCGB_SCREEN_H))
{
center = (w3dcgb_i16)((w3dcgb_i16)x + (w3dcgb_i16)(packed >> 4) - 7);
half_width = (w3dcgb_u8)(packed & 15);
/* Two-pixel guard covers rasterized endpoints at 22.5-degree views. */
left = (w3dcgb_i16)(center - (w3dcgb_i16)half_width - 2);
right = (w3dcgb_i16)(center + (w3dcgb_i16)half_width + 2);
if ((right >= 0) && (left < WIRE3DCGB_SCREEN_W))
{
if (left < 0) left = 0;
if (right >= WIRE3DCGB_SCREEN_W) right = WIRE3DCGB_SCREEN_W - 1;
w3dcgb_mask_span_y = (w3dcgb_u8)row;
w3dcgb_mask_span_min_x = (w3dcgb_u8)left;
w3dcgb_mask_span_max_x = (w3dcgb_u8)right;
w3dcgb_mask_set_span_asm();
}
}
}
i = (w3dcgb_u8)(i + 1);
}
}Wire3DCGB_MarkTriangle2D
将三角形登记为覆盖区域,用于隐藏随后绘制的线条。
语法
void Wire3DCGB_MarkTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy);参数
ax顶点 A 的视口像素 X 坐标。
ay顶点 A 的视口像素 Y 坐标。
bx顶点 B 的视口像素 X 坐标。
by顶点 B 的视口像素 Y 坐标。
cx顶点 C 的视口像素 X 坐标。
cy顶点 C 的视口像素 Y 坐标。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
标记屏幕空间三角形,不绘制像素,也不启用遮挡。顶点须位于所选视口内。实际汇编会在 X 方向增加一像素保护边距,128×96 模式限制在 0~127,160×144 模式限制在 0~159。本函数不裁剪任意越界三角形。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形遮罩隐藏红色扫描线的中间部分,紧凑轮廓遮罩隐藏蓝色线条;绿色轮廓标出遮挡区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:150
kitaqgb/lib/wire3d_cgb.c:1980
void Wire3DCGB_MarkTriangle2D(w3dcgb_u8 ax, w3dcgb_u8 ay, w3dcgb_u8 bx, w3dcgb_u8 by, w3dcgb_u8 cx, w3dcgb_u8 cy)
{
w3dcgb_mark_triangle(ax, ay, bx, by, cx, cy);
}Wire3DCGB_ProjectAxis48
用焦距 48 近似计算一个投影偏移。
语法
w3dcgb_i16 Wire3DCGB_ProjectAxis48(w3dcgb_i16 value, w3dcgb_i16 z);参数
value待投影的带符号 16 位分量。
z用于选择尺寸或计算投影的深度。
返回值
近似计算的带符号 16 位投影偏移。
使用条件与限制
近似计算 value×48/z,不使用相机或屏幕中心偏移。负数或过小的 Z 按 4 处理。Z 大于 255 时,将深度和分量绝对值同时右移直至深度可用字节表示,再将绝对值上限设为 160。返回带符号偏移;本函数不按近远深度拒绝输入。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Preprojected edge arrays, a clipped square, a tiny marker and a projectile.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Edge edges[4]={{0,1},{1,2},{2,3},{3,0}};
__prg_rom s8 vx8[4]={-8,8,8,-8};
__prg_rom s8 vy8[4]={-8,-8,8,8};
__prg_rom s16 vx16[4]={-12,12,12,-12};
__prg_rom s16 vy16[4]={-8,-8,8,8};
__prg_rom u8 mask[1]={5};
Wire3DCGB_Model shape;
void main() {
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
shape.vertices=0;shape.edges=edges;shape.faces=0;shape.edge_faces=0;
shape.vertex_count=4;shape.edge_count=4;shape.face_count=0;shape.flags=0;
Wire3DCGB_SetLineColor(2);
Wire3DCGB_DrawMaskedModel2D(&shape,vx8,vy8,mask,24,24,1);
Wire3DCGB_DrawEdgeList2D(edges,4,vx8,vy8,64,24,2);
Wire3DCGB_DrawEdgeListClipped2D(edges,4,vx16,vy16,4,4,64,3);
Wire3DCGB_DrawTinyModel2D(104,24,16,3);
result[0]=Wire3DCGB_ProjectAxis48(48,96);
result[1]=Wire3DCGB_ProjectAxis48(-48,96);
Wire3DCGB_DrawFastProjectile(96,64,64,0,1);
result[2]=Wire3DCGB_GetLineColor();
Wire3DCGB_EndFrame();result[7]=0xA55A;
while(1) {}
}预期结果
两条红色水平边、蓝色正方形、经过裁剪的绿色边框、绿色六像素标记和红色弹体,用来对比各列表格式。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_lists.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_lists.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:141
kitaqgb/lib/wire3d_cgb.c:9702
w3dcgb_i16 Wire3DCGB_ProjectAxis48(w3dcgb_i16 value, w3dcgb_i16 z)
{
w3dcgb_projection_value = value;
w3dcgb_projection_z = z;
w3dcgb_project_axis48_asm();
return w3dcgb_projection_result;
}Wire3DCGB_ProjectPoint
通过相机将世界坐标点投影到 128×96 视口。
语法
w3dcgb_u8 Wire3DCGB_ProjectPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);参数
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
sx接收投影 X 坐标的有效可写 u8 指针。
sy接收投影 Y 坐标的有效可写 u8 指针。
返回值
投影成功返回 1,深度被拒绝返回 0。
使用条件与限制
通过当前相机将世界坐标点投影到 128×96 视口。深度被拒绝时返回零,输出不变;成功时写入限制在视口内的字节坐标。输出指针必须可写且有效。这不是 160×144 全屏投影函数,MINIMAL_RUNTIME 构建不包含它。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:169
kitaqgb/lib/wire3d_cgb.c:589
w3dcgb_u8 Wire3DCGB_ProjectPoint(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy)
{
return w3dcgb_project_world(x, y, z, sx, sy);
}Wire3DCGB_ProjectPointNoRotation
减去相机位置后投影点,不执行相机旋转。
语法
w3dcgb_u8 Wire3DCGB_ProjectPointNoRotation(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_u8* sx, w3dcgb_u8* sy);参数
x点或物体原点的世界坐标 X 分量。
y点或物体原点的世界坐标 Y 分量。
z点或物体原点的世界坐标 Z 分量。
sx接收投影 X 坐标的有效可写 u8 指针。
sy接收投影 Y 坐标的有效可写 u8 指针。
返回值
投影成功返回 1,深度被拒绝返回 0。
使用条件与限制
适用于相机俯仰、偏航和滚转始终为零的游戏。减去相机位置后投影到 128×96,忽略全部相机角度。深度无效时不改变输出;成功时将坐标限制在视口内。MINIMAL_RUNTIME 构建不包含此函数。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:171
Fast path for games whose camera pitch, yaw, and roll stay at zero.
kitaqgb/lib/wire3d_cgb.c:598
w3dcgb_u8 Wire3DCGB_ProjectPointNoRotation(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z,
w3dcgb_u8* sx, w3dcgb_u8* sy)
{
return w3dcgb_project_camera_space((w3dcgb_i16)(x - w3dcgb_cam_x),
(w3dcgb_i16)(y - w3dcgb_cam_y),
(w3dcgb_i16)(z - w3dcgb_cam_z), sx, sy);
}Wire3DCGB_PutBgTile
将背景图块编号变更加入 EndFrame 队列。
语法
void Wire3DCGB_PutBgTile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 tile);参数
x32×32 背景图中的图块 X 坐标。
y32×32 背景图中的图块 Y 坐标。
tile背景图格的八位图块编号。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
为 32×32 背景图排队一次图块编号写入。忽略无效坐标以及已有 48 条待处理写入之后的提交。EndFrame 提交此私有队列;原子背景图模式会镜像写入两张背景图。属性不入队。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:188
kitaqgb/lib/wire3d_cgb.c:1067
void Wire3DCGB_PutBgTile(w3dcgb_u8 x, w3dcgb_u8 y, w3dcgb_u8 tile)
{
if (x >= 32) return;
if (y >= 32) return;
if (w3dcgb_bgq_count >= W3DCGB_BG_QUEUE_LIMIT) return;
w3dcgb_bgq_x[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = x;
w3dcgb_bgq_y[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = y;
w3dcgb_bgq_tile[(__safe_index w3dcgb_u8)w3dcgb_bgq_count] = tile;
w3dcgb_bgq_count = (w3dcgb_u8)(w3dcgb_bgq_count + 1);
}WIRE3DCGB_RGB15
将五位红、绿、蓝分量打包为 CGB 的 15 位颜色。
语法
#define WIRE3DCGB_RGB15(r5, g5, b5) ((w3dcgb_u16)((w3dcgb_u16)((r5) & 31) | ((w3dcgb_u16)((g5) & 31) << 5) | ((w3dcgb_u16)((b5) & 31) << 10)))参数
r5红色分量,0~31。
g5绿色分量,0~31。
b5蓝色分量,0~31。
返回值
以 u16 表示的 RGB15 打包值。
使用条件与限制
将各分量的低五位打包为 RGB15,每个参数只求值一次。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:50
Wire3DCGB_SetCamera
保存投影使用的相机位置与朝向,每周使用 16 档角度。
语法
void Wire3DCGB_SetCamera(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 pitch, w3dcgb_i8 yaw, w3dcgb_i8 roll);参数
x相机的世界坐标 X 分量。
y相机的世界坐标 Y 分量。
z相机的世界坐标 Z 分量。
pitch绕 X 轴旋转,一周为 16 档。
yaw绕 Y 轴旋转,一周为 16 档。
roll绕 Z 轴旋转,一周为 16 档。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
保存相机位置和角度,不改变已有像素。旋转辅助函数使用角度低四位,即一周 16 档。屏幕偏移由独立的 LCD 寄存器管理,此调用不改变它们。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Four cubes compare unity/half scale and persistent/temporary line colors.
#include "wire_cgb_example.h"
__prg_rom Wire3DCGB_Vec3 vertices[8]={
{-16,-16,-16},{16,-16,-16},{16,16,-16},{-16,16,-16},
{-16,-16,16},{16,-16,16},{16,16,16},{-16,16,16}
};
__prg_rom Wire3DCGB_Edge edges[12]={
{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},
{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}
};
Wire3DCGB_Model cube;
u8 sx;
u8 sy;
void main() {
wire_results_clear();
cube.vertices=vertices; cube.edges=edges;
cube.faces=0; cube.edge_faces=0;
cube.vertex_count=8; cube.edge_count=12; cube.face_count=0; cube.flags=0;
Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawModel(&cube,-32,32,96,0,0,0);
Wire3DCGB_DrawModelScaled(&cube,32,32,96,0,0,0,128);
Wire3DCGB_DrawModelColor(&cube,-32,-32,96,0,0,0,2);
Wire3DCGB_DrawModelScaledColor(&cube,32,-32,96,0,0,0,128,3);
result[0]=Wire3DCGB_GetLineColor();
Wire3DCGB_DrawLine3D(0,56,96,0,-56,96);
Wire3DCGB_DrawLine3DColor(-80,0,96,80,0,96,3);
result[1]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[2]=sx; result[3]=sy;
// Camera setters affect future projection, leaving the staged cubes intact.
Wire3DCGB_SetCamera(0,0,0,0,4,0);
result[4]=Wire3DCGB_ProjectPoint(0,0,96,&sx,&sy);
result[5]=Wire3DCGB_ProjectPointNoRotation(0,0,96,&sx,&sy);
result[6]=sx;
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
对比红、蓝、绿的原比例及缩放模型,以及交叉的三维线条。RAM 数值还检查相机投影及不旋转的投影变体。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_models.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_models.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:127
kitaqgb/lib/wire3d_cgb.c:8266
void Wire3DCGB_SetCamera(w3dcgb_i16 x, w3dcgb_i16 y, w3dcgb_i16 z, w3dcgb_i8 pitch, w3dcgb_i8 yaw, w3dcgb_i8 roll)
{
w3dcgb_cam_x = x;
w3dcgb_cam_y = y;
w3dcgb_cam_z = z;
w3dcgb_cam_pitch = pitch;
w3dcgb_cam_yaw = yaw;
w3dcgb_cam_roll = roll;
}Wire3DCGB_SetLineColor
选择后续线条绘制使用的颜色索引。
语法
void Wire3DCGB_SetLineColor(w3dcgb_u8 color);参数
color绘图颜色索引;示例调色板中 1=红色、2=蓝色、3=绿色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
保存 color & 3。建议使用命名的线条颜色 1~3:普通线条对位平面执行按位或,因此交叉处的颜色会组合;全屏快速线条则替换像素颜色。线条颜色零按 3 处理,不能用于擦除。普通模式的点绘制另行支持用颜色零擦除。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:119
kitaqgb/lib/wire3d_cgb.c:1140
void Wire3DCGB_SetLineColor(w3dcgb_u8 color)
{
w3dcgb_line_color = (w3dcgb_u8)(color & 3);
}Wire3DCGB_SetOcclusionActive
启用或禁用后续线条的遮挡检测。
语法
void Wire3DCGB_SetOcclusionActive(w3dcgb_u8 active);参数
active零表示禁用遮挡检测,其他值表示启用。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
将 active 归一为零或一,保存为遮挡开关。不清除遮罩,也不标记几何覆盖。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// Red/blue scanlines stop inside masks; green outlines identify the triangle.
#include "wire_cgb_example.h"
__prg_rom u8 silhouette[5]={0x73,0x74,0xFF,0x75,0x73};
void main() {
u8 y;
wire_results_clear(); Wire3DCGB_Init(); wire_colors(); Wire3DCGB_BeginFrame();
// Clearing this first test mask must leave the upper-left scanline intact.
Wire3DCGB_MarkTriangle2D(8,8,16,8,8,16);
Wire3DCGB_ClearOcclusionMask();
Wire3DCGB_MarkTriangle2D(32,16,64,16,32,48);
Wire3DCGB_MarkPackedSilhouette2D(silhouette,5,-2,104,32);
Wire3DCGB_SetOcclusionActive(1);
for(y=8;y<=56;y=y+4) Wire3DCGB_DrawLine2DColor(8,y,88,y,1);
for(y=28;y<=36;y++) Wire3DCGB_DrawLine2DColor(92,y,120,y,2);
Wire3DCGB_SetOcclusionActive(0);
Wire3DCGB_DrawLine2DColor(32,16,64,16,3);
Wire3DCGB_DrawLine2DColor(64,16,32,48,3);
Wire3DCGB_DrawLine2DColor(32,48,32,16,3);
Wire3DCGB_EndFrame(); result[7]=0xA55A;
while(1) {}
}预期结果
三角形遮罩隐藏红色扫描线的中间部分,紧凑轮廓遮罩隐藏蓝色线条;绿色轮廓标出遮挡区域。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_mask.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_mask.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:149
kitaqgb/lib/wire3d_cgb.c:1970
void Wire3DCGB_SetOcclusionActive(w3dcgb_u8 active)
{
if (active == 0) w3dcgb_occlusion_active = 0;
else w3dcgb_occlusion_active = 1;
}Wire3DCGB_SetPaletteRGB15
设置四种绘图颜色及用于贴片着色的背景调色板。
语法
void Wire3DCGB_SetPaletteRGB15(w3dcgb_u16 color0, w3dcgb_u16 color1, w3dcgb_u16 color2, w3dcgb_u16 color3);参数
color0调色板条目 0 的 RGB15 颜色。
color1调色板条目 1 的 RGB15 颜色。
color2调色板条目 2 的 RGB15 颜色。
color3调色板条目 3 的 RGB15 颜色。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
用提供的四个 RGB15 颜色设置背景调色板 0。调色板 1~3 共用 color0,并分别将对应的非零颜色重复填入条目 1~3,使单色贴片可通过属性着色。调用时须处于可访问调色板的时段,通常为 LCD 关闭的初始化阶段或 VBlank。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:118
kitaqgb/lib/wire3d_cgb.c:1112
void Wire3DCGB_SetPaletteRGB15(w3dcgb_u16 color0, w3dcgb_u16 color1, w3dcgb_u16 color2, w3dcgb_u16 color3)
{
w3dcgb_write_bg_color(0, color0);
w3dcgb_write_bg_color(1, color1);
w3dcgb_write_bg_color(2, color2);
w3dcgb_write_bg_color(3, color3);
w3dcgb_write_bg_color(4, color0);
w3dcgb_write_bg_color(5, color1);
w3dcgb_write_bg_color(6, color1);
w3dcgb_write_bg_color(7, color1);
w3dcgb_write_bg_color(8, color0);
w3dcgb_write_bg_color(9, color2);
w3dcgb_write_bg_color(10, color2);
w3dcgb_write_bg_color(11, color2);
w3dcgb_write_bg_color(12, color0);
w3dcgb_write_bg_color(13, color3);
w3dcgb_write_bg_color(14, color3);
w3dcgb_write_bg_color(15, color3);
}Wire3DCGB_SetScreenOffset
直接设置 LCD 滚动寄存器 SCX 和 SCY。
语法
void Wire3DCGB_SetScreenOffset(w3dcgb_u8 scx, w3dcgb_u8 scy);参数
scx写入 SCX 的像素滚动偏移。
scy写入 SCY 的像素滚动偏移。
返回值
无返回值;所修改的状态及显示时机见上方说明。
使用条件与限制
立即写入 SCX/SCY,不等待 VBlank,也不改变投影坐标。调用方应选择允许画面滚动变化的时机。
仅用于 CGB。普通暂存区占用 WRAM 存储体 2 的 D300~DEFF。函数共享状态,不能重入,不要从中断递归调用。示例使用固定栈和完整运行时。
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
使用示例
#pragma bank 0
// CGB raster paths: a red frame, blue diagonal, green marker and clipped line.
// Build this source together with lib/wire3d_cgb.c in an MBC5 256 KB CGB ROM.
#include "wire3d_cgb.h"
__location(0xFF40) u8 sample_lcdc;
__location(0xFF44) u8 sample_ly;
__location(0xFF4D) u8 sample_key1;
__location(0xC110) u16 result[8];
__prg_rom u8 lower_lines[8]={80,56,112,56,112,56,112,80};
void main() {
u8 i;
for(i=0;i<8;i++) result[i]=0;
Wire3DCGB_EnableDoubleSpeed();
Wire3DCGB_Init(); // Already at double speed: its internal request is a no-op.
result[2]=(u8)(sample_key1&128);
// Palette writes are performed with the LCD off, after entering VBlank.
while(sample_ly>=144) {} while(sample_ly<144) {}
sample_lcdc=0;
Wire3DCGB_SetPaletteRGB15(WIRE3DCGB_RGB15(0,0,0),WIRE3DCGB_RGB15(31,0,0),WIRE3DCGB_RGB15(0,0,31),WIRE3DCGB_RGB15(0,31,0));
sample_lcdc=0x81;
Wire3DCGB_SetScreenOffset(0,0);
Wire3DCGB_SetCamera(0,0,0,0,0,0);
Wire3DCGB_BeginFrame();
Wire3DCGB_SetLineColor(1);
Wire3DCGB_DrawLine2D(8,8,56,8);
Wire3DCGB_DrawLine2D(56,8,56,40);
Wire3DCGB_DrawLine2D(56,40,8,40);
Wire3DCGB_DrawLine2D(8,40,8,8);
Wire3DCGB_DrawLine2DColor(8,40,56,8,2);
result[0]=Wire3DCGB_GetLineColor(); // Temporary blue restores red.
Wire3DCGB_SetLineColor(3);
Wire3DCGB_DrawPoint2D(64,48);
Wire3DCGB_DrawLineClipped2D(-10,72,140,72,3);
Wire3DCGB_DrawLineList2DColor(lower_lines,2,2);
result[1]=Wire3DCGB_GetLineColor(); // The blue line list restores green.
Wire3DCGB_PutBgTile(18,1,0x80); // Queue the supplied zero glyph outside the viewport.
Wire3DCGB_EndFrame();
result[7]=0xA55A;
while(1) {}
}预期结果
本例在一帧内演示直线、点、擦除和投影。红色边框内有蓝色对角线、绿色点和水平线,右上方有蓝色 L 形及绿色数字零。
构建命令
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\wire_cgb_raster.c .\kitaqgb\lib\wire3d_cgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\wire_cgb_raster.gb --profile=dev --stack-bank=fixed --rst-disable --cgb=cgb --cart=mbc5 --romsize=256k --no-cache --no-disasm实现与源码
kitaqgb/lib/wire3d_cgb.h:117
kitaqgb/lib/wire3d_cgb.c:1091
void Wire3DCGB_SetScreenOffset(w3dcgb_u8 scx, w3dcgb_u8 scy)
{
w3dcgb_reg_scx = scx;
w3dcgb_reg_scy = scy;
}Wire3D耗时、88行配置与独立时钟
DMG的WIRE3D_DMG_HEIGHT可选88、96、120;CGB的WIRE3DCGB_HEIGHT可选88、96。默认仍为DMG 120、CGB 96。88行视口为128×88,中心Y=44。库与调用代码须使用相同配置,以wire3d_dmg_88.c / wire3d_cgb_88.c替代普通入口编译。DMG 88沿用96行的模型结构和16条边限制。CGB 160×144模式保持不变。
88行完整上传量为DMG 1,408字节、CGB 2,816字节。相较DMG 120行减少26.67%,相较96行减少8.33%;这不是fps提升比例。DMG保留256字节的列间距,CGB紧密排列11瓦片列。CGB仍预留3,072字节暂存区,以兼容全屏模式。
ProjectCameraPoint直接投影相机坐标,不减相机位置或旋转。Z不在8..255时返回0,输出不变。X/Y限制在±120;逆深度表乘积按有符号向下取整除以32,再限制到屏幕边界。这不是线段的几何裁剪。通用CGB ProjectPoint在零角度下仍保留旋转的±220限制。
DMG EndFrameNow仅省略开始时等待新的VBlank,仍处理BG队列、传输开关和dirty历史。CGB EndFrameSparseNow也同步执行。Now仍须等待STAT访问窗口、DMA完成和安全显示切换。DMG更新不是原子的。CGB稀疏清除包含当前范围和目标银行两帧前的范围。低层CGB线段绘制须登记dirty。
CGB稀疏显示启动时,首次使用每个目标存储区(前两帧)会刷新整个视口,清除初始化图块。测量包含启动开销。普通DrawLine2D不登记稀疏范围,请使用DrawLineClipped2D或显式调用MarkDirtyRect2D。
示例使用独立创作的移动方盒、条形仪表和四音BGM。移动按audio_vblank经过的tick更新,仪表由简短安全的VBlank处理更新。不要从ISR调用共享状态的渲染器。wire3d_audio_vblank.c将80字节队列置于固定WRAM,并将控制函数置于ROM银行5。不要同时编译普通audio_vblank.c。CGB示例使用WIRE3DCGB_STAGE_BANK2_ALLOCATION。8位tick差须在255帧内读取。
测量工具统计上传和显示完成后的标记。以PPU dot(每秒4,194,304;每帧70,224)记录清除、投影、光栅化、上传加显示和外部等待,并报告平均、中位数、最大间隔与超期次数。上传加显示包含内部STAT/DMA/VBlank等待。不要直接比较CGB双速CPU与DMG的原始周期。平均fps相同不能证明没有超期。尚未实机验证。
7种独立编写的示例配置各完成48次上传,共336次暂存区/VRAM比较及84次空帧残影清除检查。5种配置通过25,700项投影边界测试。5种Rust构建也完成48帧并输出非零BGM。有些间隔超过4个PPU帧的期限,平均fps不能证明无掉帧。此为模拟器验证,并非实机验证。
KOKURA对独立编写的DMG/CGB示例各运行400帧,SARAKURA分析及严格模式验证通过。各配置错误0项,待机循环的PC重复警告1项,并保留DAC/混音器爆音风险等3项信息。这不验证声音保真度或实机表现。
公开示例、图像与验证数据仅使用独立创作的几何图形、仪表和音型。不含商业游戏ROM、提取的代码、图形、画面、音乐或状态文件。
| 配置 | fps | 中位数 / 最差(PPU帧) | 期限超限 / 47 |
|---|---|---|---|
clocked_dmg_88_sparse | 13.852 | 4.449 / 4.732 | 35 |
clocked_dmg_96_sparse | 13.186 | 4.669 / 4.891 | 39 |
clocked_dmg_120_sparse | 13.434 | 4.453 / 7.176 | 35 |
clocked_cgb_88_sparse | 15.012 | 4.000 / 5.000 | 28 |
clocked_cgb_96_sparse | 15.092 | 4.000 / 5.000 | 26 |
clocked_cgb_88_full | 12.532 | 5.000 / 6.000 | 41 |
clocked_cgb_96_full | 12.588 | 5.000 / 5.020 | 41 |


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







