SERIE KITAQ • EDICIÓN DE REFERENCIA
KITAQGB
Programación en C para Game Boy
Código fuente recopilado: 14 de septiembre de 2026 · Edición inglesa: 14 de septiembre de 2026
Compilador nativo Rust y herramientas auxiliares
Con Rust 1.85 o posterior puede compilar el compilador y todas las herramientas para Windows, Linux, macOS ARM y macOS Intel. Los ejecutables nativos no necesitan .NET; las herramientas de recursos tampoco requieren Python ni Pillow.
Los ejecutables de Windows están en la raíz del repositorio; los de Linux y macOS, en los directorios bin/ de la tabla siguiente. Conserve lib/ y los avisos de licencia junto a las herramientas. En Linux/macOS use chmod +x y añada el directorio al PATH, o ejecute mediante la ruta completa.
| OS | kitaqgb |
|---|---|
| Windows x64 | kitaqgb.exe |
| Linux x64 (musl) | bin/linux-x86_64/kitaqgb |
| Linux x64 (GNU) | bin/linux-gnu-x86_64/kitaqgb |
| macOS ARM64 | bin/macos-arm64/kitaqgb |
| macOS Intel | bin/macos-x86_64/kitaqgb |
| macOS Universal | bin/macos-universal/kitaqgb |
kitaqgb · Compilador nativo Rust y herramientas auxiliares · GitHub Actions
Las compilaciones nativas y comprobaciones de ejecución finalizaron correctamente en Windows, Linux, macOS ARM y macOS Intel. KITAQGB superó 48 pruebas y 395 comprobaciones de herramientas por entorno; KITAQFC, 55 y 401. También se probó Rust 1.85. PUBLIC_DISTRIBUTION.json registra los hashes de los binarios instalados y el origen de la validación. Los flujos públicos de GitHub Actions compilan y prueban estas fuentes de forma independiente.
Las salidas de referencia guardadas comprueban bytes de ROM, diagnósticos y formatos de las herramientas. Las anteriores pruebas de emulación C# son registros históricos vinculados a sus fuentes originales. No demuestran automáticamente todas las API Rust, el hardware real ni el arranque completo de juegos mediante el BIOS FDS. El script PNG original no está disponible y se reconstruyó a partir de su especificación; no puede afirmarse igualdad de bytes con él.
--attach se acepta por compatibilidad, pero no conecta un depurador administrado al ejecutable nativo. Use un depurador nativo para el programa de PC.
KITAQGB · SHA-256 · KITAQFC · SHA-256
Compilación de ejemplos en cada sistema
Los scripts PowerShell usan el ejecutable Windows de la raíz. En Linux/macOS pase las mismas entradas C y opciones al compilador nativo, o use PowerShell 7. Los gráficos CHR y el código C son entradas distintas; font.chr conserva la fuente de los ejemplos.
kitaqgb game.c -I lib -o game.gb --cart=romonly --romsize=32kProgramas de ejemplo completos
Se incluyen todos los programas, las cabeceras compartidas, el archivo ascii.c suministrado y sus recursos convertidos. Los nombres que empiezan por m_ son funciones auxiliares de estos manuales. Lee cada comando de compilación junto a su resultado esperado y su registro de verificación. Ejecuta los comandos desde la carpeta que contiene los repositorios hermanos.
Script de compilación por lotes / Resultados y capturas de pantalla
Mostrar el juego de caracteres incluido gb_font.c
Objetivo: Muestra qué letras, dígitos y signos de puntuación contiene la fuente original incluida y cómo dibujarlos.
Funcionamiento: m_init prepara la pantalla y la fuente. m_text dibuja cada línea; m_put dibuja las comillas dobles mediante el código de carácter 34.
Controles y resultado esperado: Busque 26 letras mayúsculas, 26 minúsculas, 10 dígitos y 30 signos de puntuación. Compruebe que todos los glifos sean legibles. No se requiere ninguna entrada.

Descargar código fuente / Estado de la verificación
.\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(); }
}Su primer texto en pantalla gb_hello.c
Objetivo: Muestra la secuencia básica para iniciar una ROM y dibujar texto y un número en el fondo.
Funcionamiento: Después de m_init, m_text dibuja el saludo y m_number da formato a 42. El bucle final espera fotogramas y mantiene la pantalla visible.
Controles y resultado esperado: La pantalla muestra HELLO WORLD y 042. El cero inicial corresponde al formato decimal de tres dígitos.

Descargar código fuente / Estado de la verificación
.\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(); }
}Calcular con un valor intermedio más amplio gb_arithmetic.c
Objetivo: Muestra cómo conservar un resultado intermedio que no cabe en ocho bits.
Funcionamiento: (u16)200 + 100 produce 300. El programa divide el resultado entre 10 y luego lo convierte a u8 para mostrarlo. Observa que la conversión al tipo más amplio ocurre antes de la suma.
Controles y resultado esperado: 030 representa el resultado calculado, 30.

Descargar código fuente / Estado de la verificación
.\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(); }
}Bifurcaciones y bucles gb_control.c
Objetivo: Prueba for, continue, while, do/while y switch en un cálculo sencillo.
Funcionamiento: Sumar de 0 a 7 omitiendo 3 da 25. El bucle while avanza hasta 30, do/while hasta 32 y switch selecciona case 32 para asignar 42.
Controles y resultado esperado: 042 es el resultado final de estos bucles y bifurcaciones.

Descargar código fuente / Estado de la verificación
.\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(); }
}Funciones, arrays, estructuras y punteros gb_aggregate.c
Objetivo: Muestra cómo agrupar valores y acceder a ellos mediante funciones y punteros.
Funcionamiento: El programa copia un Point que contiene (10,20) y después escribe de 1 a 4 en un array mediante un puntero. Suma el resultado de la función, 30, a los valores del array 1, 2 y 4 y a la constante 5.
Controles y resultado esperado: La suma se muestra como 042. Siga la copia de la estructura, las escrituras en ptr[i] y el valor devuelto por la función.
![Funciones, arrays, estructuras y punteros: La suma se muestra como 042. Siga la copia de la estructura, las escrituras en ptr[i] y el valor devuelto por la función.](../verification/gb_aggregate.png)
Descargar código fuente / Estado de la verificación
.\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(); }
}Banderas de bits y copia de búfer gb_bits_memory.c
Objetivo: Agrupa banderas en un byte y copia un búfer en un área de almacenamiento independiente.
Funcionamiento: __memset borra cuatro bytes. El programa activa el bit 3, invierte el bit 1 y después copia los cuatro bytes con __memcpy.
Controles y resultado esperado: el primer byte de destino se muestra como 010; el bit 3 aporta 8 y el bit 1 aporta 2.

Descargar código fuente / Estado de la verificación
.\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(); }
}Contar nuevas pulsaciones de un botón gb_input.c
Objetivo: Distingue una nueva pulsación de un botón que sigue pulsado.
Funcionamiento: Cada fotograma llama a input_update. El contador solo avanza cuando input_pressed(BTN_A) es verdadero.
Controles y resultado esperado: el contador comienza en 000. Pulse A para obtener 001; mantenerlo pulsado no añade más. Suéltelo y vuelva a pulsarlo para obtener 002. El contador de un byte vuelve a 000 después de 255.

Descargar código fuente / Estado de la verificación
.\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); } }
}Multiplicar valores de punto fijo Q8.8 gb_fixed.c
Objetivo: Muestra la conversión de entero a Q8.8, la multiplicación y la conversión de vuelta a entero.
Funcionamiento: fix_from_int convierte 3 y 4 a Q8.8. fix_mul los multiplica y fix_to_int convierte el producto de vuelta a entero.
Controles y resultado esperado: 012 significa 3 × 4 = 12. Es el entero convertido, no el valor interno escalado por 256.

Descargar código fuente / Estado de la verificación
.\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(); }
}Asignar desde un pool fijo de objetos gb_entity.c
Objetivo: Crea un objeto a partir de un conjunto fijo de posiciones, sin asignación dinámica de memoria.
Funcionamiento: entity_init reinicia el pool. entity_create crea el tipo 1 en (20,30); el programa comprueba el ID de fallo 0xFF antes de establecer vx en 2.
Controles y resultado esperado: 001 es la cantidad de objetos activos, no un ID ni una coordenada. Este ejemplo no actualiza ni dibuja el objeto, por lo que no aparece ningún personaje en movimiento.

Descargar código fuente / Estado de la verificación
.\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(); }
}Registrar un valor de diagnóstico en RAM gb_debug.c
Objetivo: Conserva un valor del juego con nombre en un búfer de diagnóstico, separado de la visualización en pantalla.
Funcionamiento: El programa inicializa la depuración, establece la etiqueta de fotograma en 1 y registra el valor 42 con el nombre HP.
Controles y resultado esperado: 001 es la cantidad de entradas de traza. HP=42 se guarda en el búfer de diagnóstico; la pantalla por sí sola no muestra ni verifica ese contenido.

Descargar código fuente / Estado de la verificación
.\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(); }
}Guardar un historial de posiciones gb_chain.c
Objetivo: Muestra cómo guardar un historial de posiciones en un búfer circular.
Funcionamiento: chain_init asocia un array con espacio para cuatro puntos. Después, chain_push_head registra (20,30).
Controles y resultado esperado: 042 es un marcador fijo de finalización, no una coordenada ni una cantidad. Inspecciona trail y points para comprobar el historial guardado. Este programa no dibuja un cuerpo que siga al protagonista.

Descargar código fuente / Estado de la verificación
.\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(); }
}Leer el contador de fotogramas de la biblioteca gb_system.c
Objetivo: Muestra cómo las esperas contadas en fotogramas afectan al contador de fotogramas de la biblioteca.
Funcionamiento: Después de system_init, el programa llama dos veces a system_wait_vblank y muestra system_get_frame8.
Controles y resultado esperado: El resultado es 002. El bucle de espera utiliza la función auxiliar m_wait del manual, por lo que el valor mostrado no sigue aumentando.

Descargar código fuente / Estado de la verificación
.\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(); }
}Seleccionar el ID de la escena actual gb_scene.c
Objetivo: Muestra cómo seleccionar un ID para distinguir escenas, como la pantalla de título y la partida.
Funcionamiento: El programa registra dos entradas SceneDef inicializadas a cero y selecciona el ID 1 mediante scene_change.
Controles y resultado esperado: 001 es el ID actual devuelto por scene_get_current. El ejemplo no registra callbacks de escena ni dibujo de transiciones.

Descargar código fuente / Estado de la verificación
.\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(); }
}Convertir coordenadas del mundo en coordenadas de pantalla gb_camera.c
Objetivo: Resta la posición de la cámara para obtener la posición en pantalla de un punto del mundo.
Funcionamiento: Camera_Set recibe el valor Q8.8 2560, equivalente a X=10 píxeles. Después, Camera_WorldToScreenX convierte la coordenada del mundo X=52.
Controles y resultado esperado: 052 menos 010 da el 042 mostrado. Es un cálculo de coordenadas; no desplaza el fondo.

Descargar código fuente / Estado de la verificación
.\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(); }
}Descomprimir datos RLE en RAM gb_rle.c
Objetivo: Prueba una representación compacta de bytes repetidos mediante pares de cantidad y valor.
Funcionamiento: Los bytes comprimidos {3,42,0} representan tres copias de 42 seguidas de un terminador con cantidad cero. rle_decode escribe en un destino de tres bytes.
Controles y resultado esperado: el último byte, out[2], se muestra como 042. Los tres bytes descomprimidos deben contener 42.
![Descomprimir datos RLE en RAM: el último byte, out[2], se muestra como 042. Los tres bytes descomprimidos deben contener 42.](../verification/gb_rle.png)
Descargar código fuente / Estado de la verificación
.\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(); }
}Leer y escribir una casilla del tablero gb_board.c
Objetivo: Muestra cómo se corresponden las coordenadas de una cuadrícula con el almacenamiento de un juego de tablero.
Funcionamiento: Un array de 16 bytes almacena un tablero de 4×4. El programa lo borra, escribe 42 en la celda (2,1) y vuelve a leer esa misma celda.
Controles y resultado esperado: 042 es el valor leído de nuevo. El programa no dibuja el tablero de 4×4.

Descargar código fuente / Estado de la verificación
.\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(); }
}Avanzar una caja un paso de física gb_physics.c
Objetivo: Inicializa un mundo y un cuerpo rectangular, y después actualiza la posición a partir de la velocidad.
Funcionamiento: El cuerpo empieza en (40,40), con velocidad (2,0) y gravedad cero. El programa llama a kq2d_step una vez.
Controles y resultado esperado: 042 es la coordenada X resultante, medida en píxeles enteros en este ejemplo. No se dibuja una caja ni una animación continua.

Descargar código fuente / Estado de la verificación
.\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(); }
}Gestionar la finalización y el tiempo de espera de una transferencia serie gb_link.c
Objetivo: Distingue una transferencia completada de otra que no termina dentro del límite de espera.
Funcionamiento: El maestro envía 42 y espera con Link_WaitByte. Al finalizar, muestra el byte recibido; si vence el tiempo de espera, cancela la transferencia y muestra cero.
Controles y resultado esperado: Una transferencia completada sin conexión puede devolver 255. Si vence el tiempo de espera, muestra 000; con un par conectado, muestra el byte recibido. El resultado no siempre es 042.

Descargar código fuente / Estado de la verificación
.\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(); }
}Leer datos de ROM con un argumento de banco gb_bank.c
Objetivo: Muestra los argumentos necesarios para copiar datos de ROM a RAM.
Funcionamiento: far_data_read copia los dos bytes 7 y 42 en copied, con el argumento de banco 0 y longitud 2.
Controles y resultado esperado: El segundo byte de destino se muestra como 042. Este ejemplo lee ROM fija; no demuestra una transferencia entre distintos bancos conmutables.

Descargar código fuente / Estado de la verificación
.\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(); }
}Cargar datos por ID de recurso gb_asset.c
Objetivo: Registra la ubicación y la longitud de un recurso y después recupera sus bytes por ID.
Funcionamiento: El recurso 0 tiene tipo RAW, banco 0, un puntero a {7,42} y longitud 2. asset_load_raw lo copia a un búfer RAM de dos bytes.
Controles y resultado esperado: El segundo byte se muestra como 042. Esto demuestra la búsqueda y copia de recursos, no el renderizado de imágenes.

Descargar código fuente / Estado de la verificación
.\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(); }
}Asignar un proyectil gb_danmaku.c
Objetivo: Inicializa el pool de proyectiles y comprueba su ocupación tras crear uno.
Funcionamiento: Después de danmaku_reset, el programa llama a danmaku_spawn(40,40,16,0) y muestra dm_count.
Controles y resultado esperado: 001 es la cantidad de proyectiles asignados. Aquí no hay un bucle de actualización de proyectiles ni un compositor de fondo, así que no se dibuja ningún proyectil en movimiento.

Descargar código fuente / Estado de la verificación
.\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(); }
}Configurar una paleta de fondo de CGB gb_cgb_palette.c
Objetivo: Elige los colores del fondo y del texto mediante los componentes RGB de Game Boy Color.
Funcionamiento: El programa desactiva el LCD durante VBlank, configura el color 0 de la paleta 0 como blanco (31,31,31) y el color 3 como violeta (12,0,22), y después activa la pantalla.
Controles y resultado esperado: Ejecútelo en modo CGB para ver texto violeta y 042 sobre blanco. La salida monocroma de DMG no permite comprobar estos colores.

Descargar código fuente / Estado de la verificación
.\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(); }
}Desplazar el fondo horizontalmente gb_scroll.c
Objetivo: Mueve el punto de vista sin volver a dibujar el contenido del fondo.
Funcionamiento: Cada fotograma incrementa phase y lo pasa a Scroll_SetBg. La posición de ocho bits vuelve a cero después de 255.
Controles y resultado esperado: La palabra SCROLL se mueve hacia la izquierda. Observe su posición a lo largo del tiempo; una sola captura no puede mostrar el movimiento.

Descargar código fuente / Estado de la verificación
.\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); }
}Encolar una actualización de tile gb_vram_queue.c
Objetivo: Separa una solicitud de dibujo de la transferencia a VRAM que la aplica.
Funcionamiento: vram_queue_bg_tile encola el tile 52 en la coordenada (3,8) y vram_flush lo transfiere. En la fuente incluida, el tile 52 es el dígito 4.
Controles y resultado esperado: Busque un único 4 en la coordenada de tile del fondo (3,8). Es una actualización de tile, no un contador numérico.

Descargar código fuente / Estado de la verificación
.\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(); }
}Mostrar un sprite mediante DMA de OAM gb_sprite.c
Objetivo: Asigna y muestra un sprite de forma independiente del fondo.
Funcionamiento: El pool recién inicializado proporciona la posición 0. El programa selecciona el tile 65, establece la posición (70,80), activa la visualización OBJ y transfiere la OAM espejo en cada fotograma.
Controles y resultado esperado: Aparece una letra A inmóvil en la posición de sprite indicada. En un juego, comprueba si falló la asignación antes de usar el ID devuelto.

Descargar código fuente / Estado de la verificación
.\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(); }
}Generar un valor aleatorio reproducible gb_rng.c
Objetivo: Utiliza una semilla para reproducir una secuencia pseudoaleatoria.
Funcionamiento: rng_seed(1234) inicializa el generador y después se llama una vez a rng_range(10). El límite superior, 10, queda excluido.
Controles y resultado esperado: La pantalla muestra un valor entre 000 y 009. Reiniciar el mismo programa con la misma semilla reproduce ese valor; no cambia en cada fotograma.

Descargar código fuente / Estado de la verificación
.\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(); }
}Guardar una bandera de estado del juego gb_flags.c
Objetivo: Representa un estado booleano, como si un evento ha terminado, mediante un número de bandera.
Funcionamiento: El programa borra la bandera 12, la activa y la vuelve a leer con flag_get.
Controles y resultado esperado: 001 significa que el indicador 12 es verdadero. El ejemplo opera sobre indicadores en RAM y no los conserva al apagar y encender.

Descargar código fuente / Estado de la verificación
.\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(); }
}Avanzar un círculo un paso de física gb_circle.c
Objetivo: Configura un cuerpo circular y actualiza su posición a partir de la velocidad.
Funcionamiento: El círculo activo tiene radio 4, posición (40,40) y velocidad (2,0). La gravedad es cero y linear_damping_q8 vale 255; kq2dc_step se ejecuta una vez.
Controles y resultado esperado: 042 es la coordenada X entera resultante. El ejemplo no dibuja un contorno circular ni una animación de colisión.

Descargar código fuente / Estado de la verificación
.\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(); }
}Procesar un efecto de sonido en cada fotograma gb_sound.c
Objetivo: Inicia un flujo de efectos de sonido y lo hace avanzar mediante actualizaciones regulares por fotograma.
Funcionamiento: Después de Audio_Init, Audio_PlaySFX inicia tone en el canal de ID 1. El bucle principal llama a Audio_Update una vez por fotograma.
Controles y resultado esperado: Suena un efecto de pulso corto mientras se mantiene visible 042. El número es un marcador fijo, no el volumen ni la posición de reproducción. Evalúa el sonido mediante la salida de audio o una grabación.

Descargar código fuente / Estado de la verificación
.\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(); }
}Diccionario de funciones intrínsecas del compilador
149 entradas. Abre una entrada para consultar su sintaxis, argumentos, notas del código fuente y ejemplo de uso. Los fragmentos de llamada requieren inicialización previa; no son ROM independientes. Los ejemplos de paso de argumentos muestran cómo llegar a la API con los valores preparados, pero no demuestran su funcionamiento en el dispositivo.
asset.h — Identificadores de recursos y descriptores de datos
La biblioteca de recursos localiza datos en bancos de ROM mediante un ID de 8 bits. Incluya asset.c y, en FC, también bank.c. Cada AssetDesc empaquetado ocupa 6 bytes: tipo, banco del recurso, dirección de 16 bits y longitud en bytes. La tabla y los datos pueden estar en bancos distintos.
Registre la tabla antes de buscar recursos. El registro es un estado global compartido: no lo cambie durante una búsqueda. La biblioteca comprueba los ID y el puntero de salida de asset_get, pero no valida la tabla, las direcciones de los datos ni el contenido del cartucho.
Busca un recurso RAW o TILES y transfiere sus desc.len bytes mediante __vram_copy. Seleccione antes el banco de ROM de los datos: esta función de GB usa desc.ptr directamente y no selecciona desc.bank para la transferencia.
__farmemcpy
Selecciona temporalmente un banco de ROM, copia len bytes de src a dst en orden de direcciones creciente y restaura el banco guardado por el compilador. En esta plataforma, __farmemcpy y __far_memcpy comparten la implementación.
Sintaxis
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);Parámetros
dstDestino en memoria escribible, con espacio para todos los bytes copiados. Salvo en una transferencia de cero bytes, no se comprueba si el destino es nulo o inválido.
bankSelector de banco de 8 bits para la ROM de origen. Use un banco admitido por el cartucho y la distribución del compilador; se especifica por separado de la dirección.
srcPuntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.
lenCantidad de bytes, de 0 a 65535. Con cero no se copia memoria, pero sí se selecciona y restaura el banco.
Valor devuelto
Ninguno (void).
Condiciones y límites
Mantenga todo el origen en el mapeo de ROM previsto y todo el destino en memoria escribible. La copia no cambia automáticamente de banco al cruzar una ventana, no trata solapamientos como memmove ni sincroniza escrituras en VRAM. Para VRAM, use las API de transferencia gráfica.
Los ejemplos sitúan el código llamante en el banco fijo 0 para que cambiar el banco de origen no retire el código en ejecución. Las transferencias son síncronas. No las interrumpa con otra copia que cambie bancos ni suponga que la función garantiza por sí sola la seguridad de todas las interrupciones.
La rutina de GB restaura el banco registrado por el compilador. Manténgalo actualizado con __bankswitch; escribir directamente en el mapper no lo actualiza. La posición de HRAM que guarda el banco de retorno es compartida, por lo que no admite transferencias anidadas que la sobrescriban.
Ejemplo de uso
__farmemcpy(buffer,payload_bank,payload_pointer,32);
expect(*asset_bank_marker==0xA1,27); // Source bank 2 was selected only for the copy.
for(i=0;i<32;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),28);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Copia dos patrones, 32 bytes, del banco 2 partiendo del banco 1. Compara todos los bytes, comprueba la restauración del banco 1 y verifica que una copia de longitud cero conserva el testigo 0x55.
Imagen esperada: una fila roja empieza en (16,32) y ocupa 72×8 píxeles. Repite tres veces un triángulo de 8×8, un cuadrado de 8×8 con borde de un píxel y un tile con sus cuatro columnas izquierdas rellenas. El triángulo crece desde un píxel arriba a la izquierda hasta ocho en la fila inferior.
En GB, el triángulo y contorno azules de (16,64) muestran __farmemcpy; la pareja verde de (16,96), __far_memcpy. Cada pareja ocupa 16×8 píxeles. CGB usa esos colores; DMG muestra las mismas formas en negro sobre blanco.
El estado esperado es FAILED CHECKS 000 y FIRST FAILURE 00. La verificación ejecuta 300 fotogramas y compara texto, formas y colores con lo esperado. Es una prueba en emulador, no en hardware físico.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/asset.h:17
__vram_copy
__vram_copy es un alias del compilador para __vram_memcpy. Usa el mismo recuento en bytes y la misma transferencia inmediata por CPU, incluidas las reglas de acceso al LCD.
Copia inmediatamente len bytes de src a dst en orden creciente de direcciones. Vuelve al terminar; no encola el trabajo. Ambas direcciones avanzan un byte cada vez, sin saltos de fila del mapa ni conversión del formato gráfico.
Sintaxis
void __vram_copy(u16 dst, const void* src, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.srcPuntero a los bytes de origen, válido y mapeado durante toda la llamada. Los datos se leen inmediatamente, sin conservarlos para un flush posterior. No se cambia de banco automáticamente. Usa rangos sin solapamiento: esta copia hacia delante no ofrece la semántica de memmove.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_copy(0x9862,strip,3); // The copy alias uses the same CPU transfer.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_copy para escribir tres bytes del mapa en la casilla (2,3), píxel (16,24). Deben aparecer [1,2,3] de izquierda a derecha en un área de 24 × 8, con las casillas vecinas vacías. El programa completo sitúa cada copia o relleno probado en su propia fila.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/asset.h:18
bank.h — Acceso a código y datos por bancos
Un banco ROM es un bloque de almacenamiento del cartucho que se asigna al espacio de direcciones de la CPU. Distintos bancos pueden ocupar la misma dirección, por lo que un puntero de 16 bits no basta para identificar los datos. BankPtr es un descriptor de tres bytes sin relleno: un byte de banco y dos de puntero. Se crea con farptr_make y se lee con farptr_read8, farptr_read16 o farptr_read.
El ejemplo GB usa un cartucho MBC5: el código del banco fijo 0 permanece accesible en $0000–$3FFF, mientras que los bancos 1, 2 y 3 se seleccionan en $4000–$7FFF. Coloca bank_switch y el código que continúa después en ROM fija. Inicializa el registro de la biblioteca con bank_switch antes de consultarlo. En GB, far_call acepta un puntero de callback; __farcall exige un nombre de función.
__farcall_ptr
Invoca un callback sin argumentos en una dirección de ejecución del banco indicado. La rutina de ROM fija guarda el banco de cada llamador en la pila de la CPU y lo restaura al volver. La intrínseca no devuelve ningún valor.
Sintaxis
void __farcall_ptr(u8 bank, const void* func);Parámetros
bankNúmero de banco ROM de 8 bits sin signo. En una llamada debe coincidir con la ubicación de la función; en una lectura selecciona el origen. Usa el intervalo válido para el cartucho.
funcDirección ejecutable válida de un callback sin parámetros. Puede darse como nombre de función o puntero de ejecución. No se comprueban en ejecución los punteros nulos ni una combinación incorrecta de banco y dirección.
Valor devuelto
Ninguno (void).
Condiciones y límites
Estas llamadas no pasan argumentos del usuario ni hacen que el callback admita reentrada por interrupciones. Gestiona correctamente sus datos, pila e interrupciones. La restauración de bancos se prueba con llamadas anidadas y repetidas; el ejemplo no cubre hardware físico ni cualquier posible intercalado de interrupciones.
Estas interfaces GB manejan ocho bits de banco y no ajustan el noveno bit de MBC5. Siguen vigentes las reglas del cartucho, incluido el tratamiento del banco cero. El ejemplo usa explícitamente los bancos MBC5 1–3; no mezcles este estado con escrituras al mapper sin registrar.
Ejemplo de uso
requested_bank=3;requested_callback=callback;
__farcall_ptr(requested_bank,requested_callback); // Runtime bank and address.
bank_expect(callback_count==3 && callback_byte==0xD3,17);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
En este punto del programa completo, __farcall_ptr eleva callback_count a 3. El callback lee D3 del banco 3. Una lectura cercana posterior de B2 en home2 verifica el retorno con el banco 2 seleccionado.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/bank.h:15
kitaqgb/kitaqgb/CodeGenerator.cs:8335
if (funcName == "__farcall_ptr")
{
if (args.Length != 2) Error(expr, "__farcall_ptr(bank, func) expects 2 arguments");
CType callbackType = TypeOf(args[1]);
if (callbackType.IsPointer) callbackType = callbackType.Subtype;
if (callbackType != null && callbackType.IsFunction &&
(callbackType.ParamTypes ?? Array.Empty<CType>()).Length != 0)
Error(expr, "__farcall_ptr requires a callback with no parameters");
CompileIntoA(args[0]);
EmitAsm("PUSH_AF");
CompileIntoHL(args[1]);
EmitAsm("POP_AF");
UseFarCallPointerBank0Helper = true;
EmitAsm("CALL", new AsmOperand("__kq_farcall_pointer_bank0", AddressMode.Absolute));
RecordCallEdge(expr, "<indirect-far>", -1, "farcall_pointer", viaThunk: true,
viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
return;
}__farpeek16
Lee un valor de 16 bits little-endian en addr y addr+1 con una selección temporal de banco, y después lo restaura. Es la intrínseca utilizada por far_data_read16.
Sintaxis
u16 __farpeek16(u8 bank, const void* addr);Parámetros
bankNúmero de banco ROM de 8 bits sin signo. En una llamada debe coincidir con la ubicación de la función; en una lectura selecciona el origen. Usa el intervalo válido para el cartucho.
addrPuntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.
Valor devuelto
La palabra little-endian leída, como u16 (0–65535).
Condiciones y límites
El byte o la palabra completos deben ser legibles en el mapa CPU seleccionado. No se validan punteros, no se recortan direcciones ni se cambia automáticamente de banco al cruzar una ventana. Una palabra necesita dos bytes consecutivos válidos.
Las lecturas y copias lejanas son síncronas y usan memoria de trabajo compartida del compilador. No dejes que una interrupción ejecute otra transferencia sobre esa memoria mientras está en uso. En GB, la copia también guarda el banco anterior en una posición HRAM compartida. El ejemplo ejecuta las operaciones desde código fijo, sin un manejador de transferencia que compita.
Estas interfaces GB manejan ocho bits de banco y no ajustan el noveno bit de MBC5. Siguen vigentes las reglas del cartucho, incluido el tratamiento del banco cero. El ejemplo usa explícitamente los bancos MBC5 1–3; no mezcles este estado con escrituras al mapper sin registrar.
Ejemplo de uso
bank_expect(__farpeek16(3,payload+1)==0x5C7A,14);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este fragmento usa __farpeek16 para leer payload+1 del banco 3 y comprueba 5C7A. El programa completo también lee home2 con un puntero cercano para confirmar que se ha restaurado el banco 2.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/bank.h:13
kitaqgb/kitaqgb/CodeGenerator.cs:8295
if (funcName == "__farpeek16")
{
if (args.Length != 2) Error(expr, "__farpeek16(bank, addr) expects 2 arguments");
CompileIntoA(args[0]); EmitPushAAsWord(); // bank
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
EmitAsm("ADD_SP_IMM", new AsmOperand(-2, AddressMode.Relative)); // scratch
// At SP: scratch, saved address, saved bank; each occupies two bytes.
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("PUSH_HL"); // dst = &scratch
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL"); // bank
EmitAsm("POP_HL"); // dst
EmitAsm("LD_BC_IMM", new AsmOperand(2, AddressMode.Immediate16));
EmitCallFarMemcpyHelper();
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
LastCallReturnsHL = true;
return;
}__farpeek8
Selecciona temporalmente el banco indicado, lee un byte en la dirección CPU y restaura el banco anterior. Es la intrínseca utilizada por far_data_read8.
Sintaxis
u8 __farpeek8(u8 bank, const void* addr);Parámetros
bankNúmero de banco ROM de 8 bits sin signo. En una llamada debe coincidir con la ubicación de la función; en una lectura selecciona el origen. Usa el intervalo válido para el cartucho.
addrPuntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.
Valor devuelto
El byte leído, como u8 (0–255).
Condiciones y límites
El byte o la palabra completos deben ser legibles en el mapa CPU seleccionado. No se validan punteros, no se recortan direcciones ni se cambia automáticamente de banco al cruzar una ventana. Una palabra necesita dos bytes consecutivos válidos.
Las lecturas y copias lejanas son síncronas y usan memoria de trabajo compartida del compilador. No dejes que una interrupción ejecute otra transferencia sobre esa memoria mientras está en uso. En GB, la copia también guarda el banco anterior en una posición HRAM compartida. El ejemplo ejecuta las operaciones desde código fijo, sin un manejador de transferencia que compita.
Estas interfaces GB manejan ocho bits de banco y no ajustan el noveno bit de MBC5. Siguen vigentes las reglas del cartucho, incluido el tratamiento del banco cero. El ejemplo usa explícitamente los bancos MBC5 1–3; no mezcles este estado con escrituras al mapper sin registrar.
Ejemplo de uso
bank_expect(__farpeek8(3,payload)==0xD3,13);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este fragmento usa __farpeek8 para leer payload del banco 3 y comprueba D3. El programa completo también lee home2 con un puntero cercano para confirmar que se ha restaurado el banco 2.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/bank.h:12
kitaqgb/kitaqgb/CodeGenerator.cs:8262
if (funcName == "__farpeek8")
{
if (args.Length != 2) Error(expr, "__farpeek8(bank, addr) expects 2 arguments");
CompileIntoA(args[0]); EmitPushAAsWord(); // bank
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
EmitAsm("ADD_SP_IMM", new AsmOperand(-2, AddressMode.Relative)); // scratch
// At SP: scratch, saved address, saved bank; each occupies two bytes.
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("PUSH_HL"); // dst = &scratch
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL"); // bank
EmitAsm("POP_HL"); // dst
EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
EmitCallFarMemcpyHelper();
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
return;
}cgb_palette.h — Paletas de fondo y OBJ de CGB
cgb_palette.h y cgb_palette.c ofrecen empaquetado RGB15 y actualización inmediata de paletas CGB. Usa BG para fondo/ventana y OBJ para sprites. Las funciones de color único o componentes cambian un color; las de rango escriben palabras consecutivas; las de paleta completa escriben cuatro. Los rangos raw usan posiciones absolutas de color, y los intrínsecos de registro, posiciones de byte. Asigna la paleta mediante atributos del mapa u OAM. Incluye la implementación una sola vez, como en el ejemplo completo.
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Las paletas BG/ventana y OBJ son almacenes separados. Las escrituras de la biblioteca activan el incremento y dejan modificado el registro de índice. El color OBJ cero es transparente al dibujar sprites, aunque sus bytes se puedan guardar y leer.
__cgb_is_cgb
Devuelve el booleano de modo CGB del compilador. --cgb=dmg explícito se reduce a 0 y --cgb=cgb_only a 1; el destino dual --cgb=cgb utiliza detección en ejecución. Indica el modo de ejecución elegido, no todas las prestaciones del modelo físico.
Sintaxis
u8 __cgb_is_cgb();Valor devuelto
u8: 0 para modo DMG, 1 para modo CGB.
Ejemplo de uso
color_mode=__cgb_is_cgb(); // 0 in DMG mode, 1 in CGB mode for this dual-mode ROM.
if (color_mode>1) failures++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_palette.h:8
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).
__cgb_safe_set_bcpd
Escribe un byte en el registro de datos BCPD/BGPD ($FF69) de las paletas de fondo/ventana en CGB. __cgb_safe_set_bgpd es otro nombre del mismo registro. El registro de índice selecciona la posición; un color RGB15 completo necesita un byte bajo y otro alto.
Sintaxis
void __cgb_safe_set_bcpd(u8 value);Parámetros
valueUn byte, 0–255. Escribe primero el bajo y después el alto de RGB15; activa antes el incremento automático o selecciona cada posición. Escribir datos no activa el incremento por sí solo.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_bcpd(0xE0); __cgb_safe_set_bcpd(0x03); // Green word 0x03E0, low then high.
expect_color(0,27,992);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_palette.h:10
__cgb_safe_set_bcps
Escribe en CGB el registro de índice de byte BCPS/BGPI ($FF68) de las paletas de fondo/ventana. __cgb_safe_set_bgpi accede al mismo registro. Selecciona la posición del siguiente acceso al registro de datos; no escribe un color por sí solo.
Sintaxis
void __cgb_safe_set_bcps(u8 value);Parámetros
valueÍndice de byte en bits 0–5 (0–63); el bit 7 activa el incremento tras escribir datos. El bit 6 no se usa. La posición de color n empieza en el byte 2n. Tras 63 vuelve a cero: controla el rango al escribir registros directamente.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_bcps(0xB6); // BG byte 54, with auto-increment enabled (slot 27 low byte).Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_palette.h:9
__cgb_safe_set_ocpd
Escribe un byte en el registro de datos OCPD/OBPD ($FF6B) de las paletas de sprites (OBJ) en CGB. __cgb_safe_set_obpd es otro nombre del mismo registro. El registro de índice selecciona la posición; un color RGB15 completo necesita un byte bajo y otro alto.
Sintaxis
void __cgb_safe_set_ocpd(u8 value);Parámetros
valueUn byte, 0–255. Escribe primero el bajo y después el alto de RGB15; activa antes el incremento automático o selecciona cada posición. Escribir datos no activa el incremento por sí solo.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_ocpd(31); __cgb_safe_set_ocpd(0); // Red sprite color.
expect_color(1,27,31);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_palette.h:12
__cgb_safe_set_ocps
Escribe en CGB el registro de índice de byte OCPS/OBPI ($FF6A) de las paletas de sprites (OBJ). __cgb_safe_set_obpi accede al mismo registro. Selecciona la posición del siguiente acceso al registro de datos; no escribe un color por sí solo.
Sintaxis
void __cgb_safe_set_ocps(u8 value);Parámetros
valueÍndice de byte en bits 0–5 (0–63); el bit 7 activa el incremento tras escribir datos. El bit 6 no se usa. La posición de color n empieza en el byte 2n. Tras 63 vuelve a cero: controla el rango al escribir registros directamente.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_ocps(0xB6); // OBJ byte 54; this does not select a BG color.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_palette.h:11
cgb_tile.h — Números y atributos de tiles de CGB
cgb_tile.h agrupa las declaraciones de funciones intrínsecas de mapas de tiles y las macros para construir atributos CGB. El número de tile selecciona un patrón; su atributo selecciona paleta, banco VRAM del patrón, volteos y prioridad del fondo. Cambiar atributos no altera los píxeles del patrón.
La familia __settile usa el mapa 0x9800; __settileat usa una base explícita; __settilebg, el mapa de fondo elegido por LCDC; y __settilewin, su mapa de ventana. Las formas attr solo escriben atributos; las cgb escriben números de tile y atributos. Las formas bulk transfieren bytes consecutivos en la dirección indicada. Elige la función según el destino y los datos que escribe.
Usa macros como KQ_CGB_ATTR_PAL para bits individuales o KQ_CGB_ATTR para combinar campos. El campo bank selecciona el banco VRAM del patrón, no un banco ROM. Las escrituras normales de números de tile usan el banco VRAM actual; selecciona el banco cero antes de operaciones combinadas de tile/atributo. En DMG, las operaciones solo de atributos no hacen nada; las combinadas siguen escribiendo los números.
El compilador genera la implementación de las funciones intrínsecas __; no requieren enlazar cgb_tile.c. Las rutas normales de escritura esperan acceso a VRAM, pero algunas también cambian la habilitación de interrupciones. Las formas _unsafe y _fast omiten las esperas: úsalas solo si el código que llama garantiza VRAM escribible, por ejemplo con el LCD apagado. Comprueba las condiciones de interrupciones, banco y longitud de cada entrada.
__cgb_safe_set_vbk
Selecciona el banco VRAM de la CPU mediante VBK ($FF4F) en CGB. Solo escribe value & 1: los pares eligen el banco 0 y los impares el 1. Afecta a los accesos de CPU en $8000–$9FFF, no al banco de patrones indicado por un atributo del mapa. No restaura el banco anterior.
Sintaxis
void __cgb_safe_set_vbk(u8 value);Parámetros
valueSelecciona el banco VRAM de la CPU mediante VBK (
$FF4F) en CGB. Solo escribevalue & 1: los pares eligen el banco 0 y los impares el 1. Afecta a los accesos de CPU en$8000–$9FFF, no al banco de patrones indicado por un atributo del mapa. No restaura el banco anterior.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
if (color_mode) {
__cgb_safe_set_vbk(3); // Only bit zero is kept: CPU accesses now use bank 1.
if ((P_VBK&1)!=1) failures++;
__vram_copy(0x8010,shape1,16);
__cgb_safe_set_vbk(2); // Bit zero is clear, so return to bank 0.
if ((P_VBK&1)!=0) failures++;
}Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:28
Write the VRAM bank selection only on CGB. Safe here means hardware-mode guarded, not PPU-timed.
__settile
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile(2, 3, 'A');
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:31
Write a tile number to map 9800, waiting for VRAM access when necessary.
__settile_bulk
Copia count bytes consecutivos de números de tile de src a la dirección VRAM dest. Cuenta bytes, no imágenes de tile, y no ajusta el salto entre filas.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile_bulk(u16 dest, const u8* src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_bulk
// Copy `count` consecutive tile-number bytes from `src` to the VRAM byte address `dest`.
// The count is in bytes, not tile-image units; no row-stride adjustment is made. Waits
// for writable VRAM when necessary. The LCD-on path uses interrupt masking and can
// enable interrupts afterward; it is not a promise to preserve the caller's interrupt
// state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_bulk(0x9862, example_tiles, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:48
Copy count sequential bytes to dest using the current VRAM bank and access-checked transfer.
__settile_bulk_fast
Copia count bytes consecutivos de números de tile de src a la dirección VRAM dest. Cuenta bytes, no imágenes de tile, y no ajusta el salto entre filas.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_bulk_fast
// Copy `count` consecutive tile-number bytes from `src` to the VRAM byte address `dest`.
// The count is in bytes, not tile-image units; no row-stride adjustment is made. Does
// not wait for VRAM access. Call only while the LCD is off or within a caller-managed
// writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_bulk_fast(0x9862, example_tiles, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:50
Copy count sequential bytes to dest without VRAM access waits; retain source-bank visibility.
__settile_unsafe
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settile_unsafe(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_unsafe(2, 3, 'A');
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:33
Write a tile number to map 9800 without waiting for writable VRAM.
__settileat
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settileat(u16 base, u8 x, u8 y, u8 tile);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileat
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. `base` explicitly selects the map (`0x9800` or `0x9C00`); the byte
// address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settileat(0x9C00, 2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileat.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileat.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:35
Write at base+32*y+x with VRAM access checks; supply a valid tile-map base.
__settileat_unsafe
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileat_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. `base` explicitly selects the map (`0x9800` or `0x9C00`); the byte
// address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settileat_unsafe(0x9C00, 2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileat_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileat_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:37
Use an explicit map base without waiting for VRAM access.
__settileatattr
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileatattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. `base` explicitly selects the map
// (`0x9800` or `0x9C00`); the byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settileatattr(0x9C00, 2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:62
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.
__settileatattr_unsafe
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileatattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. `base` explicitly selects the map
// (`0x9800` or `0x9C00`); the byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settileatattr_unsafe(0x9C00, 2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:64
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.
__settileatcgb
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileatcgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. `base` explicitly selects the map (`0x9800` or `0x9C00`); the
// byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileatcgb(0x9C00, 2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatcgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatcgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:66
Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. Use the access-checked write path.
__settileatcgb_unsafe
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileatcgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. `base` explicitly selects the map (`0x9800` or `0x9C00`); the
// byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileatcgb_unsafe(0x9C00, 2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatcgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatcgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:68
Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. No VRAM access waits are inserted.
__settileattr
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settileattr(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. The destination is always `0x9800
// + 32*y + x`, regardless of which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9800, 2, 3, 'A');
__settileattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:53
Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
__settileattr_bulk
Copia count atributos CGB consecutivos de src a dest en el banco VRAM 1. No cambia números de tile; dest es una dirección, no un par (x,y).
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settileattr_bulk(u16 dest, const u8* src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileattr_bulk
// Copy `count` consecutive CGB attribute bytes from `src` to `dest` in VRAM bank 1. Tile
// numbers are untouched; the destination is a byte address, not an `(x,y)` pair. Waits
// for writable VRAM when necessary. The LCD-on path uses interrupt masking and can
// enable interrupts afterward; it is not a promise to preserve the caller's interrupt
// state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settile_bulk(0x9862, example_tiles, 3);
__settileattr_bulk(0x9862, example_attrs, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, A, B y C son roja, azul y verde: los atributos 1,2,3 seleccionan tres paletas distintas. En DMG, las tres letras son negras.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:89
Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. Transfers use access checks.
__settileattr_bulk_fast
Copia count atributos CGB consecutivos de src a dest en el banco VRAM 1. No cambia números de tile; dest es una dirección, no un par (x,y).
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileattr_bulk_fast
// Copy `count` consecutive CGB attribute bytes from `src` to `dest` in VRAM bank 1. Tile
// numbers are untouched; the destination is a byte address, not an `(x,y)` pair. Does
// not wait for VRAM access. Call only while the LCD is off or within a caller-managed
// writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settile_bulk(0x9862, example_tiles, 3);
__settileattr_bulk_fast(0x9862, example_attrs, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, A, B y C son roja, azul y verde: los atributos 1,2,3 seleccionan tres paletas distintas. En DMG, las tres letras son negras.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:91
Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. The caller supplies writable VRAM timing.
__settileattr_unsafe
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settileattr_unsafe(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settileattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. The destination is always `0x9800
// + 32*y + x`, regardless of which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9800, 2, 3, 'A');
__settileattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:55
Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
__settilebg
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilebg(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settilebg(2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:43
Select the background map from LCDC bit 3, then write with access checks.
__settilebg_unsafe
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilebg_unsafe(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settilebg_unsafe(2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:45
Select the LCDC background map and write without access waits.
__settilebgattr
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilebgattr(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebgattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 3 selects the background
// map: zero uses `0x9800`, one uses `0x9C00`. The destination is that base plus `32*y +
// x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settilebgattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:80
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.
__settilebgattr_unsafe
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebgattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 3 selects the background
// map: zero uses `0x9800`, one uses `0x9C00`. The destination is that base plus `32*y +
// x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settilebgattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:82
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.
__settilebgcgb
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebgcgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 3 selects the background map: zero uses `0x9800`, one
// uses `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilebgcgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgcgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgcgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:84
Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. Use the access-checked write path.
__settilebgcgb_unsafe
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebgcgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 3 selects the background map: zero uses `0x9800`, one
// uses `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilebgcgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgcgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgcgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:86
Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. No VRAM access waits are inserted.
__settilecgb
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilecgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. The destination is always `0x9800 + 32*y + x`, regardless of
// which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilecgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:57
Write tile and CGB attributes at map 9800; DMG receives only the tile write. Use the access-checked write path.
__settilecgb_bulk
Copia count números desde tile_src y después count atributos desde attr_src al mismo intervalo del mapa desde dest. Son dos arrays separados, no pares intercalados.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.tile_src, attr_srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilecgb_bulk
// Copy `count` tile numbers from `tile_src`, then `count` attributes from `attr_src`, to
// the same map addresses starting at `dest`. These are separate byte arrays, not
// interleaved pairs. Waits for writable VRAM when necessary. The LCD-on path uses
// interrupt masking and can enable interrupts afterward; it is not a promise to preserve
// the caller's interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilecgb_bulk(0x9862, example_tiles, example_attrs, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, A, B y C son roja, azul y verde: los atributos 1,2,3 seleccionan tres paletas distintas. En DMG, las tres letras son negras.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:93
Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. Transfers use access checks.
__settilecgb_bulk_fast
Copia count números desde tile_src y después count atributos desde attr_src al mismo intervalo del mapa desde dest. Son dos arrays separados, no pares intercalados.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Parámetros
destDirección del primer byte en VRAM. Para un mapa, usa
0x9800–0x9FFFy mantén toda la transferencia dentro del mapa elegido.tile_src, attr_srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
countCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
Mantén legibles los búferes de origen y visible su banco ROM durante toda la copia. No devuelve valor.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilecgb_bulk_fast
// Copy `count` tile numbers from `tile_src`, then `count` attributes from `attr_src`, to
// the same map addresses starting at `dest`. These are separate byte arrays, not
// interleaved pairs. Does not wait for VRAM access. Call only while the LCD is off or
// within a caller-managed writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilecgb_bulk_fast(0x9862, example_tiles, example_attrs, 3);
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9863, 0) != 'B') { failures++; }
if (tile_example_read(0x9864, 0) != 'C') { failures++; }
if (tile_example_read(0x9865, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, A, B y C son roja, azul y verde: los atributos 1,2,3 seleccionan tres paletas distintas. En DMG, las tres letras son negras.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:95
Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. The caller supplies writable VRAM timing.
__settilecgb_unsafe
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilecgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. The destination is always `0x9800 + 32*y + x`, regardless of
// which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilecgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:59
Write tile and CGB attributes at map 9800; DMG receives only the tile write. No VRAM access waits are inserted.
__settilewin
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilewin(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewin
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settilewin(2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:39
Select the window map from LCDC bit 6, then write a tile with access checks.
__settilewin_unsafe
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilewin_unsafe(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewin_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settilewin_unsafe(2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:41
Select the LCDC window map and write without access waits.
__settilewinattr
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilewinattr(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewinattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settilewinattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:71
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.
__settilewinattr_unsafe
Modifica solo el byte de atributos CGB, sin cambiar el número de tile. Sus bits eligen paleta, banco de imagen, volteos y prioridad.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
En CGB escribe en el banco VRAM 1 y termina con VBK=0. En DMG no escribe atributos.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewinattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settileat(0x9C00, 2, 3, 'A');
__settilewinattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:73
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.
__settilewincgb
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewincgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilewincgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:75
Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. Use the access-checked write path.
__settilewincgb_unsafe
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
El bit 6 de LCDC elige el mapa de ventana: cero usa 0x9800 y uno 0x9C00. Son coordenadas del mapa, no la posición de la ventana en pantalla.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilewincgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__settilewincgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, la A es azul: el atributo 2 selecciona la paleta azul preparada por el ejemplo. En DMG, el carácter es negro porque no se aplican los atributos CGB.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/cgb_tile.h:77
Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. No VRAM access waits are inserted.
input.h — Estado, pulsación, liberación y repetición de botones
Usa input_down para movimiento continuo, input_pressed para acciones únicas, input_released para actuar al soltar y input_repeat para repetir opciones de menú. Inicializa una vez, actualiza una vez por ciclo de entrada y consulta cuando lo necesites. btn_down, btn_pressed, btn_released y btn_repeat son alias de esas cuatro consultas.
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Los intervalos cuentan actualizaciones, no milisegundos. Una pulsación nueva genera un pulso inmediato. Con INPUT_REPEAT_DELAY=18 e INPUT_REPEAT_RATE=5, el siguiente llega 19 actualizaciones después y los demás cada 6. Cada botón tiene su contador. Aplica los mismos valores de las macros al compilar input.c; cambiar solo el encabezado del código llamante no reconfigura la biblioteca.
__readpadex
Lee todos los botones y los compara con la máscara anterior suministrada. Empaqueta el estado actual en el byte bajo y las nuevas pulsaciones, current & ~previous, en el alto. No guarda un estado anterior interno ni devuelve liberaciones o repeticiones.
Sintaxis
u16 __readpadex(u8 prev_keys);Parámetros
prev_keysLa máscara completa del
__readpad()anterior o el byte bajo del último__readpadex(). Pasa 0 en la primera lectura y guarda el nuevo byte bajo para la siguiente llamada.
Valor devuelto
Un u16: (u8)result es la máscara actual y (u8)(result >> 8) la de nuevas pulsaciones. Guárdalo en una variable de 16 bits para no perder los bits de pulsación.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Ejemplo de uso
packed_first=__readpadex(0); // First sample: current 0x11, new presses 0x11.
current_keys=(u8)packed_first;
press_keys=(u8)(packed_first>>8);
packed_held=__readpadex(current_keys); // Held without a change: new presses are 0.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa compara los tres formatos de máscara y el resultado que combina el estado actual con las nuevas pulsaciones. Mantén A y derecha a la vez. ALL KEYS vale 17; DIRECTIONS y BUTTONS LOW valen 1 cada uno. Con una máscara anterior de 0, EX CURRENT y EX NEW PRESS valen 17. Al pasar la máscara actual a la siguiente lectura, HELD PRESS vale 0. FAILED CHECKS debe ser 0. Los valores quedan en pantalla aunque sueltes los botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cacheImplementación y código fuente
kitaqgb/lib/input.h:28
intrinsics — Funciones intrínsecas del compilador
Esta sección agrupa las funciones intrínsecas que maneja directamente el generador de código KITAQGB. intrinsics es una categoría de documentación, no una instrucción para incluir una cabecera GB compartida llamada intrinsics.h. Usa las cabeceras e inicialización de cada ejemplo completo. No necesitas definir la implementación de la función intrínseca en tu juego.
Algunas funciones intrínsecas usan información de compilación; otras acceden al hardware o a la memoria durante el juego. Por ejemplo, __bankof(symbol) resuelve el banco asignado a un objeto o función con nombre, mientras que __bankswitch(bank) cambia el mapeo ROM visible en tiempo de ejecución. Distingue números de banco, direcciones de CPU y punteros al proporcionar argumentos.
Que un nombre empiece por __ no garantiza comprobación de límites, esperas de acceso a VRAM ni uso seguro desde una interrupción. Por ejemplo, quien llama a __vram_memcpy_unsafe debe garantizar un periodo en que la VRAM sea escribible. Comprueba las unidades, rangos válidos, mapeo de bancos y requisitos de interrupción de cada entrada antes de elegir una operación.
__bankof
Obtiene el número de banco ROM lógico asignado a un objeto de datos o función con nombre. Resuelve información de ubicación; no consulta el banco activo ni desreferencia un puntero.
Sintaxis
u8 bank = __bankof(symbol);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
symbolNombre de un objeto ROM o función cuya ubicación conoce el compilador o enlazador. Una variable puntero no representa aquí al objeto apuntado; no se admiten expresiones de ejecución como nombres de símbolo.
Valor devuelto
Número de banco u8. Cero puede identificar ROM común, pero no es un selector de banco válido en todos los destinos.
Ejemplo de uso
bank_expect(__bankof(payload)==3,3);
bank_expect(__bankof(callback)==3,4);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo comprueba que payload y callback están en el banco 3. Esto verifica metadatos de ubicación; las lecturas y comprobaciones del callback verifican aparte el comportamiento durante la ejecución.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:432
kitaqgb/kitaqgb/CodeGenerator.cs:7360
if (funcName == "__bankof")
{
if (args.Length != 1) Program.Error("__bankof(symbol) expects 1 argument");
if (!args[0].Match(Tag.Name, out string symName)) Program.Error("__bankof(symbol) requires a symbol name argument");
if (TryGetKnownRomBankForSymbol(symName, out int knownBank))
{
EmitAsm("LD_A_IMM", new AsmOperand(knownBank & 0xFF, AddressMode.Immediate));
return;
}
EmitAsm("LD_A_IMM", new AsmOperand(symName, ImmediateModifier.Bank));
return;
}__bankswitch
Escribe el byte bajo del banco en la dirección $2000 del cartucho y actualiza el estado del compilador. La selección se mantiene tras la llamada; no actualiza el registro usado por bank_get_current.
Sintaxis
void __bankswitch(u8 bank);Parámetros
bankNúmero de banco ROM de 8 bits sin signo. En una llamada debe coincidir con la ubicación de la función; en una lectura selecciona el origen. Usa el intervalo válido para el cartucho.
Valor devuelto
Ninguno (void).
Condiciones y límites
Un cambio de banco persistente no debe retirar el código en ejecución ni su dirección de retorno. Coloca esta llamada, la propia bank_switch cuando se use y el código posterior en el banco común o fijo 0, como hace el ejemplo.
El registro de la biblioteca empieza en cero y no detecta el mapa inicial del cartucho. Inicialízalo con bank_switch. Las intrínsecas y las escrituras directas al mapper no actualizan este registro separado; bank_get_current no permite verificar el mapa real del hardware.
Estas interfaces GB manejan ocho bits de banco y no ajustan el noveno bit de MBC5. Siguen vigentes las reglas del cartucho, incluido el tratamiento del banco cero. El ejemplo usa explícitamente los bancos MBC5 1–3; no mezcles este estado con escrituras al mapper sin registrar.
Ejemplo de uso
__bankswitch(1);
bank_expect(home1[0]==0xA1,19);
bank_expect(bank_get_current()==2,20); // Intrinsics bypass the library record.
__bankswitch(2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
bank_switch(2) registra y selecciona inicialmente el banco 2. Después, el cambio directo selecciona el banco 1 y lee A1 de home1 mientras bank_get_current sigue indicando 2; al volver, home2 devuelve B2 otra vez.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/bank.h:11
kitaqgb/kitaqgb/CodeGenerator.cs:7268
if (funcName == "__bankswitch")
{
if (args.Length != 1) Program.Error("__bankswitch(bank) expects 1 argument");
CompileIntoA(args[0]);
if (CurrentFunctionBank == 0)
{
EmitAsm("LD_MEM_A", new AsmOperand(0x2000, AddressMode.Absolute));
EmitStoreA(RomBankVar);
}
else
{
UseBankSwitchBank0Helper = true;
EmitAsm("CALL", new AsmOperand("__kq_bankswitch_bank0", AddressMode.Absolute));
}
return;
}__cgb_safe_set_bgpd
Escribe un byte en el registro de datos BCPD/BGPD ($FF69) de las paletas de fondo/ventana en CGB. __cgb_safe_set_bcpd es otro nombre del mismo registro. El registro de índice selecciona la posición; un color RGB15 completo necesita un byte bajo y otro alto.
Sintaxis
__cgb_safe_set_bgpd(/* 書式は実装と呼び出し例を参照 */);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueUn byte, 0–255. Escribe primero el bajo y después el alto de RGB15; activa antes el incremento automático o selecciona cada posición. Escribir datos no activa el incremento por sí solo.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_bgpd(31); __cgb_safe_set_bgpd(0); // Alias of BCPD: red at slot 31.
expect_color(0,31,31);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1865
__cgb_safe_set_bgpi
Escribe en CGB el registro de índice de byte BCPS/BGPI ($FF68) de las paletas de fondo/ventana. __cgb_safe_set_bcps accede al mismo registro. Selecciona la posición del siguiente acceso al registro de datos; no escribe un color por sí solo.
Sintaxis
__cgb_safe_set_bgpi(/* 書式は実装と呼び出し例を参照 */);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueÍndice de byte en bits 0–5 (0–63); el bit 7 activa el incremento tras escribir datos. El bit 6 no se usa. La posición de color n empieza en el byte 2n. Tras 63 vuelve a cero: controla el rango al escribir registros directamente.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_bgpi(0xBE); // Alias of BCPS: BG byte 62, auto-increment enabled.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1863
__cgb_safe_set_hdma1
Escribe HDMA1 ($FF51), byte alto de la dirección de origen DMA de CGB. Solo configura el origen; no copia ni inicia la transferencia. HDMA2 establece el byte bajo.
Sintaxis
void __cgb_safe_set_hdma1(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Un bloque son 16 bytes. Un nuevo traslado admite 1–128 bloques (16–2048 bytes): el valor 0 significa 16 bytes, no un traslado vacío. GDMA pausa la CPU hasta terminar. HBlank DMA copia un bloque por HBlank visible y puede abarcar varios fotogramas; comprueba su finalización antes de reutilizar los datos.
Usa GDMA con LCD apagado o en un intervalo escribible suficientemente largo. Inicia HBlank DMA con LCD activo y fuera del modo STAT 0; el ejemplo lo inicia en VBlank. No cambies bancos de origen, VBK de destino ni datos durante el traslado. HALT también pausa HBlank DMA. Estas funciones no añaden esperas, protección de interrupciones ni comprobación de rangos.
Lee $FF55 por separado: bit 7 a cero indica actividad; $FF, finalización. Tras cancelar, bit 7 vale uno y bits 0–6 conservan bloques pendientes menos uno. No se deshacen bytes copiados. Cancela solo un traslado activo; de lo contrario, bit 7 a cero inicia GDMA. Mantén todo el traslado dentro de memoria válida y del banco VRAM de destino, sin depender del desbordamiento de dirección.
Estos setters HDMA controlan DMA por hardware CGB. __vram_copy_dma es otro alias del compilador para un bucle de copia por CPU; su nombre no implica usar este controlador DMA.
Ejemplo de uso
__cgb_safe_set_hdma1(0xC6); // Source high byte: fixed-WRAM buffer at $C600.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa compara tres rutas con un búfer alineado a 16 bytes en $C600. GDMA copia 32 bytes a $8010: en pantalla (16,24) aparece un triángulo rojo de 8 × 8 que crece desde un píxel izquierdo hasta una fila inferior completa; en (24,24), un contorno cuadrado rojo de un píxel. HBlank DMA copia 32 bytes a $8030: en (16,48), media casilla azul izquierda de 4 × 8; en (24,48), un cuadrado azul de 8 × 8. Son patrones realmente transferidos, no indicadores numéricos.
CANCELED muestra media casilla verde de 4 × 8 en (16,72) y un cuadrado verde de 8 × 8 en (24,72), escritos por CPU. Se prepara y cancela un HBlank DMA de dos bloques en el mismo VBlank, antes del primer bloque. $FF55 debe valer $81; ambos patrones deben seguir intactos tras un fotograma. En DMG, las dos primeras filas quedan vacías y solo estos testigos aparecen negros. Texto negro sobre blanco. FAILED CHECKS 000 comprueba también bytes y estado final. Se prueba en KOKURA ROM dual DMG/CGB y solo CGB; no cancelación parcial, doble velocidad ni hardware real.



Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1871
__cgb_safe_set_hdma2
Escribe HDMA2 ($FF52), byte bajo del origen DMA. El hardware ignora bits 0–3 y alinea hacia abajo a 16 bytes. HDMA1=$C6, HDMA2=$0F selecciona $C600. No inicia DMA.
Sintaxis
void __cgb_safe_set_hdma2(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Un bloque son 16 bytes. Un nuevo traslado admite 1–128 bloques (16–2048 bytes): el valor 0 significa 16 bytes, no un traslado vacío. GDMA pausa la CPU hasta terminar. HBlank DMA copia un bloque por HBlank visible y puede abarcar varios fotogramas; comprueba su finalización antes de reutilizar los datos.
Usa GDMA con LCD apagado o en un intervalo escribible suficientemente largo. Inicia HBlank DMA con LCD activo y fuera del modo STAT 0; el ejemplo lo inicia en VBlank. No cambies bancos de origen, VBK de destino ni datos durante el traslado. HALT también pausa HBlank DMA. Estas funciones no añaden esperas, protección de interrupciones ni comprobación de rangos.
Lee $FF55 por separado: bit 7 a cero indica actividad; $FF, finalización. Tras cancelar, bit 7 vale uno y bits 0–6 conservan bloques pendientes menos uno. No se deshacen bytes copiados. Cancela solo un traslado activo; de lo contrario, bit 7 a cero inicia GDMA. Mantén todo el traslado dentro de memoria válida y del banco VRAM de destino, sin depender del desbordamiento de dirección.
Estos setters HDMA controlan DMA por hardware CGB. __vram_copy_dma es otro alias del compilador para un bucle de copia por CPU; su nombre no implica usar este controlador DMA.
Ejemplo de uso
__cgb_safe_set_hdma2(0x0F); // Low four bits are ignored by hardware: source remains $C600.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa compara tres rutas con un búfer alineado a 16 bytes en $C600. GDMA copia 32 bytes a $8010: en pantalla (16,24) aparece un triángulo rojo de 8 × 8 que crece desde un píxel izquierdo hasta una fila inferior completa; en (24,24), un contorno cuadrado rojo de un píxel. HBlank DMA copia 32 bytes a $8030: en (16,48), media casilla azul izquierda de 4 × 8; en (24,48), un cuadrado azul de 8 × 8. Son patrones realmente transferidos, no indicadores numéricos.
CANCELED muestra media casilla verde de 4 × 8 en (16,72) y un cuadrado verde de 8 × 8 en (24,72), escritos por CPU. Se prepara y cancela un HBlank DMA de dos bloques en el mismo VBlank, antes del primer bloque. $FF55 debe valer $81; ambos patrones deben seguir intactos tras un fotograma. En DMG, las dos primeras filas quedan vacías y solo estos testigos aparecen negros. Texto negro sobre blanco. FAILED CHECKS 000 comprueba también bytes y estado final. Se prueba en KOKURA ROM dual DMG/CGB y solo CGB; no cancelación parcial, doble velocidad ni hardware real.



Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1872
__cgb_safe_set_hdma3
Escribe HDMA3 ($FF53), parte alta del destino DMA en VRAM. Solo usa bits 0–4; el destino siempre está en $8000–$9FFF. Se combina con HDMA4. No selecciona el banco VRAM ni inicia DMA.
Sintaxis
void __cgb_safe_set_hdma3(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Un bloque son 16 bytes. Un nuevo traslado admite 1–128 bloques (16–2048 bytes): el valor 0 significa 16 bytes, no un traslado vacío. GDMA pausa la CPU hasta terminar. HBlank DMA copia un bloque por HBlank visible y puede abarcar varios fotogramas; comprueba su finalización antes de reutilizar los datos.
Usa GDMA con LCD apagado o en un intervalo escribible suficientemente largo. Inicia HBlank DMA con LCD activo y fuera del modo STAT 0; el ejemplo lo inicia en VBlank. No cambies bancos de origen, VBK de destino ni datos durante el traslado. HALT también pausa HBlank DMA. Estas funciones no añaden esperas, protección de interrupciones ni comprobación de rangos.
Lee $FF55 por separado: bit 7 a cero indica actividad; $FF, finalización. Tras cancelar, bit 7 vale uno y bits 0–6 conservan bloques pendientes menos uno. No se deshacen bytes copiados. Cancela solo un traslado activo; de lo contrario, bit 7 a cero inicia GDMA. Mantén todo el traslado dentro de memoria válida y del banco VRAM de destino, sin depender del desbordamiento de dirección.
Estos setters HDMA controlan DMA por hardware CGB. __vram_copy_dma es otro alias del compilador para un bucle de copia por CPU; su nombre no implica usar este controlador DMA.
Ejemplo de uso
__cgb_safe_set_hdma3(0xE0); // Only low five bits select the high VRAM offset.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa compara tres rutas con un búfer alineado a 16 bytes en $C600. GDMA copia 32 bytes a $8010: en pantalla (16,24) aparece un triángulo rojo de 8 × 8 que crece desde un píxel izquierdo hasta una fila inferior completa; en (24,24), un contorno cuadrado rojo de un píxel. HBlank DMA copia 32 bytes a $8030: en (16,48), media casilla azul izquierda de 4 × 8; en (24,48), un cuadrado azul de 8 × 8. Son patrones realmente transferidos, no indicadores numéricos.
CANCELED muestra media casilla verde de 4 × 8 en (16,72) y un cuadrado verde de 8 × 8 en (24,72), escritos por CPU. Se prepara y cancela un HBlank DMA de dos bloques en el mismo VBlank, antes del primer bloque. $FF55 debe valer $81; ambos patrones deben seguir intactos tras un fotograma. En DMG, las dos primeras filas quedan vacías y solo estos testigos aparecen negros. Texto negro sobre blanco. FAILED CHECKS 000 comprueba también bytes y estado final. Se prueba en KOKURA ROM dual DMG/CGB y solo CGB; no cancelación parcial, doble velocidad ni hardware real.



Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1873
__cgb_safe_set_hdma4
Escribe HDMA4 ($FF54), byte bajo del destino VRAM. Se ignoran bits 0–3, alineando hacia abajo a 16 bytes. HDMA3=$E0, HDMA4=$1F selecciona $8010. Configura una dirección, no la longitud.
Sintaxis
void __cgb_safe_set_hdma4(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Un bloque son 16 bytes. Un nuevo traslado admite 1–128 bloques (16–2048 bytes): el valor 0 significa 16 bytes, no un traslado vacío. GDMA pausa la CPU hasta terminar. HBlank DMA copia un bloque por HBlank visible y puede abarcar varios fotogramas; comprueba su finalización antes de reutilizar los datos.
Usa GDMA con LCD apagado o en un intervalo escribible suficientemente largo. Inicia HBlank DMA con LCD activo y fuera del modo STAT 0; el ejemplo lo inicia en VBlank. No cambies bancos de origen, VBK de destino ni datos durante el traslado. HALT también pausa HBlank DMA. Estas funciones no añaden esperas, protección de interrupciones ni comprobación de rangos.
Lee $FF55 por separado: bit 7 a cero indica actividad; $FF, finalización. Tras cancelar, bit 7 vale uno y bits 0–6 conservan bloques pendientes menos uno. No se deshacen bytes copiados. Cancela solo un traslado activo; de lo contrario, bit 7 a cero inicia GDMA. Mantén todo el traslado dentro de memoria válida y del banco VRAM de destino, sin depender del desbordamiento de dirección.
Estos setters HDMA controlan DMA por hardware CGB. __vram_copy_dma es otro alias del compilador para un bucle de copia por CPU; su nombre no implica usar este controlador DMA.
Ejemplo de uso
__cgb_safe_set_hdma4(0x1F); // Destination aligns to $8010, not $801F.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa compara tres rutas con un búfer alineado a 16 bytes en $C600. GDMA copia 32 bytes a $8010: en pantalla (16,24) aparece un triángulo rojo de 8 × 8 que crece desde un píxel izquierdo hasta una fila inferior completa; en (24,24), un contorno cuadrado rojo de un píxel. HBlank DMA copia 32 bytes a $8030: en (16,48), media casilla azul izquierda de 4 × 8; en (24,48), un cuadrado azul de 8 × 8. Son patrones realmente transferidos, no indicadores numéricos.
CANCELED muestra media casilla verde de 4 × 8 en (16,72) y un cuadrado verde de 8 × 8 en (24,72), escritos por CPU. Se prepara y cancela un HBlank DMA de dos bloques en el mismo VBlank, antes del primer bloque. $FF55 debe valer $81; ambos patrones deben seguir intactos tras un fotograma. En DMG, las dos primeras filas quedan vacías y solo estos testigos aparecen negros. Texto negro sobre blanco. FAILED CHECKS 000 comprueba también bytes y estado final. Se prueba en KOKURA ROM dual DMG/CGB y solo CGB; no cancelación parcial, doble velocidad ni hardware real.



Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1874
__cgb_safe_set_hdma5
Escribe HDMA5 ($FF55) para iniciar DMA o cancelar un HBlank activo. En un nuevo traslado, bits 0–6 codifican bloques menos uno y bit 7 elige GDMA (0) o HBlank DMA (1). Durante HBlank DMA activo, escribir bit 7 a cero cancela lo pendiente; no inicia otro GDMA.
Sintaxis
void __cgb_safe_set_hdma5(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Un bloque son 16 bytes. Un nuevo traslado admite 1–128 bloques (16–2048 bytes): el valor 0 significa 16 bytes, no un traslado vacío. GDMA pausa la CPU hasta terminar. HBlank DMA copia un bloque por HBlank visible y puede abarcar varios fotogramas; comprueba su finalización antes de reutilizar los datos.
Usa GDMA con LCD apagado o en un intervalo escribible suficientemente largo. Inicia HBlank DMA con LCD activo y fuera del modo STAT 0; el ejemplo lo inicia en VBlank. No cambies bancos de origen, VBK de destino ni datos durante el traslado. HALT también pausa HBlank DMA. Estas funciones no añaden esperas, protección de interrupciones ni comprobación de rangos.
Lee $FF55 por separado: bit 7 a cero indica actividad; $FF, finalización. Tras cancelar, bit 7 vale uno y bits 0–6 conservan bloques pendientes menos uno. No se deshacen bytes copiados. Cancela solo un traslado activo; de lo contrario, bit 7 a cero inicia GDMA. Mantén todo el traslado dentro de memoria válida y del banco VRAM de destino, sin depender del desbordamiento de dirección.
Estos setters HDMA controlan DMA por hardware CGB. __vram_copy_dma es otro alias del compilador para un bucle de copia por CPU; su nombre no implica usar este controlador DMA.
Ejemplo de uso
__cgb_safe_set_hdma5(1); // LCD is off: GDMA, (1+1)*16 = 32 bytes.
if (color_mode && D_CONTROL!=255) failures++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa compara tres rutas con un búfer alineado a 16 bytes en $C600. GDMA copia 32 bytes a $8010: en pantalla (16,24) aparece un triángulo rojo de 8 × 8 que crece desde un píxel izquierdo hasta una fila inferior completa; en (24,24), un contorno cuadrado rojo de un píxel. HBlank DMA copia 32 bytes a $8030: en (16,48), media casilla azul izquierda de 4 × 8; en (24,48), un cuadrado azul de 8 × 8. Son patrones realmente transferidos, no indicadores numéricos.
CANCELED muestra media casilla verde de 4 × 8 en (16,72) y un cuadrado verde de 8 × 8 en (24,72), escritos por CPU. Se prepara y cancela un HBlank DMA de dos bloques en el mismo VBlank, antes del primer bloque. $FF55 debe valer $81; ambos patrones deben seguir intactos tras un fotograma. En DMG, las dos primeras filas quedan vacías y solo estos testigos aparecen negros. Texto negro sobre blanco. FAILED CHECKS 000 comprueba también bytes y estado final. Se prueba en KOKURA ROM dual DMG/CGB y solo CGB; no cancelación parcial, doble velocidad ni hardware real.



Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1875
__cgb_safe_set_obpd
Escribe un byte en el registro de datos OCPD/OBPD ($FF6B) de las paletas de sprites (OBJ) en CGB. __cgb_safe_set_ocpd es otro nombre del mismo registro. El registro de índice selecciona la posición; un color RGB15 completo necesita un byte bajo y otro alto.
Sintaxis
__cgb_safe_set_obpd(/* 書式は実装と呼び出し例を参照 */);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueUn byte, 0–255. Escribe primero el bajo y después el alto de RGB15; activa antes el incremento automático o selecciona cada posición. Escribir datos no activa el incremento por sí solo.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_obpd(0); __cgb_safe_set_obpd(0x7C); // Alias of OCPD: blue at slot 31.
expect_color(1,31,31744);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1869
__cgb_safe_set_obpi
Escribe en CGB el registro de índice de byte OCPS/OBPI ($FF6A) de las paletas de sprites (OBJ). __cgb_safe_set_ocps accede al mismo registro. Selecciona la posición del siguiente acceso al registro de datos; no escribe un color por sí solo.
Sintaxis
__cgb_safe_set_obpi(/* 書式は実装と呼び出し例を参照 */);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueÍndice de byte en bits 0–5 (0–63); el bit 7 activa el incremento tras escribir datos. El bit 6 no se usa. La posición de color n empieza en el byte 2n. Tras 63 vuelve a cero: controla el rango al escribir registros directamente.
Valor devuelto
Ninguno (void).
Condiciones y límites
Las escrituras de paleta y de registros protegidos no escriben nada en modo DMG. Safe indica una comprobación de modo CGB, no una espera de acceso a PPU ni una sección crítica. Configura con LCD apagado o realiza una actualización breve durante una fase accesible. No espera VBlank ni protege una escritura de varios bytes frente a interrupciones.
Ejemplo de uso
__cgb_safe_set_obpi(0xBE); // Alias of OCPS: select the OBJ store's last word.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa muestra cómo las paletas BG y OBJ colorean de forma independiente el mismo patrón. Cada triángulo ocupa 8 × 8 píxeles: comienza con un píxel a la izquierda arriba y termina con una fila completa abajo. En x = 16, 32, 48, 64, 80, 96, 112, la fila BG de y = 24 es roja, azul, verde, roja, azul, verde, roja; la OBJ de y = 64 es azul, verde, roja, azul, verde, roja, azul. Las columnas usan, respectivamente, color único, componentes, rango indexado, paleta completa, rango absoluto, registro canónico y alias de registro.
En y = 88 hay siete casillas de 8 × 8: triángulo rojo (x = 16), contorno cuadrado azul de un píxel del banco 1 (32), triángulo verde reflejado horizontalmente (48), rojo reflejado verticalmente (64), azul reflejado en ambos ejes (80), triángulo verde sobre un sprite rojo visible a través del color cero (96), y sprite rojo sólido delante del triángulo BG como control sin prioridad (112). Las coordenadas son píxeles desde la esquina superior izquierda. Fondo blanco y texto negro.
FAILED CHECKS 000 confirma máscaras RGB, booleanos (incluido 2), índices de paleta normalizados, cruce de paletas, límite en posición 31, punteros nulos y longitud cero sin escrituras, y lectura de colores con LCD apagado. Se prueban cuatro combinaciones: ROM dual en DMG/CGB, ROM solo DMG en DMG y solo CGB en CGB. En DMG, CGB MODE vale 0, los triángulos son negros, banco/reflejos no afectan a la imagen y las dos últimas casillas inferiores son cuadrados negros. En CGB, CGB MODE vale 1. No se prueban hardware real ni conflictos de paleta con LCD encendido.




Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1867
__cgb_safe_set_svbk
Escribe el selector WRAM SVBK ($FF70) en CGB. El hardware usa bits 0–2; tanto 0 como 1 asignan el banco 1 a $D000–$DFFF. El 8 también asigna el banco 1. A diferencia de __svbk_set, permite un destino dual y no devuelve el banco anterior.
Sintaxis
void __cgb_safe_set_svbk(u8 value);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
valueValor de registro como
u8(0–255). Los bits se describen arriba para este registro; recibe un byte, no un puntero ni una cantidad de bytes.
Valor devuelto
Ninguno (void).
Condiciones y límites
El compilador condiciona la escritura al modo CGB; no escribe en DMG. Safe solo indica esta comprobación. No guarda ni restaura el valor previo del registro.
Cambiar de banco afecta a todos los accesos en $D000–$DFFF; la WRAM fija de $C000–$CFFF no cambia. Mantén pila y estado necesario fuera de esa ventana. El ejemplo usa --stack-bank=fixed y variables ubicadas explícitamente en WRAM fija. Coordina interrupciones y DMA bancado; estas API no crean una sección crítica.
Ejemplo de uso
__cgb_safe_set_svbk(0); // Raw selector 0 maps effective bank 1; this API returns void.
if ((W_SELECTOR&7)!=0) failures++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa solo CGB guarda 1 en $D100 del banco WRAM 2 y 2 en la misma dirección del banco 3; lee ambos y restaura el banco 1. Esos valores eligen un cuadrado rojo de 8 × 8 en (16,24) y un contorno azul de 8 × 8, de un píxel, en (16,48). FAILED CHECKS 000 verifica normalización, separación y retorno del banco previo con constantes, variables, expresiones y resultados de función. Seis compilaciones inválidas verifican KQ2103 en DMG/dual y KQ2102 para bancos constantes 0 y 8. Ejecución en KOKURA; hardware real y cambios concurrentes por interrupciones no probados.

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:1862
__critical_enter
Incrementa la profundidad de anidamiento del software y ejecuta DI.
Sintaxis
u8 __critical_enter();Valor devuelto
Devuelve la profundidad anterior como token u8: cero para el ámbito exterior y uno para el primer ámbito anidado.
Condiciones y límites
Pasa los tokens a leave en orden inverso y evita que la profundidad se desborde en 256. No se guarda el IME de hardware anterior; no uses esta pareja si necesitas conservar un estado DI previo.
Ejemplo de uso
*((u8*)0xFFFF)=0;outer=__critical_enter();inner=__critical_enter();result[7]=outer;result[8]=inner;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7970
kitaqgb/kitaqgb/CodeGenerator.cs:7970
if (funcName == "__critical_enter")
{
if (args.Length != 0) Error(expr, "__critical_enter() expects 0 arguments");
EmitLoadA(CriticalDepthVar);
EmitAsm("LD_B_A"); // token = old depth
EmitAsm("INC_A");
EmitStoreA(CriticalDepthVar);
EmitAsm("DI");
EmitAsm("LD_A_B");
return;
}__critical_leave
Reduce la profundidad de anidamiento y ejecuta EI para el token exterior cero.
Sintaxis
void __critical_leave(u8 token);Parámetros
tokenToken devuelto por la llamada correspondiente a __critical_enter.
Valor devuelto
No devuelve ningún valor.
Condiciones y límites
Los tokens distintos de cero no ejecutan EI. Pasar un cero arbitrario puede activar interrupciones aunque haya otros ámbitos activos; empareja las llamadas correctamente.
Ejemplo de uso
__critical_leave(inner);__critical_leave(outer);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7984
kitaqgb/kitaqgb/CodeGenerator.cs:7984
if (funcName == "__critical_leave")
{
if (args.Length != 1) Error(expr, "__critical_leave(token) expects 1 argument");
CompileIntoA(args[0]);
EmitAsm("LD_B_A"); // token
AsmOperand clAfterDec = MakeUniqueLabel("critleave_afterdec");
AsmOperand clDone = MakeUniqueLabel("critleave_done");
EmitLoadA(CriticalDepthVar);
EmitAsm("OR_A");
EmitAsm("JR_Z", clAfterDec);
EmitAsm("DEC_A");
EmitStoreA(CriticalDepthVar);
EmitLabel(clAfterDec);
// Re-enable only for outermost token (token==0).
EmitAsm("LD_A_B");
EmitAsm("OR_A");
EmitAsm("JR_NZ", clDone);
EmitAsm("EI");
EmitLabel(clDone);
return;
}__dot2_q8_8
Calcula un producto escalar sin signo de dos términos.
Sintaxis
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Parámetros
a0Palabra sin signo de 0 a 65535.
a1Palabra sin signo de 0 a 65535.
b0Coeficiente sin signo de 0 a 255.
b1Coeficiente sin signo de 0 a 255.
Valor devuelto
Cada término es floor(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[36]=__dot2_q8_8(513,769,64,127);
v0=513;v1=769;v2=64;v3=127;
result[37]=__dot2_q8_8(v0,v1,v2,v3);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[36]=509, result[37]=509. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11575
kitaqgb/kitaqgb/CodeGenerator.cs:11575
if (funcName == "__dot2_q8_8")
{
if (args.Length != 4) Error(expr, "__dot2_q8_8 requires 4 arguments");
LastCallReturnsHL = true;
// Fast paths when coefficients are compile-time constants.
// Special-case: 0 / power-of-two coefficients (u8) => shift instead of mul.
if (TryGetU8Const(args[2], out int ax0) && (ax0 == 0) && TryGetU8Const(args[3], out int ay0) && (ay0 == 0))
{
CompileDiscard(args[0]);
CompileDiscard(args[1]);
EmitAsm("XOR_A");
EmitAsm("LD_H_A");
EmitAsm("LD_L_A");
return;
}
if (TryGetU8Const(args[2], out int axOnly) && (axOnly == 0))
{
CompileDiscard(args[0]);
// only y*ay
if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
{
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[3]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitMul16x8Shr8_HL("__dot2_y");
}
return;
}
if (TryGetU8Const(args[3], out int ayOnly) && (ayOnly == 0))
{
// only x*ax
if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitMul16x8Shr8_HL("__dot2_x");
}
// Preserve the scaled result while evaluating the zero-product input.
EmitAsm("PUSH_HL");
CompileDiscard(args[1]);
EmitAsm("POP_HL");
return;
}
// x*ax
if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitMul16x8Shr8_HL("__dot2_x");
}
EmitAsm("PUSH_HL");
// y*ay
if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
{
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[3]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitMul16x8Shr8_HL("__dot2_y");
}
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__dot3_q8_8
Calcula un producto escalar sin signo de tres términos.
Sintaxis
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parámetros
a0Palabra sin signo de 0 a 65535.
a1Palabra sin signo de 0 a 65535.
a2Palabra sin signo de 0 a 65535.
b0Coeficiente sin signo de 0 a 255.
b1Coeficiente sin signo de 0 a 255.
b2Coeficiente sin signo de 0 a 255.
Valor devuelto
Cada término es floor(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[6]=__dot3_q8_8(513,769,1025,64,127,3);
v0=513;v1=769;v2=1025;v3=64;v4=127;v5=3;
result[7]=__dot3_q8_8(v0,v1,v2,v3,v4,v5);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[6]=521, result[7]=521. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11550
kitaqgb/kitaqgb/CodeGenerator.cs:11550
if (funcName == "__dot3_q8_8")
{
if (args.Length != 6) Error(expr, "__dot3_q8_8 requires 6 arguments");
LastCallReturnsHL = true;
// x*ax
EmitDotTermToHL(args[0], args[3], signed: false, q1_7: false, labelPrefix: "__dot3_x");
EmitAsm("PUSH_HL");
// y*ay
EmitDotTermToHL(args[1], args[4], signed: false, q1_7: false, labelPrefix: "__dot3_y");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
EmitAsm("PUSH_HL");
// z*az
EmitDotTermToHL(args[2], args[5], signed: false, q1_7: false, labelPrefix: "__dot3_z");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__farcall
Llama a una función con nombre y sin argumentos usando el banco indicado. Cuando hace falta cambiar de banco, una rutina en ROM fija restaura el banco del llamador al regresar.
Sintaxis
__farcall(bank, function_name);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
bankNúmero de banco ROM de 8 bits sin signo. En una llamada debe coincidir con la ubicación de la función; en una lectura selecciona el origen. Usa el intervalo válido para el cartucho.
funcNombre declarado de una función sin parámetros. Esta intrínseca de llamada por nombre no acepta una variable que contenga una dirección de función. El argumento bank no se pasa al callback.
Valor devuelto
El tipo de resultado depende de la función llamada. Un callback void no devuelve valor; los callbacks u8 y u16 devuelven su valor escalar. El ejemplo mostrado usa callbacks void.
Condiciones y límites
Estas llamadas no pasan argumentos del usuario ni hacen que el callback admita reentrada por interrupciones. Gestiona correctamente sus datos, pila e interrupciones. La restauración de bancos se prueba con llamadas anidadas y repetidas; el ejemplo no cubre hardware físico ni cualquier posible intercalado de interrupciones.
Estas interfaces GB manejan ocho bits de banco y no ajustan el noveno bit de MBC5. Siguen vigentes las reglas del cartucho, incluido el tratamiento del banco cero. El ejemplo usa explícitamente los bancos MBC5 1–3; no mezcles este estado con escrituras al mapper sin registrar.
Ejemplo de uso
__farcall(3,callback);
bank_expect(callback_count==2 && callback_byte==0xD3,16);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
En este punto del programa completo, __farcall eleva callback_count a 2. El callback lee D3 del banco 3. Una lectura cercana posterior de B2 en home2 verifica el retorno con el banco 2 seleccionado.
El programa completo enseña a identificar datos con un banco y una dirección cercana, a llamar callbacks entre bancos y a distinguir el registro de la biblioteca del mapa real. Todos los números se muestran en hexadecimal. BANK IN y BANK OUT deben ser 02; BYTE HEX, D3; WORD HEX, 5C7A; FAILED CHECKS y FIRST FAILURE, 00. CGB y FC muestran texto azul; DMG, negro.
CALL COUNT muestra 03: far_call, __farcall y __farcall_ptr invocan el callback una vez cada uno.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7288
kitaqgb/kitaqgb/CodeGenerator.cs:7288
if (funcName == "__farcall")
{
if (args.Length != 2) Program.Error("__farcall(bank, func) expects 2 arguments");
if (!args[1].Match(Tag.Name, out string targetName)) Program.Error("__farcall(bank, func) requires 2nd argument to be a function name");
// A named far call supplies no arguments. Reject data symbols
// and callbacks that would otherwise read stale argument storage.
CFunctionInfo targetInfo;
if (!Functions.TryGetValue(targetName, out targetInfo))
{
Error(expr, "__farcall requires a declared function name");
return;
}
if ((targetInfo.Parameters ?? Array.Empty<FieldInfo>()).Length != 0)
{
Error(expr, "__farcall requires a callback with no parameters");
return;
}
Expr bankExpr = FoldConstants(args[0]);
int targetBank = -1;
if (targetInfo != null)
{
targetBank = targetInfo.RomBank;
PrepareStructReturnDestination(expr, targetInfo.ReturnType, targetInfo, targetName);
if (targetInfo.ReturnType != null)
LastCallReturnsHL = ReturnsInHL(expr, targetInfo.ReturnType);
}
// A known bank permits direct dispatch when safe, otherwise request a fixed-bank thunk.
if (TryResolveBankExprToConstU8(args[0], out int bankLiteral))
{
int optimizedBank = bankLiteral & 0xFF;
bool farcallDirectOK = IsDirectCallBankSafe(optimizedBank, CurrentFunctionBank);
if (!farcallDirectOK)
{
string farcallThunkName = EnsureBank0Thunk(
targetName,
optimizedBank,
isStackCall: targetInfo != null && targetInfo.IsStackCall,
stackArgBytes: 0);
EmitAsm("CALL", new AsmOperand(farcallThunkName, AddressMode.Absolute));
RecordCallEdge(expr, targetName, optimizedBank, "farcall_bank_thunk", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
}
else
{
if (Program.CheckBankCalls && !InUnsafe && optimizedBank != 0)
{
AsmOperand ok = MakeUniqueLabel("kq_bank_ok");
EmitLoadA(RomBankVar);
EmitAsm("CP_IMM", new AsmOperand(optimizedBank & 0xFF, AddressMode.Immediate));
EmitAsm("JP_Z", ok);
EmitAsm("JP", EnsureCheckTrap());
EmitLabel(ok);
}
EmitAsm("CALL", new AsmOperand(targetName, AddressMode.Absolute));
RecordCallEdge(expr, targetName, optimizedBank, "farcall_direct", viaThunk: false, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
}
return;
}
// A = requested bank argument (runtime value)
CompileIntoA(bankExpr);
// Always dispatch via bank0 helper thunk.
string thunkName = EnsureBank0RuntimeFarcallThunk(targetName);
EmitAsm("CALL", new AsmOperand(thunkName, AddressMode.Absolute));
RecordCallEdge(expr, targetName, targetBank, "farcall_intrinsic", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
return;
}__getwinmapbase
Lee el bit 6 de LCDC para seleccionar la base del mapa de tiles de la ventana.
Sintaxis
u16 __getwinmapbase();Valor devuelto
Devuelve $9800 o $9C00, la base del mapa de tiles, no la dirección de los gráficos de tiles.
Ejemplo de uso
M_LCDC=0;result[2]=__getwinmapbase();M_LCDC=0x40;result[3]=__getwinmapbase();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10642
kitaqgb/kitaqgb/CodeGenerator.cs:10642
if (funcName == "__getwinmapbase")
{
if (args.Length != 0) Error(expr, "__getwinmapbase requires 0 arguments");
AsmOperand win_base0 = MakeUniqueLabel("getwinbase_base0");
AsmOperand win_done = MakeUniqueLabel("getwinbase_done");
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)); // bit6 (Window tile map display select)
EmitAsm("JR_Z", win_base0);
EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
EmitAsm("JR", win_done);
EmitLabel(win_base0);
EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
EmitLabel(win_done);
LastCallReturnsHL = true;
return;
}__mac16
Calcula una multiplicación-acumulación sin signo.
Sintaxis
u16 __mac16(u16 acc, u16 a, u8 b);Parámetros
accAcumulador de 16 bits que se va a sumar.
aPalabra sin signo de 0 a 65535.
bCoeficiente sin signo de 0 a 255.
Valor devuelto
Cada término es floor(a*b/256) y se suma a acc; devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[34]=__mac16(65500,513,127);
v0=65500;v1=513;v2=127;
result[35]=__mac16(v0,v1,v2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[34]=218, result[35]=218. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11499
kitaqgb/kitaqgb/CodeGenerator.cs:11499
if (funcName == "__mac16")
{
if (args.Length != 3) Error(expr, "__mac16 requires 3 arguments");
// mul = (a*b)>>8
EmitDotTermToHL(args[1], args[2], signed: false, q1_7: false, labelPrefix: "__mac16_mul");
// acc + mul
EmitAsm("PUSH_HL");
CompileIntoHL(args[0]);
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
LastCallReturnsHL = true;
return;
}__mul16x8
Calcula una multiplicación sin signo.
Sintaxis
u16 __mul16x8(u16 a, u8 b);Parámetros
aPalabra sin signo de 0 a 65535.
bCoeficiente sin signo de 0 a 255.
Valor devuelto
Cada término es floor(a*b/256); devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[24]=__mul16x8(513,127);
v0=513;v1=127;
result[25]=__mul16x8(v0,v1);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[24]=254, result[25]=254. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11467
kitaqgb/kitaqgb/CodeGenerator.cs:11467
if (funcName == "__mul16x8")
{
if (args.Length != 2) Error(expr, "__mul16x8 requires 2 arguments");
EmitDotTermToHL(args[0], args[1], signed: false, q1_7: false, labelPrefix: "__mul16x8");
LastCallReturnsHL = true;
return;
}__mul8x8_hi
Multiplica dos bytes sin signo y devuelve el byte alto.
Sintaxis
u8 __mul8x8_hi(u8 a, u8 b);Parámetros
aByte sin signo de 0 a 255.
bByte sin signo de 0 a 255.
Valor devuelto
floor(a*b/256), en el rango 0..254.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[4]=__mul8x8_hi(255,255);
v0=255;v1=255;
result[5]=__mul8x8_hi(v0,v1);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[4]=254, result[5]=254. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11452
kitaqgb/kitaqgb/CodeGenerator.cs:11452
if (funcName == "__mul8x8_hi")
{
if (args.Length != 2) Error(expr, "__mul8x8_hi requires 2 arguments");
CompileIntoA(args[0]);
EmitAsm("PUSH_AF");
CompileIntoA(args[1]);
EmitAsm("LD_C_A");
EmitAsm("POP_AF");
EmitAsm("LD_E_A");
EmitMul8x8ToHL_EC("__mul8x8_hi");
EmitAsm("LD_A_H");
return;
}__padrep
Programa la repetición de una sola acción seleccionada por mask. Si hay botones admitidos recién pulsados, elige el bit activo de menor peso y lo devuelve inmediatamente. En caso contrario, conserva la acción activa mientras siga admitida y mantenida. Cuando termina, adopta el bit de menor peso que continúe pulsado, sin emitir un pulso inmediato. Si no queda ninguno, borra el estado de ejecución.
Sintaxis
u8 __padrep(u8* state, u8 keys, u8 trigger, u8 mask);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.keysMáscara completa de botones mantenidos con el formato GB
BTN_*. Pasa una lectura de__readpad, el byte bajo de__readpadexoinput_current; la función de repetición no lee el hardware.triggerBits recién pulsados en esta actualización, con el mismo formato que
keys: normalmentekeys & ~previous_keyso el byte alto de__readpadex. Incluye solo bits presentes también enkeys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.maskBits de botones GB admitidos, combinados con
|. Cero desactiva todas las acciones y borra el estado de ejecución. Varios bits permiten varios candidatos, pero la función devuelve un único bit seleccionado por llamada, no una máscara de pulsos combinados.
Valor devuelto
Cero si no corresponde emitir un pulso; en caso contrario, uno de estos valores: 1, 2, 4, 8, 16, 32, 64, 128. El resultado distinto de cero es un bit de botón, no un booleano normalizado ni un recuento de repeticiones.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Espera inicial de 0 a 255 llamadas a la función de repetición. Con un valor positivo N, la primera repetición mantenida ocurre exactamente N llamadas después del pulso inicial. Con 0 se omite el caso de igualdad del retardo inicial y el intervalo determina cuándo llega el primer pulso mantenido. No se mide en milisegundos.
Intervalo de las repeticiones posteriores, de 0 a 255 llamadas. Un N positivo genera un pulso cada N actualizaciones válidas de mantenimiento tras la primera repetición. Cero genera uno en cada actualización válida después de la espera inicial; en esa fase, tanto 0 como 1 dan un pulso por actualización. Si espera e intervalo son 0, cada actualización mantenida emite un pulso.
Llama a la función una vez por ciclo de entrada previsto. Cada llamada modifica el estado y no espera a VBlank. Adoptar un botón mantenido sin un bit de nueva pulsación devuelve 0 en esa llamada e inicia una nueva espera. Estas funciones eligen una acción activa por estado; input_repeat mantiene temporizadores independientes para los ocho botones.
Ejemplo de uso
result=__padrep(actions,keys,trigger,48); // A|B: select A, the lower bit.
if (result!=0) action_pulses++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11249
{
if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");
AsmOperand pm_done = MakeUniqueLabel("padrepM_done");
AsmOperand pm_ret0 = MakeUniqueLabel("padrepM_ret0");
AsmOperand pm_no_trig = MakeUniqueLabel("padrepM_notrig");
AsmOperand pm_reset_all = MakeUniqueLabel("padrepM_reset");
AsmOperand pm_dir0 = MakeUniqueLabel("padrepM_dir0");
AsmOperand pm_have_dir = MakeUniqueLabel("padrepM_havedir");
AsmOperand pm_first_rep = MakeUniqueLabel("padrepM_firstrep");
AsmOperand pm_ret_dir = MakeUniqueLabel("padrepM_retdir");
AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");
// Evaluate args safely:
// HL = state*, B = keys, C = trigger, E = mask
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
EmitAsm("POP_HL");
// trigMasked = trigger & mask -> D
EmitAsm("LD_A_C");
EmitAsm("AND_E");
EmitAsm("LD_D_A");
EmitAsm("OR_A");
EmitAsm("JP_Z", pm_no_trig);
// select = lowbit(trigMasked) = x & -x
EmitAsm("LD_A_D");
EmitAsm("CPL");
EmitAsm("INC_A");
EmitAsm("AND_D");
EmitAsm("LD_C_A"); // save selected in C
// dirMask=selected, dasCnt=0, arrCnt=0
EmitAsm("LD_A_C");
EmitAsm("LD_HL_A"); // [0]=selected
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); Emi__padrep_down
Avanza un paso la repetición exclusiva de abajo. Una nueva pulsación de abajo devuelve 8 inmediatamente y reinicia los contadores. Mantener abajo genera los siguientes pulsos según los tiempos del estado; soltarlo borra el estado de ejecución. Los demás botones no activan esta repetición.
Sintaxis
u8 __padrep_down(u8* state, u8 keys, u8 trigger);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.keysMáscara completa de botones mantenidos con el formato GB
BTN_*. Pasa una lectura de__readpad, el byte bajo de__readpadexoinput_current; la función de repetición no lee el hardware.triggerBits recién pulsados en esta actualización, con el mismo formato que
keys: normalmentekeys & ~previous_keyso el byte alto de__readpadex. Incluye solo bits presentes también enkeys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.
Valor devuelto
Cero si no corresponde emitir un pulso; en caso contrario, uno de estos valores: 8. El resultado distinto de cero es un bit de botón, no un booleano normalizado ni un recuento de repeticiones.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Espera inicial de 0 a 255 llamadas a la función de repetición. Con un valor positivo N, la primera repetición mantenida ocurre exactamente N llamadas después del pulso inicial. Con 0 se omite el caso de igualdad del retardo inicial y el intervalo determina cuándo llega el primer pulso mantenido. No se mide en milisegundos.
Intervalo de las repeticiones posteriores, de 0 a 255 llamadas. Un N positivo genera un pulso cada N actualizaciones válidas de mantenimiento tras la primera repetición. Cero genera uno en cada actualización válida después de la espera inicial; en esa fase, tanto 0 como 1 dan un pulso por actualización. Si espera e intervalo son 0, cada actualización mantenida emite un pulso.
Llama a la función una vez por ciclo de entrada previsto. Cada llamada modifica el estado y no espera a VBlank. Adoptar un botón mantenido sin un bit de nueva pulsación devuelve 0 en esa llamada e inicia una nueva espera. Estas funciones eligen una acción activa por estado; input_repeat mantiene temporizadores independientes para los ocho botones.
Ejemplo de uso
result=__padrep_down(down,keys,trigger);
if (result!=0) down_pulses++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11069
{
if (args.Length != 3) Error(expr, "__padrep_down requires 3 arguments (state*, keys, trigger)");
AsmOperand pd_done = MakeUniqueLabel("padrepD_done");
AsmOperand pd_ret0 = MakeUniqueLabel("padrepD_ret0");
AsmOperand pd_no_trig = MakeUniqueLabel("padrepD_notrig");
AsmOperand pd_dir0 = MakeUniqueLabel("padrepD_dir0");
AsmOperand pd_have_dir = MakeUniqueLabel("padrepD_havedir");
AsmOperand pd_first_rep = MakeUniqueLabel("padrepD_firstrep");
AsmOperand pd_ret_dir = MakeUniqueLabel("padrepD_retdir");
AsmOperand pd_skip_dasinc = MakeUniqueLabel("padrepD_skipdasinc");
AsmOperand pd_skip_arrinc = MakeUniqueLabel("padrepD_skiparrinc");
AsmOperand pd_reset_all = MakeUniqueLabel("padrepD_reset");
// Evaluate args safely:
// HL = state*, B = keys, C = trigger
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
EmitAsm("POP_HL");
// --- Trigger Down? (bit3 = 0x08) ---
EmitAsm("LD_A_C");
EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate));
EmitAsm("JP_Z", pd_no_trig);
// dirMask=0x08, dasCnt=0, arrCnt=0
EmitAsm("LD_A_IMM", new AsmOperand(0x08, AddressMode.Immediate));
EmitAsm("LD_HL_A"); // [0]=0x08
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // dasCnt=0
EmitAsm("INC_HL");
EmitAsm("LD_HL_A"); // arrCnt=0
EmitAsm("POP_HL");
EmitAsm__padrep_init
Inicializa los cinco bytes de estado que proporciona el programa: borra la acción activa y ambos contadores, y guarda la espera inicial y el intervalo de repetición. No lee botones ni genera pulsos.
Sintaxis
void __padrep_init(u8* state, u8 das, u8 arr);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.dasEspera inicial de 0 a 255 llamadas a la función de repetición. Con un valor positivo N, la primera repetición mantenida ocurre exactamente N llamadas después del pulso inicial. Con 0 se omite el caso de igualdad del retardo inicial y el intervalo determina cuándo llega el primer pulso mantenido. No se mide en milisegundos.
arrIntervalo de las repeticiones posteriores, de 0 a 255 llamadas. Un N positivo genera un pulso cada N actualizaciones válidas de mantenimiento tras la primera repetición. Cero genera uno en cada actualización válida después de la espera inicial; en esa fase, tanto 0 como 1 dan un pulso por actualización. Si espera e intervalo son 0, cada actualización mantenida emite un pulso.
Valor devuelto
Ninguno (void).
Ejemplo de uso
__padrep_init(lr,3,2);
// Active action and counters are zero; delay and interval are retained.
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10808
{
if (args.Length != 3) Error(expr, "__padrep_init requires 3 arguments (state*, das, arr)");
// Evaluate args with register safety
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]); EmitAsm("LD_B_A"); // das
CompileIntoA(args[2]); EmitAsm("LD_C_A"); // arr
EmitAsm("POP_HL");
// dirMask = 0; dasCnt=0; arrCnt=0; store params
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // [0]=0
EmitAsm("INC_HL");
EmitAsm("LD_A_B");
EmitAsm("LD_HL_A"); // [1]=das
EmitAsm("INC_HL");
EmitAsm("LD_A_C");
EmitAsm("LD_HL_A"); // [2]=arr
EmitAsm("INC_HL");
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // [3]=dasCnt
EmitAsm("INC_HL");
EmitAsm("LD_HL_A"); // [4]=arrCnt
return;
}
// 2.21 __padrep_reset(state_ptr) -> void
// Resets runtime counters for PadRepeatState while keeping das/arr params:
// dirMask=0, dasCnt=0, arrCnt=0
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__padrep_reset")
{
if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");
CompileIntoHL(args[0]);
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // [0]=0 (dirMask)
// [3]=0, [4]=0
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
EmitAsm("LD_HL_A");
EmitAsm("INC_HL"); // -> [4]
EmitAsm("LD_HL_A");
EmitAsm("POP_HL");
return;
}
// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new__padrep_lr
Avanza un paso la repetición de izquierda o derecha. Una nueva pulsación de derecha devuelve 1 de inmediato; en su defecto, una de izquierda devuelve 2. Si ambas son nuevas, gana derecha. Sin pulsaciones nuevas, mantener ambas direcciones devuelve 0 y congela los contadores; no mantener ninguna borra el estado de ejecución.
Sintaxis
u8 __padrep_lr(u8* state, u8 keys, u8 trigger);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.keysMáscara completa de botones mantenidos con el formato GB
BTN_*. Pasa una lectura de__readpad, el byte bajo de__readpadexoinput_current; la función de repetición no lee el hardware.triggerBits recién pulsados en esta actualización, con el mismo formato que
keys: normalmentekeys & ~previous_keyso el byte alto de__readpadex. Incluye solo bits presentes también enkeys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.
Valor devuelto
Cero si no corresponde emitir un pulso; en caso contrario, uno de estos valores: 1, 2. El resultado distinto de cero es un bit de botón, no un booleano normalizado ni un recuento de repeticiones.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Espera inicial de 0 a 255 llamadas a la función de repetición. Con un valor positivo N, la primera repetición mantenida ocurre exactamente N llamadas después del pulso inicial. Con 0 se omite el caso de igualdad del retardo inicial y el intervalo determina cuándo llega el primer pulso mantenido. No se mide en milisegundos.
Intervalo de las repeticiones posteriores, de 0 a 255 llamadas. Un N positivo genera un pulso cada N actualizaciones válidas de mantenimiento tras la primera repetición. Cero genera uno en cada actualización válida después de la espera inicial; en esa fase, tanto 0 como 1 dan un pulso por actualización. Si espera e intervalo son 0, cada actualización mantenida emite un pulso.
Llama a la función una vez por ciclo de entrada previsto. Cada llamada modifica el estado y no espera a VBlank. Adoptar un botón mantenido sin un bit de nueva pulsación devuelve 0 en esa llamada e inicia una nueva espera. Estas funciones eligen una acción activa por estado; input_repeat mantiene temporizadores independientes para los ocho botones.
Ejemplo de uso
result=__padrep_lr(lr,keys,trigger);
if (result!=0) pulses++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10864
{
if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");
AsmOperand pr_done = MakeUniqueLabel("padrep_done");
AsmOperand pr_ret0 = MakeUniqueLabel("padrep_ret0");
AsmOperand pr_check_left = MakeUniqueLabel("padrep_chkL");
AsmOperand pr_no_trig = MakeUniqueLabel("padrep_notrig");
AsmOperand pr_reset_all = MakeUniqueLabel("padrep_reset");
AsmOperand pr_dir0 = MakeUniqueLabel("padrep_dir0");
AsmOperand pr_have_dir = MakeUniqueLabel("padrep_havedir");
AsmOperand pr_first_rep = MakeUniqueLabel("padrep_firstrep");
AsmOperand pr_ret_dir = MakeUniqueLabel("padrep_retdir");
AsmOperand pr_skip_dasinc = MakeUniqueLabel("padrep_skipdasinc");
AsmOperand pr_skip_arrinc = MakeUniqueLabel("padrep_skiparrinc");
// Evaluate args safely:
// HL = state*, B = keys, C = trigger
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
EmitAsm("POP_HL");
// --- Trigger Right? (bit0) ---
EmitAsm("LD_A_C");
EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
EmitAsm("JP_Z", pr_check_left);
// dirMask=0x01, dasCnt=0, arrCnt=0
EmitAsm("LD_A_IMM", new AsmOperand(0x01, AddressMode.Immediate));
EmitAsm("LD_HL_A"); // [0]=0x01
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // dasCnt=0
EmitAsm("INC_HL");
EmitAsm("LD_HL_A")__padrep_mask
__padrep_mask es otro nombre de __padrep; ambas llamadas usan la misma implementación del compilador. Comparten selección de una sola acción, tiempos, formato del estado y valores devueltos. El nombre explícito ayuda a reconocer en la llamada que la selección se hace mediante una máscara.
Programa la repetición de una sola acción seleccionada por mask. Si hay botones admitidos recién pulsados, elige el bit activo de menor peso y lo devuelve inmediatamente. En caso contrario, conserva la acción activa mientras siga admitida y mantenida. Cuando termina, adopta el bit de menor peso que continúe pulsado, sin emitir un pulso inmediato. Si no queda ninguno, borra el estado de ejecución.
Sintaxis
u8 __padrep_mask(u8* state, u8 keys, u8 trigger, u8 mask);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.keysMáscara completa de botones mantenidos con el formato GB
BTN_*. Pasa una lectura de__readpad, el byte bajo de__readpadexoinput_current; la función de repetición no lee el hardware.triggerBits recién pulsados en esta actualización, con el mismo formato que
keys: normalmentekeys & ~previous_keyso el byte alto de__readpadex. Incluye solo bits presentes también enkeys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.maskBits de botones GB admitidos, combinados con
|. Cero desactiva todas las acciones y borra el estado de ejecución. Varios bits permiten varios candidatos, pero la función devuelve un único bit seleccionado por llamada, no una máscara de pulsos combinados.
Valor devuelto
Cero si no corresponde emitir un pulso; en caso contrario, uno de estos valores: 1, 2, 4, 8, 16, 32, 64, 128. El resultado distinto de cero es un bit de botón, no un booleano normalizado ni un recuento de repeticiones.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Espera inicial de 0 a 255 llamadas a la función de repetición. Con un valor positivo N, la primera repetición mantenida ocurre exactamente N llamadas después del pulso inicial. Con 0 se omite el caso de igualdad del retardo inicial y el intervalo determina cuándo llega el primer pulso mantenido. No se mide en milisegundos.
Intervalo de las repeticiones posteriores, de 0 a 255 llamadas. Un N positivo genera un pulso cada N actualizaciones válidas de mantenimiento tras la primera repetición. Cero genera uno en cada actualización válida después de la espera inicial; en esa fase, tanto 0 como 1 dan un pulso por actualización. Si espera e intervalo son 0, cada actualización mantenida emite un pulso.
Llama a la función una vez por ciclo de entrada previsto. Cada llamada modifica el estado y no espera a VBlank. Adoptar un botón mantenido sin un bit de nueva pulsación devuelve 0 en esa llamada e inicia una nueva espera. Estas funciones eligen una acción activa por estado; input_repeat mantiene temporizadores independientes para los ocho botones.
Ejemplo de uso
result=__padrep_mask(alias_state,keys,trigger,48);
if (result!=0) alias_pulses++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11249
{
if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");
AsmOperand pm_done = MakeUniqueLabel("padrepM_done");
AsmOperand pm_ret0 = MakeUniqueLabel("padrepM_ret0");
AsmOperand pm_no_trig = MakeUniqueLabel("padrepM_notrig");
AsmOperand pm_reset_all = MakeUniqueLabel("padrepM_reset");
AsmOperand pm_dir0 = MakeUniqueLabel("padrepM_dir0");
AsmOperand pm_have_dir = MakeUniqueLabel("padrepM_havedir");
AsmOperand pm_first_rep = MakeUniqueLabel("padrepM_firstrep");
AsmOperand pm_ret_dir = MakeUniqueLabel("padrepM_retdir");
AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");
// Evaluate args safely:
// HL = state*, B = keys, C = trigger, E = mask
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
EmitAsm("POP_HL");
// trigMasked = trigger & mask -> D
EmitAsm("LD_A_C");
EmitAsm("AND_E");
EmitAsm("LD_D_A");
EmitAsm("OR_A");
EmitAsm("JP_Z", pm_no_trig);
// select = lowbit(trigMasked) = x & -x
EmitAsm("LD_A_D");
EmitAsm("CPL");
EmitAsm("INC_A");
EmitAsm("AND_D");
EmitAsm("LD_C_A"); // save selected in C
// dirMask=selected, dasCnt=0, arrCnt=0
EmitAsm("LD_A_C");
EmitAsm("LD_HL_A"); // [0]=selected
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); Emi__padrep_reset
Cancela la acción activa y pone a cero ambos contadores, conservando la espera y el intervalo configurados. Úsala al cerrar un menú o cambiar el destino de la entrada para no arrastrar la temporización de una pulsación anterior.
Sintaxis
void __padrep_reset(u8* state);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
statePuntero a cinco bytes consecutivos de RAM escribible, por ejemplo
u8 state[5]. El formato es[0] bit del botón activo,[1] espera inicial,[2] intervalo,[3] contador de espera transcurrida,[4] contador de repetición transcurrida. Reserva un estado para cada repetición independiente e inicialízalo antes de usarlo. No se admite un puntero nulo, un área menor de cinco bytes ni compartir el estado entre las variantes direccionales y la general.
Valor devuelto
Ninguno (void).
Ejemplo de uso
__padrep_reset(lr);
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa entrega lecturas de botones predefinidas a cuatro estados independientes para mostrar cuándo se repite una acción. Con espera 3 e intervalo 2, ocho llamadas numeradas del 0 al 7 generan pulsos en 0, 3, 5 y 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES y ALIAS PULSES muestran 004. Las variantes generales reciben A y B juntos y seleccionan A (16). También se comprueban la inicialización, el reinicio, la prioridad de direcciones, la liberación, la adopción de una dirección ya mantenida, los tiempos cero y un argumento con una expresión. CHECKS muestra 057 y FAILED CHECKS debe mostrar 000. Es un ejemplo determinista del procesamiento de estado; no espera a que pulses botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10840
{
if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");
CompileIntoHL(args[0]);
EmitAsm("XOR_A");
EmitAsm("LD_HL_A"); // [0]=0 (dirMask)
// [3]=0, [4]=0
EmitAsm("PUSH_HL");
EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
EmitAsm("LD_HL_A");
EmitAsm("INC_HL"); // -> [4]
EmitAsm("LD_HL_A");
EmitAsm("POP_HL");
return;
}
// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new trigger, simultaneous Left/Right holds suppress movement without resetting counters.
// A single held direction is adopted without immediate movement when no direction is active.
// Subsequent updates use the five-byte state's initial delay and repeat interval; ARR=0 repeats each update.
if (funcName == "__padrep_lr")
{
if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");
AsmOperand pr_done = MakeUniqueLabel("padrep_done");
AsmOperand pr_ret0 = MakeUniqueLabel("padrep_ret0");
AsmOperand pr_check_left = MakeUniqueLabel("padrep_chkL");
AsmOperand pr_no_trig = MakeUniqueLabel("padrep_notrig");
AsmOperand pr_reset_all = MakeUniqueLabel("padrep_reset");
AsmOperand pr_dir0 = MakeUniqueLabel("padrep_dir0");
AsmOperand pr_have_dir = MakeUniqueLabel("padrep_havedir");
AsmOperand pr_first_rep = MakeUniqueLabel("padrep_firstrep");
AsmOperand pr_ret_dir = MakeUniqueLabel("padrep__readpad
Lee la cruceta y los botones de acción desde JOYP y los combina en la máscara común de 8 bits. Selecciona cada grupo, realiza dos lecturas para estabilizarlo y restaura los bits de selección a 0x30.
Sintaxis
u8 __readpad();Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Valor devuelto
Una máscara de 8 bits en formato común BTN_*. Puede contener varios botones a la vez; 0 significa ninguno.
Condiciones y límites
Máscaras comunes: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit a 1 indica que el botón está mantenido. input_* usa este formato en GB y FC; las funciones FC de lectura directa emplean otro orden.
Esta llamada obtiene una nueva lectura del hardware. No actualiza el estado guardado de input_* ni nes_pad1_*, ni genera eventos de pulsación, liberación o repetición. Guarda la lectura y compárala con tu máscara anterior cuando necesites detectar cambios.
Ejemplo de uso
all_keys=__readpad(); // A + Right: 0x11, decimal 17.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa compara los tres formatos de máscara y el resultado que combina el estado actual con las nuevas pulsaciones. Mantén A y derecha a la vez. ALL KEYS vale 17; DIRECTIONS y BUTTONS LOW valen 1 cada uno. Con una máscara anterior de 0, EX CURRENT y EX NEW PRESS valen 17. Al pasar la máscara actual a la siguiente lectura, HELD PRESS vale 0. FAILED CHECKS debe ser 0. Los valores quedan en pantalla aunque sueltes los botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cacheImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10669
{
if (args.Length != 0) Error(expr, "__readpad requires 0 arguments");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
// Directions (P1=0x20)
EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1); // dummy reads for settle
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
EmitAsm("LD_C_A"); // dpad in C
// Buttons (P1=0x10)
EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
// shift into upper nibble (<<4)
EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA");
EmitAsm("OR_C");
// Restore P1 selection (0x30) without losing return value
EmitAsm("LD_C_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LD_A_C");
return;
}
// 2.17 __readpaddir() -> u8 in A (bits0..3 only)
// Returns dpad nibble (Right,Left,Up,Down) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpaddir")
{
if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm__readpadbtn
Lee solo los botones de acción de JOYP en el nibble bajo: A=1, B=2, Select=4, Start=8. No son los valores BTN_A a BTN_START, que ocupan el nibble alto. Restaura la selección JOYP a 0x30.
Sintaxis
u8 __readpadbtn();Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Valor devuelto
Máscara de 0 a 15 en el orden indicado. Puede tener varios bits activos; comprueba un botón con result & bit, no comparando todo el resultado con 1.
Condiciones y límites
Esta llamada obtiene una nueva lectura del hardware. No actualiza el estado guardado de input_* ni nes_pad1_*, ni genera eventos de pulsación, liberación o repetición. Guarda la lectura y compárala con tu máscara anterior cuando necesites detectar cambios.
Ejemplo de uso
buttons=__readpadbtn(); // A: 1, not BTN_A (16).Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa compara los tres formatos de máscara y el resultado que combina el estado actual con las nuevas pulsaciones. Mantén A y derecha a la vez. ALL KEYS vale 17; DIRECTIONS y BUTTONS LOW valen 1 cada uno. Con una máscara anterior de 0, EX CURRENT y EX NEW PRESS valen 17. Al pasar la máscara actual a la siguiente lectura, HELD PRESS vale 0. FAILED CHECKS debe ser 0. Los valores quedan en pantalla aunque sueltes los botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cacheImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10729
{
if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
EmitAsm("LD_C_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LD_A_C");
return;
}
// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadex")
{
if (args.Length != 1) Error(expr, "__readpadex requires 1 argument (prev_keys)");
// prev -> B
CompileIntoA(args[0]);
EmitAsm("LD_B_A");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
// Directions (P1=0x20)
EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
EmitAsm("LD_C_A"); // dpad in C
// Buttons (P1=0x10)
EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1)__readpaddir
Lee solo la cruceta de JOYP. El resultado ocupa los bits 0–3: derecha=1, izquierda=2, arriba=4, abajo=8; los bits 4–7 son cero. Ignora los botones de acción y restaura la selección JOYP a 0x30.
Sintaxis
u8 __readpaddir();Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Valor devuelto
Máscara de 0 a 15 en el orden indicado. Puede tener varios bits activos; comprueba un botón con result & bit, no comparando todo el resultado con 1.
Condiciones y límites
Esta llamada obtiene una nueva lectura del hardware. No actualiza el estado guardado de input_* ni nes_pad1_*, ni genera eventos de pulsación, liberación o repetición. Guarda la lectura y compárala con tu máscara anterior cuando necesites detectar cambios.
Ejemplo de uso
directions=__readpaddir(); // Right: 1. The held A button is not included.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este programa compara los tres formatos de máscara y el resultado que combina el estado actual con las nuevas pulsaciones. Mantén A y derecha a la vez. ALL KEYS vale 17; DIRECTIONS y BUTTONS LOW valen 1 cada uno. Con una máscara anterior de 0, EX CURRENT y EX NEW PRESS valen 17. Al pasar la máscara actual a la siguiente lectura, HELD PRESS vale 0. FAILED CHECKS debe ser 0. Los valores quedan en pantalla aunque sueltes los botones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cacheImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10706
{
if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
EmitAsm("LD_C_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LD_A_C");
return;
}
// 2.18 __readpadbtn() -> u8 in A (bits0..3 only)
// Returns buttons nibble (A,B,Select,Start) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadbtn")
{
if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");
AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP
EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("LDH_A_MEM", p1);
EmitAsm("CPL");
EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
EmitAsm("LD_C_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", p1);
EmitAsm("LD_A_C");
return;
}
// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a c__sdot2_q1_7
Calcula un producto escalar con signo de dos términos.
Sintaxis
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);Parámetros
a0Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a1Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
b0Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b1Patrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es 2*trunc(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[20]=__sdot2_q1_7(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[21]=__sdot2_q1_7(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[22]=__sdot2_q1_7(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[23]=__sdot2_q1_7(v0,v1,v2,v3);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[20]=1018, result[21]=1018, result[22]=64518, result[23]=64518. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11732
kitaqgb/kitaqgb/CodeGenerator.cs:11732
if (funcName == "__sdot2_q1_7")
{
if (args.Length != 4) Error(expr, "__sdot2_q1_7 requires 4 arguments");
LastCallReturnsHL = true;
// x*ax
if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: true, out _))
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_q1_7_x");
EmitAsm("ADD_HL_HL");
}
EmitAsm("PUSH_HL");
// y*ay
if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: true, out _))
{
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[3]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_q1_7_y");
EmitAsm("ADD_HL_HL");
}
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__sdot2_q8_8
Calcula un producto escalar con signo de dos términos.
Sintaxis
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Parámetros
a0Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a1Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
b0Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b1Patrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es trunc(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[30]=__sdot2_q8_8(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[31]=__sdot2_q8_8(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[32]=__sdot2_q8_8(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[33]=__sdot2_q8_8(v0,v1,v2,v3);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[30]=509, result[31]=509, result[32]=65027, result[33]=65027. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11654
kitaqgb/kitaqgb/CodeGenerator.cs:11654
if (funcName == "__sdot2_q8_8")
{
if (args.Length != 4) Error(expr, "__sdot2_q8_8 requires 4 arguments");
LastCallReturnsHL = true;
// Fast paths when coefficients are compile-time constants.
// Special-case: 0 / power-of-two coefficients (s8 two's complement) => arithmetic shift instead of mul.
if (TryGetS8Const(args[2], out int sax0) && (sax0 == 0) && TryGetS8Const(args[3], out int say0) && (say0 == 0))
{
CompileDiscard(args[0]);
CompileDiscard(args[1]);
EmitAsm("XOR_A");
EmitAsm("LD_H_A");
EmitAsm("LD_L_A");
return;
}
if (TryGetS8Const(args[2], out int saxOnly) && (saxOnly == 0))
{
CompileDiscard(args[0]);
// only y*ay
if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
{
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[3]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_y");
}
return;
}
if (TryGetS8Const(args[3], out int sayOnly) && (sayOnly == 0))
{
// only x*ax
if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_x");
}
// Preserve the scaled result while evaluating the zero-product input.
EmitAsm("PUSH_HL");
CompileDiscard(args[1]);
EmitAsm("POP_HL");
return;
}
// x*ax
if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_x");
}
EmitAsm("PUSH_HL");
// y*ay
if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
{
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[3]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitSMul16x8Shr8_HL("__sdot2_y");
}
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__sdot3_q1_7
Calcula un producto escalar con signo de tres términos.
Sintaxis
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parámetros
a0Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a1Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a2Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
b0Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b1Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b2Patrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es 2*trunc(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[16]=__sdot3_q1_7(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[17]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[18]=__sdot3_q1_7(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[19]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[16]=1042, result[17]=1042, result[18]=64494, result[19]=64494. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11790
kitaqgb/kitaqgb/CodeGenerator.cs:11790
if (funcName == "__sdot3_q1_7")
{
if (args.Length != 6) Error(expr, "__sdot3_q1_7 requires 6 arguments");
LastCallReturnsHL = true;
// x*ax
EmitDotTermToHL(args[0], args[3], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_x");
EmitAsm("PUSH_HL");
// y*ay
EmitDotTermToHL(args[1], args[4], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_y");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
EmitAsm("PUSH_HL");
// z*az
EmitDotTermToHL(args[2], args[5], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_z");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__sdot3_q8_8
Calcula un producto escalar con signo de tres términos.
Sintaxis
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parámetros
a0Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a1Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
a2Patrón de bits de 16 bits con signo que representa de −32768 a 32767.
b0Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b1Patrón de bits de un byte con signo, coeficiente de −128 a 127.
b2Patrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es trunc(a*b/256). Suma los términos y devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[8]=__sdot3_q8_8(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[9]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[10]=__sdot3_q8_8(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[11]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[8]=521, result[9]=521, result[10]=65015, result[11]=65015. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11767
kitaqgb/kitaqgb/CodeGenerator.cs:11767
if (funcName == "__sdot3_q8_8")
{
if (args.Length != 6) Error(expr, "__sdot3_q8_8 requires 6 arguments");
LastCallReturnsHL = true;
// x*ax
EmitDotTermToHL(args[0], args[3], signed: true, q1_7: false, labelPrefix: "__sdot3_x");
EmitAsm("PUSH_HL");
// y*ay
EmitDotTermToHL(args[1], args[4], signed: true, q1_7: false, labelPrefix: "__sdot3_y");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
EmitAsm("PUSH_HL");
// z*az
EmitDotTermToHL(args[2], args[5], signed: true, q1_7: false, labelPrefix: "__sdot3_z");
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
return;
}__settile_fast
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El destino siempre es 0x9800 + 32*y + x, independientemente de los mapas de fondo y ventana elegidos en LCDC.
No espera el acceso a VRAM. Úsala con la LCD apagada o cuando el código que llama garantice un intervalo de escritura suficiente para toda la transferencia.
Sintaxis
__settile_fast(x, y, tile);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_fast
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void __settile_fast(u8 x, u8 y, u8 tile);
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_fast(2, 3, 'A');
if (tile_example_read(0x9862, 0) != 'A') { failures++; }
if (tile_example_read(0x9C62, 0) != 0) { failures++; }
tile_example_finish(failures, 0);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:9321
{
if (args.Length != 3) Error(expr, "__settile_unsafe requires 3 arguments");
// Evaluate args left-to-right (matches normal call semantics)
// Preserve x,y while evaluating tile.
CompileIntoA(args[0]);
EmitAsm("LD_L_A");
EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("PUSH_HL"); // x
CompileIntoA(args[1]);
EmitAsm("LD_L_A");
EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("PUSH_HL"); // y
CompileIntoA(args[2]);
EmitAsm("LD_C_A"); // tile
// Restore y -> A, x -> B
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitAsm("LD_D_A"); // y temp
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitAsm("LD_B_A"); // x
EmitAsm("LD_A_D"); // y
UsedSetTileFastCore = true;
EmitAsm("CALL", new AsmOperand("__settile_fast_core", AddressMode.Absolute));
return;
}
// 2.6b __settile_bulk(dest_u16, src_u16, count_u8)
// v9: bulk tile-map copy (LCD ON safe with wait+DI/EI inside core).
if (funcName == "__settile_bulk")
{
if (args.Length != 3) Error(expr, "__settile_bulk requires 3 arguments");
Expr bulkCountExpr = FoldConstants(args[2]);
// dest__settilebg16_buf
Escribe una celda de 16×16 píxeles en un búfer de mapa en RAM: arriba, tile,tile+1; abajo, tile+2,tile+3. Los valores vuelven a cero al llegar a 256. No transfiere nada a VRAM.
Sintaxis
__settilebg16_buf(buf, x, y, tile);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
bufBúfer RAM con 32 bytes por fila de tiles. Un mapa completo de 32×32 necesita 1024 bytes. La esquina superior izquierda está en
64*y + 2*x; los cuatro desplazamientos son0,1,32,33desde allí.x, yCoordenadas en celdas de 16×16 píxeles, de 0 a 15 en un búfer completo de 32×32 tiles. No son las coordenadas de tiles de 8×8 de las escrituras individuales.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg16_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__memset(example_tiles, 0, 1024);
__memset(example_attrs, 0, 1024);
__settilebg16_buf(example_tiles, 1, 1, 'A');
// The buffer operation must not write to VRAM yet.
if (tile_example_read(0x9C42, 0) != 0) { failures++; }
// Transfer the prepared cell so its four characters can be seen.
__settilebg16_flush(example_tiles, 1, 2, 2);
if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
if (example_tiles[66] != 'A') { failures++; }
if (example_tiles[99] != 'D') { failures++; }
if (tile_example_read(0x9C44, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
La celda muestra AB arriba y CD abajo, desde el tile (2,2): es una celda de 16×16 píxeles en (1,1). Los ejemplos de escritura en búfer comprueban que preparar RAM no modifica VRAM antes de transferir. Los ejemplos de transferencia preparan explícitamente los bytes RAM primero.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=2), equivalentes al píxel (16,16). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
AB
CD

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10564
{
if (args.Length != 4) Error(expr, "__settilebg16_buf requires 4 arguments");
EmitTile16BufferedWriteIntrinsic(args[0], args[1], args[2], args[3], incrementQuad: true, labelPrefix: "settilebg16buf");
return;
}
// Update tile and attribute RAM buffers separately, repeating the attribute across the cell.
if (funcName == "__settilebg16cgb_buf")
{
if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
return;
}
// Flush both tile rows belonging to the requested buffered cell row.
if (funcName == "__settilebg16_flush")
{
if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
return;
}
// Flush the tile buffer first and copy attributes only when CGB hardware is available.
if (funcName == "__settilebg16cgb_flush")
{
if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
EmitTile16FlushRowsIn__settilebg16_flush
Transfiere dos filas contiguas de tiles del búfer RAM al mapa BG seleccionado por LCDC. El origen está en 64*y + x; copia count bytes de esa fila y de la situada 32 bytes después.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
__settilebg16_flush(buf, y, x, count);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
bufBúfer de mapa legible con un paso de 32 bytes por fila. Un mapa completo ocupa 1024 bytes. La variante de dos búferes recibe números de tile y atributos en arrays separados con la misma disposición, sin intercalarlos.
y, x, countEl orden es
y,x,count.yes una fila de celdas de 16 píxeles (0–15);xycountse miden en tiles de 8 píxeles. Manténx+count <= 32. Para una celda en(2,3), usay=3,x=4,count=2.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg16_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Waits for writable VRAM when necessary. The LCD-on path uses
// interrupt masking and can enable interrupts afterward; it is not a promise to preserve
// the caller's interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__memset(example_tiles, 0, 1024);
__memset(example_attrs, 0, 1024);
example_tiles[66]='A'; example_tiles[67]='B';
example_tiles[98]='C'; example_tiles[99]='D';
__settilebg16_flush(example_tiles, 1, 2, 2);
if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
if (example_tiles[66] != 'A') { failures++; }
if (example_tiles[99] != 'D') { failures++; }
if (tile_example_read(0x9C44, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
La celda muestra AB arriba y CD abajo, desde el tile (2,2): es una celda de 16×16 píxeles en (1,1). Los ejemplos de escritura en búfer comprueban que preparar RAM no modifica VRAM antes de transferir. Los ejemplos de transferencia preparan explícitamente los bytes RAM primero.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=2), equivalentes al píxel (16,16). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
AB
CD

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10581
{
if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
return;
}
// Flush the tile buffer first and copy attributes only when CGB hardware is available.
if (funcName == "__settilebg16cgb_flush")
{
if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
if (IsKnownCgbRuntimeTrue())
{
EmitSetVbkUnchecked(1);
EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
}
else
{
AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
EmitRuntimeCgbCheckCall();
EmitAsm("OR_A");
// Two unrolled row copies can exceed the relative-branch range.
EmitAsm("JP_Z", skip);
EmitSetVbkUnchecked(1);
EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
EmitAsm("JR", done);__settilebg16cgb_buf
Escribe una celda de 16×16 píxeles en un búfer de mapa en RAM: arriba, tile,tile+1; abajo, tile+2,tile+3. Los valores vuelven a cero al llegar a 256. No transfiere nada a VRAM.
Escribe también el mismo byte attr en las cuatro celdas del búfer de atributos separado. Ambos búferes son RAM normal, por lo que la preparación también se ejecuta en DMG.
Sintaxis
__settilebg16cgb_buf(tilebuf, attrbuf, x, y, tile, attr);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
tilebuf, attrbufBúfer RAM con 32 bytes por fila de tiles. Un mapa completo de 32×32 necesita 1024 bytes. La esquina superior izquierda está en
64*y + 2*x; los cuatro desplazamientos son0,1,32,33desde allí.x, yCoordenadas en celdas de 16×16 píxeles, de 0 a 15 en un búfer completo de 32×32 tiles. No son las coordenadas de tiles de 8×8 de las escrituras individuales.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
attrByte de atributos CGB: bits 0–2, paleta 0–7; bit 3, banco de imagen; bits 5 y 6, inversión horizontal y vertical; bit 7, prioridad del fondo o ventana. Deja el bit 4 sin usar en cero.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg16cgb_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM. Also write the same `attr` byte to all four corresponding
// cells in the separate attribute buffer. Both buffers are ordinary RAM, so this
// preparation also runs on DMG.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__memset(example_tiles, 0, 1024);
__memset(example_attrs, 0, 1024);
__settilebg16cgb_buf(example_tiles, example_attrs, 1, 1, 'A', 2);
// The buffer operation must not write to VRAM yet.
if (tile_example_read(0x9C42, 0) != 0) { failures++; }
// Transfer the prepared cell so its four characters can be seen.
__settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
if (example_tiles[66] != 'A') { failures++; }
if (example_tiles[99] != 'D') { failures++; }
if (tile_example_read(0x9C44, 0) != 0) { failures++; }
if (example_attrs[66] != 2) { failures++; }
if (example_attrs[99] != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
La celda muestra AB arriba y CD abajo, desde el tile (2,2): es una celda de 16×16 píxeles en (1,1). Los ejemplos de escritura en búfer comprueban que preparar RAM no modifica VRAM antes de transferir. Los ejemplos de transferencia preparan explícitamente los bytes RAM primero.
Los cuatro caracteres son azules en CGB porque sus atributos son 2. En DMG son negros.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=2), equivalentes al píxel (16,16). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
AB
CD

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10572
{
if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
return;
}
// Flush both tile rows belonging to the requested buffered cell row.
if (funcName == "__settilebg16_flush")
{
if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
return;
}
// Flush the tile buffer first and copy attributes only when CGB hardware is available.
if (funcName == "__settilebg16cgb_flush")
{
if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
if (IsKnownCgbRuntimeTrue())
{
EmitSetVbkUnchecked(1);
EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
}
else
{
AsmOperand ski__settilebg16cgb_flush
Transfiere dos filas contiguas de tiles del búfer RAM al mapa BG seleccionado por LCDC. El origen está en 64*y + x; copia count bytes de esa fila y de la situada 32 bytes después.
Escribe número de tile y atributo CGB. En DMG solo escribe el número. En CGB, entra con el banco 0 seleccionado; los atributos usan el banco 1 y al terminar queda VBK=0.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
__settilebg16cgb_flush(tilebuf, attrbuf, y, x, count);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
tilebuf, attrbufBúfer de mapa legible con un paso de 32 bytes por fila. Un mapa completo ocupa 1024 bytes. La variante de dos búferes recibe números de tile y atributos en arrays separados con la misma disposición, sin intercalarlos.
y, x, countEl orden es
y,x,count.yes una fila de celdas de 16 píxeles (0–15);xycountse miden en tiles de 8 píxeles. Manténx+count <= 32. Para una celda en(2,3), usay=3,x=4,count=2.
Valor devuelto
Ninguno (void).
Condiciones y límites
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilebg16cgb_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Write a tile number and its CGB attribute. On DMG, only the tile-
// number write is performed. On CGB, enter with VRAM bank 0 selected; attributes use
// bank 1 and the call finishes with `VBK=0`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);
void main() {
u8 failures;
failures = 0;
tile_example_begin();
__memset(example_tiles, 0, 1024);
__memset(example_attrs, 0, 1024);
example_tiles[66]='A'; example_tiles[67]='B';
example_tiles[98]='C'; example_tiles[99]='D';
example_attrs[66]=2; example_attrs[67]=2;
example_attrs[98]=2; example_attrs[99]=2;
__settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
if (example_tiles[66] != 'A') { failures++; }
if (example_tiles[99] != 'D') { failures++; }
if (tile_example_read(0x9C44, 0) != 0) { failures++; }
if (example_attrs[66] != 2) { failures++; }
if (example_attrs[99] != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
La celda muestra AB arriba y CD abajo, desde el tile (2,2): es una celda de 16×16 píxeles en (1,1). Los ejemplos de escritura en búfer comprueban que preparar RAM no modifica VRAM antes de transferir. Los ejemplos de transferencia preparan explícitamente los bytes RAM primero.
Los cuatro caracteres son azules en CGB porque sus atributos son 2. En DMG son negros.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=2), equivalentes al píxel (16,16). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
AB
CD

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:10589
{
if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
if (IsKnownCgbRuntimeTrue())
{
EmitSetVbkUnchecked(1);
EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
}
else
{
AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
EmitRuntimeCgbCheckCall();
EmitAsm("OR_A");
// Two unrolled row copies can exceed the relative-branch range.
EmitAsm("JP_Z", skip);
EmitSetVbkUnchecked(1);
EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
EmitAsm("JR", done);
EmitLabel(skip);
EmitLabel(done);
}
return;
}
// 2.14 __getbgmapbase() -> u16 in HL
// Returns the BG tilemap base address selected by LCDC bit3:
// bit3=0 => 0x9800, bit3=1 => 0x9C00
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__getbgmapbase")
{
if (args.Length != 0) Error(expr, "__getb__smac16
Calcula una multiplicación-acumulación con signo.
Sintaxis
u16 __smac16(u16 acc, u16 a, u8 b);Parámetros
accAcumulador de 16 bits que se va a sumar.
aPatrón de bits de 16 bits con signo que representa de −32768 a 32767.
bPatrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es trunc(a*b/256) y se suma a acc; devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[26]=__smac16(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[27]=__smac16(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[28]=__smac16(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[29]=__smac16(v0,v1,v2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[26]=92, result[27]=92, result[28]=65372, result[29]=65372. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11516
kitaqgb/kitaqgb/CodeGenerator.cs:11516
if (funcName == "__smac16")
{
if (args.Length != 3) Error(expr, "__smac16 requires 3 arguments");
// mul = (a*b)>>8 (signed)
EmitDotTermToHL(args[1], args[2], signed: true, q1_7: false, labelPrefix: "__smac16_mul");
// acc + mul
EmitAsm("PUSH_HL");
CompileIntoHL(args[0]);
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
LastCallReturnsHL = true;
return;
}__smac16_q1_7
Calcula una multiplicación-acumulación con signo.
Sintaxis
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);Parámetros
accAcumulador de 16 bits que se va a sumar.
aPatrón de bits de 16 bits con signo que representa de −32768 a 32767.
bPatrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es 2*trunc(a*b/256) y se suma a acc; devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[0]=__smac16_q1_7(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[1]=__smac16_q1_7(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[2]=__smac16_q1_7(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[3]=__smac16_q1_7(v0,v1,v2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[0]=220, result[1]=220, result[2]=65244, result[3]=65244. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11533
kitaqgb/kitaqgb/CodeGenerator.cs:11533
if (funcName == "__smac16_q1_7")
{
if (args.Length != 3) Error(expr, "__smac16_q1_7 requires 3 arguments");
// mul = (a*b)>>7 (approx via >>8 then *2)
EmitDotTermToHL(args[1], args[2], signed: true, q1_7: true, labelPrefix: "__smac16_q1_7_mul");
// acc + mul
EmitAsm("PUSH_HL");
CompileIntoHL(args[0]);
EmitAsm("POP_DE");
EmitAsm("ADD_HL_DE");
LastCallReturnsHL = true;
return;
}__smul16x8_q1_7
Calcula una multiplicación con signo.
Sintaxis
u16 __smul16x8_q1_7(u16 a, u8 b);Parámetros
aPatrón de bits de 16 bits con signo que representa de −32768 a 32767.
bPatrón de bits de un byte con signo, coeficiente de −128 a 127.
Valor devuelto
Cada término es 2*trunc(a*b/256); devuelve los 16 bits bajos.
Condiciones y límites
En GB, los productos se dividen entre 256; los resultados con signo se truncan hacia cero. Las variantes Q1.7 duplican ese resultado truncado y pueden diferir en una unidad de la división exacta entre 128. Cada término se redondea por separado; la suma final se desborda a 16 bits.
Ejemplo de uso
// Compare literal coefficients with values read from RAM.
result[12]=__smul16x8_q1_7(-513,-64);
v0=-513;v1=-64;
result[13]=__smul16x8_q1_7(v0,v1);
// Reverse coefficient signs and verify negative products.
result[14]=__smul16x8_q1_7(-513,64);
v0=-513;v1=64;
result[15]=__smul16x8_q1_7(v0,v1);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Compara entradas literales con valores guardados en RAM. Los resultados representativos se muestran como valores de 16 bits sin signo; los negativos usan patrones en complemento a dos. GB ajusta la escala de los productos de punto fijo, mientras FC devuelve productos enteros, por lo que entradas idénticas pueden dar resultados distintos.
El fragmento guarda estos valores de 16 bits: result[12]=256, result[13]=256, result[14]=65280, result[15]=65280. Cada cálculo con entrada literal coincide con su equivalente con entrada en RAM. La pantalla muestra valores representativos; el array result guarda todos los resultados.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:11490
kitaqgb/kitaqgb/CodeGenerator.cs:11490
if (funcName == "__smul16x8_q1_7")
{
if (args.Length != 2) Error(expr, "__smul16x8_q1_7 requires 2 arguments");
EmitDotTermToHL(args[0], args[1], signed: true, q1_7: true, labelPrefix: "__smul16x8_q1_7");
LastCallReturnsHL = true;
return;
}__sram_read8
Lee un byte de SRAM en una dirección de CPU.
Sintaxis
u8 __sram_read8(u16 address);Parámetros
addressDirección de CPU en $A000..$BFFF.
Valor devuelto
Devuelve el byte sin comprobar la presencia de RAM ni la validez de los datos.
Condiciones y límites
Escribe 0x0A en $0000 para activar la RAM antes del acceso y después escribe cero para desactivarla. No selecciona el banco ni valida la dirección; el código que llama configura el banco RAM. No gestiona cabeceras de guardado ni sumas de comprobación.
Ejemplo de uso
*((u8*)0x4000)=0;result[12]=__sram_read8(0xA006);result[19]=*((u8*)0xA006);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Guarda tres bytes, 7/42/255, y prueba una longitud distinta, una ranura no válida y una suma de comprobación dañada. Una carga correcta da 42 en el byte central; una lectura fallida conserva 77 en el destino; validar después de borrar devuelve 0.


Comando de compilación
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=8kImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:8825
kitaqgb/kitaqgb/CodeGenerator.cs:8825
if (funcName == "__sram_read8")
{
if (args.Length != 1) Error(expr, "__sram_read8(addr) expects 1 argument");
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
EmitAsm("POP_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_B_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
EmitAsm("LD_A_B");
return;
}__sram_write8
Escribe un byte de SRAM en una dirección de CPU.
Sintaxis
void __sram_write8(u16 address, u8 value);Parámetros
addressDirección de CPU en $A000..$BFFF.
valueValor de byte de 0 a 255.
Valor devuelto
No devuelve ningún valor.
Condiciones y límites
Escribe 0x0A en $0000 para activar la RAM antes del acceso y después escribe cero para desactivarla. No selecciona el banco ni valida la dirección; el código que llama configura el banco RAM. No gestiona cabeceras de guardado ni sumas de comprobación.
Ejemplo de uso
__sram_write8(0xA006,8);result[20]=*((u8*)0xA006);result[13]=save_exists(0);readback[0]=77;result[14]=save_read(0,readback,3);result[15]=readback[0];Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Guarda tres bytes, 7/42/255, y prueba una longitud distinta, una ranura no válida y una suma de comprobación dañada. Una carga correcta da 42 en el byte central; una lectura fallida conserva 77 en el destino; validar después de borrar devuelve 0.


Comando de compilación
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=8kImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:8846
kitaqgb/kitaqgb/CodeGenerator.cs:8846
if (funcName == "__sram_write8")
{
if (args.Length != 2) Error(expr, "__sram_write8(addr, value) expects 2 arguments");
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("LD_C_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
EmitAsm("POP_HL");
EmitAsm("LD_A_C");
EmitAsm("LD_HL_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
return;
}__svbk_get
Lee el banco WRAM efectivo asignado a $D000–$DFFF. Lee bits 0–2 de SVBK y normaliza el selector 0 al banco 1. Devuelve un número de banco, no capacidad RAM, dirección ni puntero.
Sintaxis
u8 __svbk_get();Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Valor devuelto
Banco efectivo actual como u8, 1–7; el selector 0 se devuelve como 1.
Condiciones y límites
__svbk_get y __svbk_set requieren --cgb=cgb_only o #pragma rom_cgb cgb_only. Los destinos DMG y dual rechazan las llamadas con KQ2103, aunque una condición en ejecución las omita. Usa el setter protegido directo solo si su contrato diferente encaja en tu programa.
Cambiar de banco afecta a todos los accesos en $D000–$DFFF; la WRAM fija de $C000–$CFFF no cambia. Mantén pila y estado necesario fuera de esa ventana. El ejemplo usa --stack-bank=fixed y variables ubicadas explícitamente en WRAM fija. Coordina interrupciones y DMA bancado; estas API no crean una sección crítica.
Ejemplo de uso
if (__svbk_get()!=1) failures++; // Normalize raw selector zero to effective bank 1.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa solo CGB guarda 1 en $D100 del banco WRAM 2 y 2 en la misma dirección del banco 3; lee ambos y restaura el banco 1. Esos valores eligen un cuadrado rojo de 8 × 8 en (16,24) y un contorno azul de 8 × 8, de un píxel, en (16,48). FAILED CHECKS 000 verifica normalización, separación y retorno del banco previo con constantes, variables, expresiones y resultados de función. Seis compilaciones inválidas verifican KQ2103 en DMG/dual y KQ2102 para bancos constantes 0 y 8. Ejecución en KOKURA; hardware real y cambios concurrentes por interrupciones no probados.

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7387
{
if (args.Length != 0) Error(expr, "__svbk_get() expects 0 arguments");
if (!Program.IsCgbOnlyTargetRequested())
Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_get() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");
EmitReadNormalizedSvbkIntoA();
return;
}
// Select a WRAM bank and return the prior normalized bank; diagnose out-of-range constant requests.
if (funcName == "__svbk_set")
{
if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
if (!Program.IsCgbOnlyTargetRequested())
Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");
Expr bankExpr = FoldConstants(args[0]);
if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);
EmitReadNormalizedSvbkIntoA();
EmitAsm("LD_B_A");
CompileIntoA(bankExpr);
EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
EmitAsm("LD_A_B");
return;
}
// __cgb_safe_set_* (safe CGB-only register write wrappers)
if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
{__svbk_set
Cambia el banco WRAM asignado a $D000–$DFFF y devuelve el banco efectivo anterior. Guarda el resultado para restaurar el banco del llamador. No copia ni borra memoria y no restaura el banco automáticamente.
Sintaxis
u8 __svbk_set(u8 bank);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
bankBanco solicitado como
u8, 1–7. Una constante fuera del rango genera KQ2102. El intrínseco no comprueba valores dinámicos; valídalos antes de llamar, sin depender de la máscara del registro.
Valor devuelto
Banco efectivo anterior al cambio como u8, 1–7. Es el banco antiguo, no el recién seleccionado.
Condiciones y límites
__svbk_get y __svbk_set requieren --cgb=cgb_only o #pragma rom_cgb cgb_only. Los destinos DMG y dual rechazan las llamadas con KQ2103, aunque una condición en ejecución las omita. Usa el setter protegido directo solo si su contrato diferente encaja en tu programa.
Cambiar de banco afecta a todos los accesos en $D000–$DFFF; la WRAM fija de $C000–$CFFF no cambia. Mantén pila y estado necesario fuera de esa ventana. El ejemplo usa --stack-bank=fixed y variables ubicadas explícitamente en WRAM fija. Coordina interrupciones y DMA bancado; estas API no crean una sección crítica.
Ejemplo de uso
previous_bank=__svbk_set(requested_bank);
if(previous_bank!=1 || __svbk_get()!=2) failures++;
*((u8*)0xD100)=1; // This byte belongs to bank 2.
previous_bank=__svbk_set(next_bank()); // Dynamic argument evaluates to 3.
if(previous_bank!=2 || __svbk_get()!=3) failures++;
*((u8*)0xD100)=2; // Same CPU address, different physical RAM.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa solo CGB guarda 1 en $D100 del banco WRAM 2 y 2 en la misma dirección del banco 3; lee ambos y restaura el banco 1. Esos valores eligen un cuadrado rojo de 8 × 8 en (16,24) y un contorno azul de 8 × 8, de un píxel, en (16,48). FAILED CHECKS 000 verifica normalización, separación y retorno del banco previo con constantes, variables, expresiones y resultados de función. Seis compilaciones inválidas verifican KQ2103 en DMG/dual y KQ2102 para bancos constantes 0 y 8. Ejecución en KOKURA; hardware real y cambios concurrentes por interrupciones no probados.

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7397
{
if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
if (!Program.IsCgbOnlyTargetRequested())
Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");
Expr bankExpr = FoldConstants(args[0]);
if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);
EmitReadNormalizedSvbkIntoA();
EmitAsm("LD_B_A");
CompileIntoA(bankExpr);
EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
EmitAsm("LD_A_B");
return;
}
// __cgb_safe_set_* (safe CGB-only register write wrappers)
if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
{
if (args.Length != 1) Program.Error(funcName + "(value_u8) expects 1 argument");
if (IsKnownCgbRuntimeTrue())
{
CompileIntoA(args[0]);
if (cgbOffset == 0x4F)
EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", new AsmOperand(cgbOffset, AddressMode.HighMem));
CgbGuardedWriteCount++;
CgbGuardedRegisters.Add(cgbReg);
return;__vram_memcpy_unsafe
Copia inmediatamente len bytes de src a dst en orden creciente de direcciones. Vuelve al terminar; no encola el trabajo. Ambas direcciones avanzan un byte cada vez, sin saltos de fila del mapa ni conversión del formato gráfico.
Sintaxis
void __vram_memcpy_unsafe(u16 dst, const void* src, u16 len);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.srcPuntero a los bytes de origen, válido y mapeado durante toda la llamada. Los datos se leen inmediatamente, sin conservarlos para un flush posterior. No se cambia de banco automáticamente. Usa rangos sin solapamiento: esta copia hacia delante no ofrece la semántica de memmove.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Omite toda espera LCD/STAT y escribe directamente con la CPU. Mantén el LCD apagado o garantiza que toda la transferencia quepa en un intervalo escribible controlado. Entrar en VBlank no hace segura una transferencia de cualquier tamaño. Esta intrínseca no emite DI ni EI para la transferencia, ni recorta o comprueba límites.
Ejemplo de uso
// The LCD is off throughout these unchecked writes.
__vram_memcpy_unsafe(0x98C2,strip,computed_length());Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_memcpy_unsafe para escribir tres bytes del mapa en la casilla (2,6), píxel (16,48). Deben aparecer [1,2,3] de izquierda a derecha en un área de 24 × 8, con las casillas vecinas vacías. El programa completo sitúa cada copia o relleno probado en su propia fila.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:8104
{
if (args.Length != 3) Error(expr, "__vram_memcpy_unsafe(dst, src, len) expects 3 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
// Use the same count specialization without emitting LCD-mode waits.
Expr vramCopyUnsafeLen = FoldConstants(args[2]);
EmitLengthExprIntoBC(vramCopyUnsafeLen);
EmitAsm("POP_DE"); // src
EmitAsm("POP_HL"); // dst
if (vramCopyUnsafeLen.Match(Tag.Integer, out int vramCopyUnsafeLenConst) &&
TryEmitVramMemcpyConstCount(false, vramCopyUnsafeLenConst, "vramcpyu_small"))
return;
AsmOperand vcuLoop = MakeUniqueLabel("vramcpyu_loop");
AsmOperand vcuDone = MakeUniqueLabel("vramcpyu_done");
EmitLabel(vcuLoop);
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JR_Z", vcuDone);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_BC");
EmitAsm("JR", vcuLoop);
EmitLabel(vcuDone);
return;
}
// __vram_memset(dst_ptr, value_u8, len_u16)
// Safe VRAM fill: if LCD is on, each byte write is gated by STAT mode 0/1.
if (funcName == "__vram_memset")
{
if (args.Length != 3) Error(expr, "__vram_memset(dst, value, l__vram_memset_unsafe
Rellena inmediatamente len bytes consecutivos desde dst con el byte value. Repite un byte, no un patrón gráfico completo. En el mapa se repite un índice de patrón; en los datos gráficos se modifican los planos de bits de los píxeles.
Sintaxis
void __vram_memset_unsafe(u16 dst, u8 value, u16 len);Los nombres de parámetros explican la función intrínseca del compilador; no constituyen un prototipo C adicional.
Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.valueUn byte de 0 a 255 que se repite. Su efecto visible depende de si el destino contiene índices de tiles, planos de píxeles u otra estructura de bytes.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Omite toda espera LCD/STAT y escribe directamente con la CPU. Mantén el LCD apagado o garantiza que toda la transferencia quepa en un intervalo escribible controlado. Entrar en VBlank no hace segura una transferencia de cualquier tamaño. Esta intrínseca no emite DI ni EI para la transferencia, ni recorta o comprueba límites.
Ejemplo de uso
__vram_memset_unsafe(0x9942,1,computed_length()); // Safe here because the LCD is off.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_memset_unsafe para rellenar tres bytes del mapa desde la casilla (2,10), píxel (16,80), con el índice 1. Se espera una barra sólida de 24 × 8 con casillas vecinas vacías. El recuento procede de variables o del resultado de una función; los tres bytes deben conservar el valor 1.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:8196
{
if (args.Length != 3) Error(expr, "__vram_memset_unsafe(dst, value, len) expects 3 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
CompileIntoA(args[1]); EmitAsm("LD_D_A"); // value
// Specialize a constant unsafe fill or fall back to its unchecked byte loop.
Expr vramSetUnsafeLen = FoldConstants(args[2]);
EmitLengthExprIntoBC(vramSetUnsafeLen);
EmitAsm("POP_HL"); // dst
if (vramSetUnsafeLen.Match(Tag.Integer, out int vramSetUnsafeLenConst) &&
TryEmitVramMemsetConstCount(false, vramSetUnsafeLenConst, "vramsetu_small"))
return;
AsmOperand vsuLoop = MakeUniqueLabel("vramsetu_loop");
AsmOperand vsuDone = MakeUniqueLabel("vramsetu_done");
EmitLabel(vsuLoop);
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JR_Z", vsuDone);
EmitAsm("LD_A_D");
EmitAsm("LDI_HL_A");
EmitAsm("DEC_BC");
EmitAsm("JR", vsuLoop);
EmitLabel(vsuDone);
return;
}
// __far_memcpy(dst_ptr, bank_u8, src_ptr, len_u16)
// Copies from switchable ROM bank via a bank0 helper and restores original bank.
if (funcName == "__far_memcpy")
{
if (args.Length != 4) Error(expr, "__far_memcpy(dst, bank, src, len) expects 4 arguments");
CompileIntoHL(__wait_ly
Con el LCD activo, espera a que LY coincida con la línea objetivo.
Sintaxis
void __wait_ly(u8 ly);Parámetros
lyLínea de barrido de 0 a 153; los valores superiores a 153 no terminan mientras el LCD esté funcionando.
Valor devuelto
No devuelve ningún valor.
Condiciones y límites
Vuelve de inmediato si ya está en la línea objetivo; si esa línea ha pasado, espera al siguiente fotograma. También vuelve de inmediato con el LCD apagado.
Ejemplo de uso
M_LCDC=0x91;__wait_ly(80);result[9]=*((u8*)0xFF44);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/kitaqgb/CodeGenerator.cs:7483
kitaqgb/kitaqgb/CodeGenerator.cs:7483
if (funcName == "__wait_ly")
{
if (args.Length != 1) Error(expr, "__wait_ly(target) expects 1 argument");
CompileIntoA(args[0]);
EmitAsm("LD_B_A");
AsmOperand wlyLoop = MakeUniqueLabel("waitly_loop");
AsmOperand wlyDone = MakeUniqueLabel("waitly_done");
// If LCD is off, LY is not meaningful; return.
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
EmitAsm("JR_Z", wlyDone);
EmitLabel(wlyLoop);
EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
EmitAsm("CP_B");
EmitAsm("JR_NZ", wlyLoop);
EmitLabel(wlyDone);
return;
}physics2d.h — Movimiento y colisiones en 2D
Proporciona integración de cajas, contactos AABB y un solucionador opcional de respuesta a superficies. Los cuerpos usan centros y semiextensiones; active=0 excluye una posición del array e inv_mass_q8<=0 la hace estática. KQRect usa la esquina superior izquierda y ancho/alto. Los contactos entre cajas no usan el campo restitution del cuerpo; las respuestas a superficies usan los coeficientes de KQSurface2D.
Usa unidades enteras coherentes para posiciones, radios, semiextensiones, velocidades y gravedad. Cada paso equivale a un tick de simulación. Los ejemplos usan una unidad por píxel. Estas rutinas no convierten automáticamente las posiciones a Q8.8 ni las dibujan.
El mundo utiliza un array prestado. El código que llama reserva y posee la memoria de los cuerpos y debe mantenerla válida. Inicializar el mundo no inicializa los elementos del array.
__smul16x8
Multiplica un valor de 16 bits con signo por un coeficiente de 8 bits con signo, divide entre 256 y trunca hacia cero.
Sintaxis
s16 __smul16x8(s16 a, s8 b);Parámetros
aValor de 16 bits con signo al que se aplica el factor de escala.
bCoeficiente de un byte con signo, entre -128 y 127.
Valor devuelto
Devuelve el producto escalado como un entero de 16 bits con signo.
Condiciones y límites
El coeficiente va de -128 a 127, con una escala donde 256 representa 1,0. Los productos se truncan hacia cero para ambos signos: -257*64/256 devuelve -64.
Ejemplo de uso
result[13]=__smul16x8(-257,64);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
La izquierda muestra el estado inicial; la derecha, un tick después. Una caja roja de 16x16 con centro (24,24) y velocidad (4,16) llega a (28,36), con velocidad Y nula sobre el suelo azul. Los orígenes de los paneles están en las coordenadas de pantalla (8,48) y (88,48). En DMG, todas las marcas son negras. Las comprobaciones de RAM también cubren la inicialización, las funciones de ajuste, la integración, las consultas de rectángulos y puntos y el escalado con signo.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/physics2d.h:9
Multiply a signed word by a signed byte; divide by 256, truncating toward zero.
kitaqgb/kitaqgb/CodeGenerator.cs:11478
if (funcName == "__smul16x8")
{
if (args.Length != 2) Error(expr, "__smul16x8 requires 2 arguments");
EmitDotTermToHL(args[0], args[1], signed: true, q1_7: false, labelPrefix: "__smul16x8");
LastCallReturnsHL = true;
return;
}rpg.h — Números aleatorios, texto, mapas, guardado y servicios RPG/ADV/SLG
Las funciones auxiliares de texto absoluto dibujan directamente en el mapa del fondo. text_window abre un área con marco, text_clear_rect rellena una región y text_print_xy escribe bytes de tiles sin interpretar códigos de control. text_print_u8, text_print_u16 y text_print_s16 convierten números a decimal usando solo las celdas necesarias. Reserva y borra un campo completo al sustituir valores de distinta longitud.
Las funciones RNG declaradas en rpg.h se implementan en lib/rng.c. Comparten el generador GB del compilador: rng_seed lo reinicia, rng8/rng_next8 consumen un byte, rng16/rng_next16 consumen dos, y rand_range/rng_range, rng_chance y weighted_choice adaptan las extracciones a decisiones del juego. Compila rng.c con el programa al usar estas funciones. Para pruebas reproducibles, fija la semilla y el orden de llamadas; las selecciones basadas en módulo no garantizan probabilidades exactas.
La decodificación RLE usa pares de bytes count/value y un contador cero como terminador. rle_decode lee un puntero normal y escribe en RAM; rle_decode_far lee un puntero con banco ROM y escribe en RAM; __rle_decode_vram descomprime la entrada de un banco directamente en VRAM con escrituras adaptadas al LCD. Las funciones RAM están en lib/rle.c; la operación VRAM directa la genera el compilador. Las tres devuelven bytes de salida y requieren un flujo válido y espacio suficiente en destino.
El almacenamiento de estado declarado en rpg.h se implementa en lib/flags.c. Las tres funciones de banderas operan sobre 2048 bits booleanos independientes; quest_state y quest_set_state operan sobre 64 bytes independientes para etapas definidas por el juego. Compila flags.c, respeta los límites de ID y usa un sistema de guardado separado si el progreso debe conservarse al apagar.
El diálogo en ventana comparte un rectángulo activo, un cursor y una velocidad de caracteres. text_open inicializa el área, text_close la borra y text_set_speed fija esperas VBlank por carácter. text_print y text_print_far interpretan flujos; text_choice ejecuta un selector vertical bloqueante. Carga los tiles, ajusta el área y planifica cómo manejar esas llamadas en el juego.
Ofrece dibujo de mapas y metatiles, scripts, guardados SRAM y búsqueda de rutas. Las celdas normales miden 8×8 píxeles; los metatiles, 16×16. Cargar/dibujar, generar máscaras de movimiento y generar máscaras de ataque son operaciones separadas; el juego coordina la carga gráfica y la ejecución de eventos.
La búsqueda usa unos 4 KiB de espacio de trabajo compartido y no reentrante. Limita los mapas a 1024 celdas y las distancias exploradas a 254. La distancia 0xFF significa «no alcanzado», así que no se puede representar una ruta de 255 pasos. Las salidas de coste y máscara necesitan width*height bytes escribibles.
Por defecto hay cuatro ranuras de 512 bytes, cada una con una cabecera de seis bytes y hasta 506 bytes de datos. El ejemplo usa MBC5 con 8 KiB de SRAM. Las llamadas terminan con la RAM desactivada y no restauran el banco ni el estado de activación anterior.
Los menús permiten tanto una selección bloqueante como llamadas separadas de entrada y dibujo desde el bucle del juego. El menú del inventario muestra los nombres y las cantidades de los objetos.
__bit_clear
Borra el indicador elegido a 0 sin borrar el resto del byte. Úsalo para quitar una condición guardada, como un estado temporal. Volver a borrar el mismo bit lo mantiene en 0.
Sintaxis
void __bit_clear(u8* p, u16 bit_index);Parámetros
pPuntero al primer byte del array de indicadores en memoria accesible por la CPU. La llamada no reserva ni inicializa el array. Usa RAM escribible para activar, borrar e invertir; la operación de prueba solo lee.
bit_indexÍndice de bit
u16desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona0x01en el byte 0; el 7,0x80en el byte 0; el 8,0x01en el byte 1. En general, el desplazamiento esindex / 8y la máscara1 << (index % 8). Para un array de N bytes, exigeindex < 8*Ny un rango válido de direcciones de CPU.
Valor devuelto
Ninguno (void).
Condiciones y límites
El código que llama debe proporcionar espacio suficiente. No hay argumento de longitud, recorte de límites, cambio de banco ni transferencia gráfica automática. Un número de bit grande puede seleccionar un byte muy lejos del array reservado. Mantén estable el mapeo de memoria; no uses estas funciones de indicadores RAM como atajo para registros de hardware cuyas lecturas o escrituras tengan efectos secundarios.
Activar, borrar e invertir leen un byte, modifican el bit y lo escriben de nuevo. Esta secuencia no es atómica. Coordina las escrituras del bucle principal y de los manejadores de interrupción para no perder una actualización intermedia de otro bit del mismo byte.
Ejemplo de uso
__bit_clear(bit_flags,2);
bit_clear_value=bit_flags[0]; // A6 & FB = A2, decimal 162.
__bit_clear(bit_flags,2); // Clearing it twice leaves it clear.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa completo llega a este paso con el byte 0 en 0xA6. Borrar el bit 2 lo cambia a 0xA2 (binario 10100010, decimal 162); CLEAR BIT2 debe mostrar 162. Borrarlo otra vez no cambia el resultado. Los demás indicadores se conservan en ambas llamadas.
Valores esperados en GB, en decimal: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE procede de activar el índice 9 en el segundo byte, pasando de 0x80 a 0x82 sin modificar el byte 0. DMG usa texto negro sobre blanco; CGB, azul sobre blanco.
El programa comprueba cada transición mostrada e indica el número de fallos. Activar, borrar e invertir no devuelven valores; sus filas muestran el byte RAM resultante. Otras pruebas en emulador comparan los 520 bytes de búferes con zonas de protección en 136 casos por plataforma, incluidos índices constantes y en ejecución que cruzan límites de byte y de 256 bytes, y las ocho máscaras no nulas posibles. Son pruebas del estado de RAM, no una demostración gráfica. No se han probado hardware físico ni escrituras concurrentes desde interrupciones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:187
kitaqgb/kitaqgb/CodeGenerator.cs:8582
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
{
if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
EmitAsm("LD_B_A"); // bit number 0..7
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitShiftRightLogicalHL(3);
EmitAsm("PUSH_HL"); // byte offset
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)
AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
EmitAsm("LD_A_B");
EmitAsm("OR_A");
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR_Z", maskReady);
EmitLabel(maskLoop);
EmitAsm("ADD_A");
EmitAsm("DEC_B");
EmitAsm("JR_NZ", maskLoop);
EmitLabel(maskReady);
EmitAsm("LD_B_A"); // mask
if (funcName == "__bit_test")
{
AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
AsmOperand done = MakeUniqueLabel("bit_test_done");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("JR_Z", retZero);
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR", done);
EmitLabel(retZero);
EmitAsm("XOR_A");
EmitLabel(done);
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_set")
{
EmitAsm("LD_A_HL");
EmitAsm("OR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_clear")
{
EmitAsm("LD_A_B");
EmitAsm("CPL");
EmitAsm("LD_B_A");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
EmitAsm("LD_A_HL");
EmitAsm("XOR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}__bit_set
Activa el indicador elegido a 1 conservando los otros siete bits del byte. Por ejemplo, marca una misión completada o un objeto recogido. Activar un bit que ya vale 1 lo deja igual; no es una conmutación.
Sintaxis
void __bit_set(u8* p, u16 bit_index);Parámetros
pPuntero al primer byte del array de indicadores en memoria accesible por la CPU. La llamada no reserva ni inicializa el array. Usa RAM escribible para activar, borrar e invertir; la operación de prueba solo lee.
bit_indexÍndice de bit
u16desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona0x01en el byte 0; el 7,0x80en el byte 0; el 8,0x01en el byte 1. En general, el desplazamiento esindex / 8y la máscara1 << (index % 8). Para un array de N bytes, exigeindex < 8*Ny un rango válido de direcciones de CPU.
Valor devuelto
Ninguno (void).
Condiciones y límites
El código que llama debe proporcionar espacio suficiente. No hay argumento de longitud, recorte de límites, cambio de banco ni transferencia gráfica automática. Un número de bit grande puede seleccionar un byte muy lejos del array reservado. Mantén estable el mapeo de memoria; no uses estas funciones de indicadores RAM como atajo para registros de hardware cuyas lecturas o escrituras tengan efectos secundarios.
Activar, borrar e invertir leen un byte, modifican el bit y lo escriben de nuevo. Esta secuencia no es atómica. Coordina las escrituras del bucle principal y de los manejadores de interrupción para no perder una actualización intermedia de otro bit del mismo byte.
Ejemplo de uso
__bit_set(bit_flags,1);
bit_set_value=bit_flags[0]; // A4 | 02 = A6, decimal 166.
__bit_set(bit_flags,1); // Setting it twice leaves it set.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Activa el bit 1 de 0xA4: 10100100 | 00000010 pasa a 10100110, es decir, 0xA6 (166 decimal). SET BIT1 debe mostrar 166. El ejemplo vuelve a activarlo y comprueba que el byte sigue en 0xA6, mostrando que repetir la activación no invierte el indicador.
Valores esperados en GB, en decimal: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE procede de activar el índice 9 en el segundo byte, pasando de 0x80 a 0x82 sin modificar el byte 0. DMG usa texto negro sobre blanco; CGB, azul sobre blanco.
El programa comprueba cada transición mostrada e indica el número de fallos. Activar, borrar e invertir no devuelven valores; sus filas muestran el byte RAM resultante. Otras pruebas en emulador comparan los 520 bytes de búferes con zonas de protección en 136 casos por plataforma, incluidos índices constantes y en ejecución que cruzan límites de byte y de 256 bytes, y las ocho máscaras no nulas posibles. Son pruebas del estado de RAM, no una demostración gráfica. No se han probado hardware físico ni escrituras concurrentes desde interrupciones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:186
kitaqgb/kitaqgb/CodeGenerator.cs:8582
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
{
if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
EmitAsm("LD_B_A"); // bit number 0..7
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitShiftRightLogicalHL(3);
EmitAsm("PUSH_HL"); // byte offset
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)
AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
EmitAsm("LD_A_B");
EmitAsm("OR_A");
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR_Z", maskReady);
EmitLabel(maskLoop);
EmitAsm("ADD_A");
EmitAsm("DEC_B");
EmitAsm("JR_NZ", maskLoop);
EmitLabel(maskReady);
EmitAsm("LD_B_A"); // mask
if (funcName == "__bit_test")
{
AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
AsmOperand done = MakeUniqueLabel("bit_test_done");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("JR_Z", retZero);
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR", done);
EmitLabel(retZero);
EmitAsm("XOR_A");
EmitLabel(done);
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_set")
{
EmitAsm("LD_A_HL");
EmitAsm("OR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_clear")
{
EmitAsm("LD_A_B");
EmitAsm("CPL");
EmitAsm("LD_B_A");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
EmitAsm("LD_A_HL");
EmitAsm("XOR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}__bit_test
Lee un indicador de un array de bits empaquetados sin modificarlo. Así, ocho indicadores independientes de encendido/apagado comparten cada byte de RAM. El resultado indica si el bit elegido está desactivado o activado; su representación numérica depende de la plataforma.
Sintaxis
u8 __bit_test(const u8* p, u16 bit_index);Parámetros
pPuntero al primer byte del array de indicadores en memoria accesible por la CPU. La llamada no reserva ni inicializa el array. Usa RAM escribible para activar, borrar e invertir; la operación de prueba solo lee.
bit_indexÍndice de bit
u16desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona0x01en el byte 0; el 7,0x80en el byte 0; el 8,0x01en el byte 1. En general, el desplazamiento esindex / 8y la máscara1 << (index % 8). Para un array de N bytes, exigeindex < 8*Ny un rango válido de direcciones de CPU.
Valor devuelto
u8: exactamente 0 si el bit está desactivado y 1 si está activado. La implementación de GB normaliza el byte enmascarado a estos dos valores.
Condiciones y límites
El código que llama debe proporcionar espacio suficiente. No hay argumento de longitud, recorte de límites, cambio de banco ni transferencia gráfica automática. Un número de bit grande puede seleccionar un byte muy lejos del array reservado. Mantén estable el mapeo de memoria; no uses estas funciones de indicadores RAM como atajo para registros de hardware cuyas lecturas o escrituras tengan efectos secundarios.
Ejemplo de uso
bit_off=__bit_test(bit_flags,1); // Bit 1 of A4 is clear: zero.
bit_on=__bit_test(bit_flags,7); // Bit 7 is set: GB returns 1; FC returns 128.
bit_is_on=0;
if(bit_on!=0)bit_is_on=1; // The same nonzero test works on both targets.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Empieza con el byte 0 en 0xA4 (binario 10100100). Probar el bit 1 devuelve 0; probar el bit 7 devuelve 1 en GB o 128 en FC. El ejemplo también convierte ese resultado no nulo a un indicador común y comprueba que el byte original no cambia. Mira TEST OFF y TEST ON en la pantalla de abajo.
Valores esperados en GB, en decimal: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE procede de activar el índice 9 en el segundo byte, pasando de 0x80 a 0x82 sin modificar el byte 0. DMG usa texto negro sobre blanco; CGB, azul sobre blanco.
El programa comprueba cada transición mostrada e indica el número de fallos. Activar, borrar e invertir no devuelven valores; sus filas muestran el byte RAM resultante. Otras pruebas en emulador comparan los 520 bytes de búferes con zonas de protección en 136 casos por plataforma, incluidos índices constantes y en ejecución que cruzan límites de byte y de 256 bytes, y las ocho máscaras no nulas posibles. Son pruebas del estado de RAM, no una demostración gráfica. No se han probado hardware físico ni escrituras concurrentes desde interrupciones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:185
kitaqgb/kitaqgb/CodeGenerator.cs:8582
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
{
if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
EmitAsm("LD_B_A"); // bit number 0..7
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitShiftRightLogicalHL(3);
EmitAsm("PUSH_HL"); // byte offset
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)
AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
EmitAsm("LD_A_B");
EmitAsm("OR_A");
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR_Z", maskReady);
EmitLabel(maskLoop);
EmitAsm("ADD_A");
EmitAsm("DEC_B");
EmitAsm("JR_NZ", maskLoop);
EmitLabel(maskReady);
EmitAsm("LD_B_A"); // mask
if (funcName == "__bit_test")
{
AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
AsmOperand done = MakeUniqueLabel("bit_test_done");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("JR_Z", retZero);
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR", done);
EmitLabel(retZero);
EmitAsm("XOR_A");
EmitLabel(done);
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_set")
{
EmitAsm("LD_A_HL");
EmitAsm("OR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_clear")
{
EmitAsm("LD_A_B");
EmitAsm("CPL");
EmitAsm("LD_B_A");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
EmitAsm("LD_A_HL");
EmitAsm("XOR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}__bit_toggle
Invierte el indicador elegido: 0 pasa a 1 y 1 a 0. Conserva los demás bits. Dos llamadas sobre el mismo bit restauran su estado original, siempre que nada lo escriba entre ambas.
Sintaxis
void __bit_toggle(u8* p, u16 bit_index);Parámetros
pPuntero al primer byte del array de indicadores en memoria accesible por la CPU. La llamada no reserva ni inicializa el array. Usa RAM escribible para activar, borrar e invertir; la operación de prueba solo lee.
bit_indexÍndice de bit
u16desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona0x01en el byte 0; el 7,0x80en el byte 0; el 8,0x01en el byte 1. En general, el desplazamiento esindex / 8y la máscara1 << (index % 8). Para un array de N bytes, exigeindex < 8*Ny un rango válido de direcciones de CPU.
Valor devuelto
Ninguno (void).
Condiciones y límites
El código que llama debe proporcionar espacio suficiente. No hay argumento de longitud, recorte de límites, cambio de banco ni transferencia gráfica automática. Un número de bit grande puede seleccionar un byte muy lejos del array reservado. Mantén estable el mapeo de memoria; no uses estas funciones de indicadores RAM como atajo para registros de hardware cuyas lecturas o escrituras tengan efectos secundarios.
Activar, borrar e invertir leen un byte, modifican el bit y lo escriben de nuevo. Esta secuencia no es atómica. Coordina las escrituras del bucle principal y de los manejadores de interrupción para no perder una actualización intermedia de otro bit del mismo byte.
Ejemplo de uso
__bit_toggle(bit_flags,7);
bit_toggle_value=bit_flags[0]; // A2 ^ 80 = 22, decimal 34.
__bit_toggle(bit_flags,7);
bit_restore_value=bit_flags[0]; // Two toggles restore A2.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El programa completo entra con 0xA2. Invertir el bit 7 produce 0x22 (34 decimal), por lo que TOGGLE BIT7 muestra 034. Invertirlo otra vez restaura 0xA2 y RESTORE muestra 162. Ambas observaciones demuestran los dos sentidos de la inversión conservando los demás bits.
Valores esperados en GB, en decimal: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE procede de activar el índice 9 en el segundo byte, pasando de 0x80 a 0x82 sin modificar el byte 0. DMG usa texto negro sobre blanco; CGB, azul sobre blanco.
El programa comprueba cada transición mostrada e indica el número de fallos. Activar, borrar e invertir no devuelven valores; sus filas muestran el byte RAM resultante. Otras pruebas en emulador comparan los 520 bytes de búferes con zonas de protección en 136 casos por plataforma, incluidos índices constantes y en ejecución que cruzan límites de byte y de 256 bytes, y las ocho máscaras no nulas posibles. Son pruebas del estado de RAM, no una demostración gráfica. No se han probado hardware físico ni escrituras concurrentes desde interrupciones.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:188
kitaqgb/kitaqgb/CodeGenerator.cs:8582
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
{
if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
EmitAsm("LD_B_A"); // bit number 0..7
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitShiftRightLogicalHL(3);
EmitAsm("PUSH_HL"); // byte offset
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)
AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
EmitAsm("LD_A_B");
EmitAsm("OR_A");
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR_Z", maskReady);
EmitLabel(maskLoop);
EmitAsm("ADD_A");
EmitAsm("DEC_B");
EmitAsm("JR_NZ", maskLoop);
EmitLabel(maskReady);
EmitAsm("LD_B_A"); // mask
if (funcName == "__bit_test")
{
AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
AsmOperand done = MakeUniqueLabel("bit_test_done");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("JR_Z", retZero);
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR", done);
EmitLabel(retZero);
EmitAsm("XOR_A");
EmitLabel(done);
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_set")
{
EmitAsm("LD_A_HL");
EmitAsm("OR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
if (funcName == "__bit_clear")
{
EmitAsm("LD_A_B");
EmitAsm("CPL");
EmitAsm("LD_B_A");
EmitAsm("LD_A_HL");
EmitAsm("AND_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}
EmitAsm("LD_A_HL");
EmitAsm("XOR_B");
EmitAsm("LD_HL_A");
EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
return;
}__copy16
Copia exactamente 16 bytes de src a dst, sin argumento de longitud. Por ejemplo, permite copiar un registro de 16 bytes o un tile de GB/NES situado en memoria accesible por la CPU. El 16 indica bytes, no bits; la llamada no carga gráficos en el hardware de vídeo.
Sintaxis
void __copy16(void* dst, const void* src);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
srcPuntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.
Valor devuelto
Ninguno (void).
Condiciones y límites
Usa regiones de origen y destino que no se solapen. Estas rutinas copian hacia delante y no ofrecen la seguridad de memmove para zonas superpuestas; escribir sobre bytes del origen aún no leídos puede corromper la copia.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__copy16(memory_destination+1,memory_source+1);
memory_copy_16=memory_check(16,0); // Exactly 16 bytes, not 16 bits.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __copy16 para copiar 16 bytes con un patrón conocido desde el desplazamiento 1. memory_check compara los arrays completos de origen y destino; la fila COPY16 debe mostrar 016. El primer byte queda como protección y también se comprueba que los bytes posteriores al bloque no cambien.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:177
kitaqgb/kitaqgb/CodeGenerator.cs:8247
if (funcName == "__copy16" || funcName == "__copy32")
{
if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");
int copyLen = (funcName == "__copy16") ? 16 : 32;
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
EmitAsm("POP_DE"); // src
EmitAsm("POP_HL"); // dst
EmitRamMemcpyLoop(funcName.TrimStart('_'));
return;
}
void EmitRamMemcpyLoop(string labelPrefix)
{
AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");
EmitLabel(loop);
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JR_Z", done);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_BC");
EmitAsm("JR", loop);
EmitLabel(done);
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__copy32
Copia exactamente 32 bytes de src a dst, sin argumento de longitud. Úsalo para un registro fijo de 32 bytes o dos patrones de tile contiguos de 16 bytes en memoria accesible por la CPU. Copia byte a byte hacia direcciones crecientes; no realiza una lectura única de 32 bits ni cambia de banco.
Sintaxis
void __copy32(void* dst, const void* src);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
srcPuntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.
Valor devuelto
Ninguno (void).
Condiciones y límites
Usa regiones de origen y destino que no se solapen. Estas rutinas copian hacia delante y no ofrecen la seguridad de memmove para zonas superpuestas; escribir sobre bytes del origen aún no leídos puede corromper la copia.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__copy32(memory_destination+1,memory_source+1);
memory_copy_32=memory_check(32,0);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __copy32 para copiar 32 bytes con un patrón conocido desde el desplazamiento 1. memory_check compara los arrays completos de origen y destino; la fila COPY32 debe mostrar 032. El primer byte queda como protección y también se comprueba que los bytes posteriores al bloque no cambien.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:178
kitaqgb/kitaqgb/CodeGenerator.cs:8247
if (funcName == "__copy16" || funcName == "__copy32")
{
if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");
int copyLen = (funcName == "__copy16") ? 16 : 32;
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
EmitAsm("POP_DE"); // src
EmitAsm("POP_HL"); // dst
EmitRamMemcpyLoop(funcName.TrimStart('_'));
return;
}
void EmitRamMemcpyLoop(string labelPrefix)
{
AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");
EmitLabel(loop);
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JR_Z", done);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_BC");
EmitAsm("JR", loop);
EmitLabel(done);
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__far_memcpy
Selecciona temporalmente un banco de ROM, copia len bytes de src a dst en orden de direcciones creciente y restaura el banco guardado por el compilador. En esta plataforma, __farmemcpy y __far_memcpy comparten la implementación.
Sintaxis
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);Parámetros
dstDestino en memoria escribible, con espacio para todos los bytes copiados. Salvo en una transferencia de cero bytes, no se comprueba si el destino es nulo o inválido.
bankSelector de banco de 8 bits para la ROM de origen. Use un banco admitido por el cartucho y la distribución del compilador; se especifica por separado de la dirección.
srcPuntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.
lenCantidad de bytes, de 0 a 65535. Con cero no se copia memoria, pero sí se selecciona y restaura el banco.
Valor devuelto
Ninguno (void).
Condiciones y límites
Mantenga todo el origen en el mapeo de ROM previsto y todo el destino en memoria escribible. La copia no cambia automáticamente de banco al cruzar una ventana, no trata solapamientos como memmove ni sincroniza escrituras en VRAM. Para VRAM, use las API de transferencia gráfica.
Los ejemplos sitúan el código llamante en el banco fijo 0 para que cambiar el banco de origen no retire el código en ejecución. Las transferencias son síncronas. No las interrumpa con otra copia que cambie bancos ni suponga que la función garantiza por sí sola la seguridad de todas las interrupciones.
La rutina de GB restaura el banco registrado por el compilador. Manténgalo actualizado con __bankswitch; escribir directamente en el mapper no lo actualiza. La posición de HRAM que guarda el banco de retorno es compartida, por lo que no admite transferencias anidadas que la sobrescriban.
Ejemplo de uso
__far_memcpy(buffer,2,asset_shapes,32); // Same backend as __farmemcpy.
expect(*asset_bank_marker==0xA1,29);
for(i=0;i<32;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),32);
buffer[32]=0x55;
__far_memcpy(buffer+32,2,asset_shapes,0); // Zero count writes no bytes and restores the bank.
expect(buffer[32]==0x55 && *asset_bank_marker==0xA1,30);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Copia dos patrones, 32 bytes, del banco 2 partiendo del banco 1. Compara todos los bytes, comprueba la restauración del banco 1 y verifica que una copia de longitud cero conserva el testigo 0x55.
Imagen esperada: una fila roja empieza en (16,32) y ocupa 72×8 píxeles. Repite tres veces un triángulo de 8×8, un cuadrado de 8×8 con borde de un píxel y un tile con sus cuatro columnas izquierdas rellenas. El triángulo crece desde un píxel arriba a la izquierda hasta ocho en la fila inferior.
En GB, el triángulo y contorno azules de (16,64) muestran __farmemcpy; la pareja verde de (16,96), __far_memcpy. Cada pareja ocupa 16×8 píxeles. CGB usa esos colores; DMG muestra las mismas formas en negro sobre blanco.
El estado esperado es FAILED CHECKS 000 y FIRST FAILURE 00. La verificación ejecuta 300 fotogramas y compara texto, formas y colores con lo esperado. Es una prueba en emulador, no en hardware físico.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:148
__fill_tilemap
__fill_tilemap es un alias del compilador para __vram_memset. Usa el mismo recuento en bytes y la misma transferencia inmediata por CPU, incluidas las reglas de acceso al LCD.
Rellena inmediatamente len bytes consecutivos desde dst con el byte value. Repite un byte, no un patrón gráfico completo. En el mapa se repite un índice de patrón; en los datos gráficos se modifican los planos de bits de los píxeles.
Sintaxis
void __fill_tilemap(u16 dst, u8 value, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.valueUn byte de 0 a 255 que se repite. Su efecto visible depende de si el destino contiene índices de tiles, planos de píxeles u otra estructura de bytes.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Indica la dirección inicial y el recuento de bytes. Este alias no elige el mapa de fondo ni lo rellena entero automáticamente. Para un mapa completo de 32 × 32 índices, selecciona el banco VRAM 0 y usa la dirección base y 1024 bytes.
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__fill_tilemap(0x9922,1,length_base+length_extra); // Explicit address and byte count, not a whole-map clear.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __fill_tilemap para rellenar tres bytes del mapa desde la casilla (2,9), píxel (16,72), con el índice 1. Se espera una barra sólida de 24 × 8 con casillas vecinas vacías. El recuento procede de variables o del resultado de una función; los tres bytes deben conservar el valor 1.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:157
__getbgmapbase
Lee el bit 3 de LCDC para seleccionar la base del mapa de tiles del fondo.
Sintaxis
u16 __getbgmapbase();Valor devuelto
Devuelve $9800 o $9C00, la base del mapa de tiles, no la dirección de los gráficos de tiles.
Ejemplo de uso
M_LCDC=0;result[0]=__getbgmapbase();M_LCDC=8;result[1]=__getbgmapbase();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:140
kitaqgb/kitaqgb/CodeGenerator.cs:10619
if (funcName == "__getbgmapbase")
{
if (args.Length != 0) Error(expr, "__getbgmapbase requires 0 arguments");
AsmOperand gb_base0 = MakeUniqueLabel("getbgbase_base0");
AsmOperand gb_done = MakeUniqueLabel("getbgbase_done");
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate)); // bit3 (BG tile map display select)
EmitAsm("JR_Z", gb_base0);
EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
EmitAsm("JR", gb_done);
EmitLabel(gb_base0);
EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
EmitLabel(gb_done);
LastCallReturnsHL = true;
return;
}__manhattan
Suma las diferencias absolutas de X e Y entre dos puntos.
Sintaxis
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);Parámetros
x1Coordenada X del primer punto, de 0 a 255.
y1Coordenada Y del primer punto, de 0 a 255.
x2Coordenada X del segundo punto, de 0 a 255.
y2Coordenada Y del segundo punto, de 0 a 255.
Valor devuelto
Devuelve el byte bajo de |x1−x2|+|y1−y2|; las distancias superiores a 255 se desbordan.
Ejemplo de uso
result[5]=__manhattan(2,3,9,11);result[6]=__manhattan(0,0,255,255);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Prueba el interior, los límites, un punto exterior y ancho cero: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Verifica la distancia 15, la distancia desbordada 254 y los índices de array 64/65280.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:183
kitaqgb/kitaqgb/CodeGenerator.cs:8516
if (funcName == "__manhattan")
{
if (args.Length != 4) Error(expr, "__manhattan(x1, y1, x2, y2) expects 4 arguments");
Expr[] foldedManhattanArgs = args.Select(FoldConstants).ToArray();
if (foldedManhattanArgs[0].Match(Tag.Integer, out int constX1) &&
foldedManhattanArgs[1].Match(Tag.Integer, out int constY1) &&
foldedManhattanArgs[2].Match(Tag.Integer, out int constX2) &&
foldedManhattanArgs[3].Match(Tag.Integer, out int constY2))
{
int dx = Math.Abs((constX1 & 0xFF) - (constX2 & 0xFF));
int dy = Math.Abs((constY1 & 0xFF) - (constY2 & 0xFF));
EmitAsm("LD_A_IMM", new AsmOperand((dx + dy) & 0xFF, AddressMode.Immediate));
return;
}
EmitPushExprAsWord(args[0]); // x1
EmitPushExprAsWord(args[1]); // y1
EmitPushExprAsWord(args[2]); // x2
EmitPushExprAsWord(args[3]); // y2
AsmOperand dxGe = MakeUniqueLabel("manhattan_dx_ge");
AsmOperand dxDone = MakeUniqueLabel("manhattan_dx_done");
AsmOperand dyGe = MakeUniqueLabel("manhattan_dy_ge");
AsmOperand dyDone = MakeUniqueLabel("manhattan_dy_done");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_B_A"); // x1
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // x2
EmitAsm("LD_A_B");
EmitAsm("CP_C");
EmitAsm("JR_NC", dxGe);
EmitAsm("LD_A_C");
EmitAsm("SUB_B");
EmitAsm("JR", dxDone);
EmitLabel(dxGe);
EmitAsm("LD_A_B");
EmitAsm("SUB_C");
EmitLabel(dxDone);
EmitAsm("LD_B_A"); // dx
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // y1
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A"); // y2
EmitAsm("LD_A_C");
EmitAsm("CP_D");
EmitAsm("JR_NC", dyGe);
EmitAsm("LD_A_D");
EmitAsm("SUB_C");
EmitAsm("JR", dyDone);
EmitLabel(dyGe);
EmitAsm("LD_A_C");
EmitAsm("SUB_D");
EmitLabel(dyDone);
EmitAsm("ADD_B");
EmitAsm("ADD_SP_IMM", new AsmOperand(8, AddressMode.Relative));
return;
}__map_index
Convierte las coordenadas de una celda en un índice de array por filas.
Sintaxis
u16 __map_index(u8 x, u8 y, u8 width);Parámetros
xColumna de 0 a 255, normalmente menor que width.
yFila de 0 a 255.
widthCeldas por fila, de 0 a 255.
Valor devuelto
Devuelve y*width+x como u16; no valida las coordenadas.
Ejemplo de uso
result[7]=__map_index(4,3,20);result[8]=__map_index(255,255,255);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Prueba el interior, los límites, un punto exterior y ancho cero: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Verifica la distancia 15, la distancia desbordada 254 y los índices de array 64/65280.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:181
kitaqgb/kitaqgb/CodeGenerator.cs:8365
if (funcName == "__map_index")
{
if (args.Length != 3) Error(expr, "__map_index(x, y, width) expects 3 arguments");
Expr xExpr = FoldConstants(args[0]);
Expr yExpr = FoldConstants(args[1]);
Expr widthExpr = FoldConstants(args[2]);
// Apply the width parameter's byte conversion before choosing a shift specialization.
if (widthExpr.Match(Tag.Integer, out int byteWidth))
widthExpr = Expr.Make(Tag.Integer, byteWidth & 0xFF).WithSource(widthExpr.Source);
if (xExpr.Match(Tag.Integer, out int fullXConst) &&
yExpr.Match(Tag.Integer, out int fullYConst) &&
widthExpr.Match(Tag.Integer, out int fullWidthConst))
{
// Apply the byte argument widths before calculating the word-sized constant index.
int fullIndexConst = (((fullYConst & 0xFF) * (fullWidthConst & 0xFF)) + (fullXConst & 0xFF)) & 0xFFFF;
EmitAsm("LD_HL_IMM", new AsmOperand(fullIndexConst, AddressMode.Immediate16));
LastCallReturnsHL = true;
return;
}
if (widthExpr.Match(Tag.Integer, out int widthConst) && widthConst > 0 && IsPow2(widthConst))
{
CompileIntoA(yExpr);
EmitAsm("LD_L_A");
EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
int sh = Log2Pow2(widthConst);
for (int i = 0; i < sh; i++) EmitAsm("ADD_HL_HL");
if (xExpr.Match(Tag.Integer, out int xConst) && xConst == 0)
{
LastCallReturnsHL = true;
return;
}
CompileIntoA(xExpr);
EmitAsm("LD_E_A");
EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("ADD_HL_DE");
LastCallReturnsHL = true;
return;
}
CompileIntoA(args[0]); EmitPushAAsWord(); // x
CompileIntoA(args[1]); EmitPushAAsWord(); // y
CompileIntoA(args[2]); EmitPushAAsWord(); // width
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // width
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A"); // y
EmitMul8x8ToHL_EC("__map_index");
// Loading the saved X argument uses HL, so preserve the multiplication result first.
EmitAsm("PUSH_HL");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("POP_HL");
EmitAsm("ADD_HL_DE");
EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
LastCallReturnsHL = true;
return;
}__memcpy
Copia de src a dst el número de bytes indicado. El contador de 16 bits permite transferir más de 255 bytes, por ejemplo una tabla completa en RAM. Lee y escribe en orden creciente de direcciones.
Sintaxis
void __memcpy(void* dst, const void* src, u16 count);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
srcPuntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.
countNúmero de bytes como
u16(0–65535), no de elementos. El máximo del tipo no implica que la máquina disponga de un búfer válido de ese tamaño. Todo el intervalo debe caber en el mapa de memoria seleccionado.
Valor devuelto
Ninguno (void).
Condiciones y límites
Un contador a cero no transfiere bytes. Los efectos secundarios de las expresiones de los argumentos sí se ejecutan: el ejemplo pasa dos funciones que devuelven punteros y comprueba que ambas se llaman aunque la longitud sea cero.
Usa regiones de origen y destino que no se solapen. Estas rutinas copian hacia delante y no ofrecen la seguridad de memmove para zonas superpuestas; escribir sobre bytes del origen aún no leídos puede corromper la copia.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__memcpy(memory_destination+1,memory_source+1,257);
memory_copy_word=memory_check(257,0); // 257 copied bytes; guards unchanged.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __memcpy para copiar 257 bytes con un patrón conocido desde el desplazamiento 1. memory_check compara los arrays completos de origen y destino; la fila COPY WORD debe mostrar 257. El primer byte queda como protección y también se comprueba que los bytes posteriores al bloque no cambien.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:142
kitaqgb/kitaqgb/CodeGenerator.cs:9084
if (funcName == "__memcpy" || funcName == "__memcpy_small")
{
// Select byte or word count semantics before optional fixed-array bounds diagnostics.
bool useU8Count = (funcName == "__memcpy_small");
if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }
bool doCheck = Program.CheckMemCopy && !InUnsafe;
bool hasDst = false, hasSrc = false;
int dstBytes = 0, srcBytes = 0;
string dstName = null, srcName = null;
if (doCheck)
{
hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
}
bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
int normalizedConstCount = constCountAny;
if (isConstCountAny && useU8Count)
normalizedConstCount &= 0xFF;
if (doCheck && isConstCountAny)
{
if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
{
Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
{
Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
}
if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
{
if (normalizedConstCount == 0) return;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
for (int k = 0; k < normalizedConstCount; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
if (normalizedConstCount <= 64)
{
int blocks8 = normalizedConstCount / 8;
int rem8 = normalizedConstCount % 8;
if (blocks8 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
// Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
EmitLabel(loop8);
for (int k = 0; k < 8; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop8);
}
for (int k = 0; k < rem8; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
int blocks16 = normalizedConstCount / 16;
int rem16 = normalizedConstCount % 16;
if (blocks16 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
// Use sixteen-byte groups while keeping the residual copy length known at compile time.
AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
EmitLabel(loop16);
for (int k = 0; k < 16; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop16);
}
for (int k = 0; k < rem16; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
if (useU8Count)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_B_A");
if (doCheck)
{
if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
}
AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
EmitAsm("OR_A");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
EmitAsm("JP_Z", doneLabel);
EmitLabel(smallLoopLabel);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", smallLoopLabel);
EmitLabel(doneLabel);
return;
}
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[2]);
if (doCheck && !isConstCountAny)
{
if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
}
EmitAsm("LD_B_H");
EmitAsm("LD_C_L");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
// A zero word count must skip the first write as well as the loop.
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_Z", wordDoneLabel);
EmitLabel(loopLabel);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_BC");
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_NZ", loopLabel);
EmitLabel(wordDoneLabel);
return;
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__memcpy_small
Copia un bloque de hasta 255 bytes con un contador de 8 bits. Usa esta variante cuando la longitud quepa en un byte. _small indica el tamaño del contador; no garantiza un tiempo de ejecución ni un tamaño de código concretos.
Sintaxis
void __memcpy_small(void* dst, const void* src, u8 len);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
srcPuntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.
lenNúmero de bytes como
u8: 0–255. No pases 256 para representar un bloque completo de 256 bytes: esa longitud no cabe en el parámetro. Para 256 bytes o más, usa la función correspondiente sin_small.
Valor devuelto
Ninguno (void).
Condiciones y límites
Un contador a cero no transfiere bytes. Los efectos secundarios de las expresiones de los argumentos sí se ejecutan: el ejemplo pasa dos funciones que devuelven punteros y comprueba que ambas se llaman aunque la longitud sea cero.
Usa regiones de origen y destino que no se solapen. Estas rutinas copian hacia delante y no ofrecen la seguridad de memmove para zonas superpuestas; escribir sobre bytes del origen aún no leídos puede corromper la copia.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__memcpy_small(memory_destination+1,memory_source+1,255);
memory_copy_byte=memory_check(255,0); // Maximum representable byte count.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __memcpy_small para copiar 255 bytes con un patrón conocido desde el desplazamiento 1. memory_check compara los arrays completos de origen y destino; la fila COPY BYTE debe mostrar 255. El primer byte queda como protección y también se comprueba que los bytes posteriores al bloque no cambien.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:175
kitaqgb/kitaqgb/CodeGenerator.cs:9084
if (funcName == "__memcpy" || funcName == "__memcpy_small")
{
// Select byte or word count semantics before optional fixed-array bounds diagnostics.
bool useU8Count = (funcName == "__memcpy_small");
if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }
bool doCheck = Program.CheckMemCopy && !InUnsafe;
bool hasDst = false, hasSrc = false;
int dstBytes = 0, srcBytes = 0;
string dstName = null, srcName = null;
if (doCheck)
{
hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
}
bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
int normalizedConstCount = constCountAny;
if (isConstCountAny && useU8Count)
normalizedConstCount &= 0xFF;
if (doCheck && isConstCountAny)
{
if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
{
Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
{
Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
}
if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
{
if (normalizedConstCount == 0) return;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
for (int k = 0; k < normalizedConstCount; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
if (normalizedConstCount <= 64)
{
int blocks8 = normalizedConstCount / 8;
int rem8 = normalizedConstCount % 8;
if (blocks8 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
// Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
EmitLabel(loop8);
for (int k = 0; k < 8; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop8);
}
for (int k = 0; k < rem8; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
int blocks16 = normalizedConstCount / 16;
int rem16 = normalizedConstCount % 16;
if (blocks16 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
// Use sixteen-byte groups while keeping the residual copy length known at compile time.
AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
EmitLabel(loop16);
for (int k = 0; k < 16; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop16);
}
for (int k = 0; k < rem16; k++)
{
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
}
return;
}
if (useU8Count)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoA(args[2]);
EmitAsm("LD_B_A");
if (doCheck)
{
if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
}
AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
EmitAsm("OR_A");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
EmitAsm("JP_Z", doneLabel);
EmitLabel(smallLoopLabel);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", smallLoopLabel);
EmitLabel(doneLabel);
return;
}
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[1]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[2]);
if (doCheck && !isConstCountAny)
{
if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
}
EmitAsm("LD_B_H");
EmitAsm("LD_C_L");
EmitAsm("POP_DE");
EmitAsm("POP_HL");
AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
// A zero word count must skip the first write as well as the loop.
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_Z", wordDoneLabel);
EmitLabel(loopLabel);
EmitAsm("LD_A_DE");
EmitAsm("LDI_HL_A");
EmitAsm("INC_DE");
EmitAsm("DEC_BC");
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_NZ", loopLabel);
EmitLabel(wordDoneLabel);
return;
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__memset
Rellena el destino con un mismo valor de un byte. El contador de 16 bits resulta útil para vaciar un búfer grande o inicializar una tabla. Por ejemplo, 0x5A escribe 5A 5A 5A…; no repite un entero de varios bytes.
Sintaxis
void __memset(void* dst, u8 value, u16 count);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
valueByte que se repite en cada dirección del destino: 0–255. Usa 0 para poner el intervalo a cero. Para inicializar un array de
u16con un número distinto de cero, asigna ese valor a cada elemento.countNúmero de bytes como
u16(0–65535), no de elementos. El máximo del tipo no implica que la máquina disponga de un búfer válido de ese tamaño. Todo el intervalo debe caber en el mapa de memoria seleccionado.
Valor devuelto
Ninguno (void).
Condiciones y límites
Un contador a cero no transfiere bytes. Los efectos secundarios de las expresiones de los argumentos sí se ejecutan: el ejemplo pasa dos funciones que devuelven punteros y comprueba que ambas se llaman aunque la longitud sea cero.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__memset(memory_destination+1,0x5A,257);
memory_fill_word=memory_check(257,1); // Repeat one byte, not a 16-bit word.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __memset para rellenar 257 bytes desde el desplazamiento 1 con 0x5A. memory_check exige que todos esos bytes valgan 0x5A, que los bytes de protección sigan en 0xCC y que el búfer de origen separado no cambie. La fila FILL WORD debe mostrar 257.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:141
kitaqgb/kitaqgb/CodeGenerator.cs:8926
if (funcName == "__memset" || funcName == "__memset_small")
{
// The small API truncates its count to a byte; the general API uses a word count.
bool useU8Count = (funcName == "__memset_small");
if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }
// -Zcheck: fixed-array range check for memset length
bool doCheck = Program.CheckMemCopy && !InUnsafe;
bool hasDst = false;
int dstBytes = 0;
string dstName = null;
if (doCheck)
hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
int normalizedConstCount = constCountAny;
if (isConstCountAny && useU8Count)
normalizedConstCount &= 0xFF;
if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
{
Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
// Size-specialized memset for small constant counts (<=16): unrolled stores.
if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
{
if (normalizedConstCount == 0) return;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
for (int k = 0; k < normalizedConstCount; k++)
EmitAsm("LDI_HL_A");
return;
}
// Size-specialized memset for constant counts beyond 16
if (isConstCountAny && normalizedConstCount >= 0)
{
if (normalizedConstCount == 0) return;
if (normalizedConstCount > 16 && normalizedConstCount <= 64)
{
int blocks8 = normalizedConstCount / 8;
int rem = normalizedConstCount % 8;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
if (blocks8 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
// Emit groups of eight stores followed by the constant remainder for medium fills.
AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
EmitLabel(loop8);
for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop8);
}
for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
return;
}
if (normalizedConstCount <= 255)
{
int blocks16 = normalizedConstCount / 16;
int rem = normalizedConstCount % 16;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
if (blocks16 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
// Use sixteen-store groups for larger byte-range constant fills.
AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
EmitLabel(loop16);
for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop16);
}
for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
return;
}
}
if (useU8Count)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("LD_D_A");
CompileIntoA(args[2]);
EmitAsm("LD_B_A");
if (doCheck && hasDst)
EmitU8LeqConstOrTrapInA(dstBytes);
AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
EmitAsm("OR_A");
EmitAsm("POP_HL");
EmitAsm("JP_Z", doneLabel);
EmitAsm("LD_A_D");
EmitLabel(smallLoopLabel);
EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", smallLoopLabel);
EmitLabel(doneLabel);
return;
}
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[2]);
if (doCheck && hasDst && !isConstCountAny)
EmitU16LeqConstOrTrap(dstBytes);
EmitAsm("LD_B_H");
EmitAsm("LD_C_L");
CompileIntoA(args[1]);
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
// A zero word count must skip the first write as well as the loop.
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_Z", wordDoneLabel);
EmitLabel(loopLabel);
EmitAsm("LD_A_D");
EmitAsm("LDI_HL_A");
EmitAsm("DEC_BC");
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_NZ", loopLabel);
EmitLabel(wordDoneLabel);
return;
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__memset_small
Rellena hasta 255 bytes consecutivos con un mismo valor, usando un contador de 8 bits. Sirve para inicializar un búfer de trabajo pequeño. Si necesitas más de 255 bytes, utiliza __memset.
Sintaxis
void __memset_small(void* dst, u8 value, u8 len);Parámetros
dstPuntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.
valueByte que se repite en cada dirección del destino: 0–255. Usa 0 para poner el intervalo a cero. Para inicializar un array de
u16con un número distinto de cero, asigna ese valor a cada elemento.lenNúmero de bytes como
u8: 0–255. No pases 256 para representar un bloque completo de 256 bytes: esa longitud no cabe en el parámetro. Para 256 bytes o más, usa la función correspondiente sin_small.
Valor devuelto
Ninguno (void).
Condiciones y límites
Un contador a cero no transfiere bytes. Los efectos secundarios de las expresiones de los argumentos sí se ejecutan: el ejemplo pasa dos funciones que devuelven punteros y comprueba que ambas se llaman aunque la longitud sea cero.
La operación termina antes de devolver el control. No cambia bancos de ROM/RAM ni recorta el intervalo al límite del búfer. Mantén estable el mapa de memoria, usa regiones válidas y coordina el acceso con las interrupciones que utilicen los mismos búferes.
En GB son accesos ordinarios de la CPU a memoria. No esperan un momento seguro para acceder a VRAM/OAM, no inician DMA ni seleccionan bancos de VRAM/WRAM de CGB. Para esas tareas, usa las API específicas de transferencia gráfica.
Ejemplo de uso
memory_reset();
__memset_small(memory_destination+1,0x5A,255);
memory_fill_byte=memory_check(255,1);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo usa __memset_small para rellenar 255 bytes desde el desplazamiento 1 con 0x5A. memory_check exige que todos esos bytes valgan 0x5A, que los bytes de protección sigan en 0xCC y que el búfer de origen separado no cambie. La fila FILL BYTE debe mostrar 255.
El programa completo inicializa dos búferes de 264 bytes antes de cada operación y comprueba todos los bytes del origen, del destino y de las zonas de protección. Debe mostrar COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 y FAILED 000. WORD/BYTE indican el tamaño del contador, no el de los elementos copiados. Los números proceden de las comprobaciones del ejemplo; estas funciones void no devuelven valores.
Las capturas muestran letras negras sobre blanco en DMG, azules sobre blanco en CGB y azules sobre negro en FC. El ejemplo verifica operaciones de RAM; el texto presenta sus resultados, no una primitiva de dibujo. Otras pruebas en emulador cubren 88 casos por plataforma, con longitudes constantes y calculadas en ejecución de 0, 1, 16, 17, 32, 33, 64, 65 y 255 bytes, más 256 y 257 para los contadores de 16 bits. No se ha probado en hardware real.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:176
kitaqgb/kitaqgb/CodeGenerator.cs:8926
if (funcName == "__memset" || funcName == "__memset_small")
{
// The small API truncates its count to a byte; the general API uses a word count.
bool useU8Count = (funcName == "__memset_small");
if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }
// -Zcheck: fixed-array range check for memset length
bool doCheck = Program.CheckMemCopy && !InUnsafe;
bool hasDst = false;
int dstBytes = 0;
string dstName = null;
if (doCheck)
hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
int normalizedConstCount = constCountAny;
if (isConstCountAny && useU8Count)
normalizedConstCount &= 0xFF;
if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
{
Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
EmitAsm("JP", EnsureCheckTrap());
return;
}
// Size-specialized memset for small constant counts (<=16): unrolled stores.
if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
{
if (normalizedConstCount == 0) return;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
for (int k = 0; k < normalizedConstCount; k++)
EmitAsm("LDI_HL_A");
return;
}
// Size-specialized memset for constant counts beyond 16
if (isConstCountAny && normalizedConstCount >= 0)
{
if (normalizedConstCount == 0) return;
if (normalizedConstCount > 16 && normalizedConstCount <= 64)
{
int blocks8 = normalizedConstCount / 8;
int rem = normalizedConstCount % 8;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
if (blocks8 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
// Emit groups of eight stores followed by the constant remainder for medium fills.
AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
EmitLabel(loop8);
for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop8);
}
for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
return;
}
if (normalizedConstCount <= 255)
{
int blocks16 = normalizedConstCount / 16;
int rem = normalizedConstCount % 16;
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("POP_HL");
if (blocks16 > 0)
{
EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
// Use sixteen-store groups for larger byte-range constant fills.
AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
EmitLabel(loop16);
for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", loop16);
}
for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
return;
}
}
if (useU8Count)
{
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoA(args[1]);
EmitAsm("LD_D_A");
CompileIntoA(args[2]);
EmitAsm("LD_B_A");
if (doCheck && hasDst)
EmitU8LeqConstOrTrapInA(dstBytes);
AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
EmitAsm("OR_A");
EmitAsm("POP_HL");
EmitAsm("JP_Z", doneLabel);
EmitAsm("LD_A_D");
EmitLabel(smallLoopLabel);
EmitAsm("LDI_HL_A");
EmitAsm("DEC_B");
EmitAsm("JP_NZ", smallLoopLabel);
EmitLabel(doneLabel);
return;
}
CompileIntoHL(args[0]);
EmitAsm("PUSH_HL");
CompileIntoHL(args[2]);
if (doCheck && hasDst && !isConstCountAny)
EmitU16LeqConstOrTrap(dstBytes);
EmitAsm("LD_B_H");
EmitAsm("LD_C_L");
CompileIntoA(args[1]);
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
// A zero word count must skip the first write as well as the loop.
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_Z", wordDoneLabel);
EmitLabel(loopLabel);
EmitAsm("LD_A_D");
EmitAsm("LDI_HL_A");
EmitAsm("DEC_BC");
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JP_NZ", loopLabel);
EmitLabel(wordDoneLabel);
return;
}
// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
{
string knownCallName;
if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
knownCallName == "__cgb_is_cgb" &&
callArgs != null && callArgs.Length == 0 &&
// A resolved target configuration can replace the zero-argument CGB query with a literal.
Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
{
return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
}
Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);
// Optimization (post-A3): when we must spill a non-atomic argument into a temp,
// prefer a u8 temp if the *parameter type* is u8-like.
// This preserves semantics (implicit truncation to u8) while avoiding needless
// u16 temporaries created by integer promotions.
CType[] paramTypes = null;
string __fname = null;
if (newFunc.Match(Tag.Name, out __fname))
{
paramTypes = ctx.FindFuncParams(__fname);
if (paramTypes == null)
{
CType fnTypeFromName;
if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
paramTypes = fnTypeFromName.ParamTypes;
}
}
else
{
CType fnTypeFromExpr;
if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
paramTypes = fnTypeFromExpr.ParamTypes;
}
Expr[] newArgs = new Expr[callArgs.Length];
for (int i = 0; i < callArgs.Length; i++)
{
Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
bool preserveConstantArgIntrinsic = false;
if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
argFunc.Match(Tag.Name, out string argFuncName))
{
preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
}
// Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);
if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
{
// If we know the callee's parameter type, use it to choose a smaller temp.
// Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
CType spillAs = null;
if (paramTypes != null && i < paramTypes.Length)
{
CType pt = paramTypes[i];
if (IsU8Like(pt)) spillAs = CType.UInt8;
}
if (spillAs == null) spillAs = InferType(ctx, a);
if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
a = SpillToTemp(ctx, a, spillAs, prefix);
}
newArgs[i] = a;
}
// Lint: const discard for argument passing
// Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
// Suppress when the argument has an explicit cast, or inside __unsafe.
if (!ctx.InUnsafe && paramTypes != null)
{
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
if (!IsPointerToNonConst(pt)) continue;
if (HasTopLevelCast(newArgs[i])) continue;
CType at = InferType(ctx, newArgs[i]);
if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
{
string fn = __fname ?? "<call>";
WarnConstDiscard(expr, $"argument {i} to '{fn}'");
break; // keep it quiet: one warning per call is enough
}
}
}
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
FilePosition argPos = newArgs[i].Source;
if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
argPos = expr.Source;
WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
"argument " + i + " to '" + fn + "'");
}
}
// Lint: __enum_strict argument passing.
if (paramTypes != null)
{
string fn = __fname ?? "<call>";
for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
{
CType pt = paramTypes[i];
if (pt == null) continue;
CType at = InferType(ctx, newArgs[i]);
if (at == null) continue;
if (HasTopLevelCast(newArgs[i])) continue;
if (IsStrictEnum(pt))
{
if (!IsSameEnum(pt, at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
i, fn, pt.Show(), at.Show());
}
}
else if (IsPlainInteger(pt) && IsStrictEnum(at))
{
Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
"argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
i, fn, at.Show(), pt.Show());
}
}
}
if (!ctx.InUnsafe)
RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);
// __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
// Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
// Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
// Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
// when there is no declared function named "slice").
if (newFunc.Match(Tag.Name, out string __sfname))
{
bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
if (isSliceIntrinsic)
{
if (newArgs.Length != 2)
{
Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
}
else
{
CType pty = InferType(ctx, newArgs[0]);
if (pty == null || !(pty.IsPointer || pty.IsArray))
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));
CType lty = InferType(ctx, newArgs[1]);
if (lty == null || !lty.IsInteger)
Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
}
return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
}
}
return MakeCall(newFunc, newArgs).WithSource(expr.Source);
}__rle_decode_vram
Descomprime un flujo count/value con banco indicado directamente en VRAM, sin búfer de salida intermedio en RAM. El compilador genera un bucle que lee cada par, escribe sus repeticiones con la rutina de VRAM que respeta los tiempos del LCD y devuelve los bytes producidos. Es útil para cargar datos de tiles comprimidos o un mapa de tiles organizado por bytes.
Sintaxis
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);Parámetros
dstDirección CPU de 16 bits del primer byte de salida, dentro de 0x8000–0x9FFF. Reserva suficientes bytes consecutivos sin pasar de 0x9FFF. Por ejemplo, 0x8860 es el tile 134 en la disposición de datos de tiles con índices sin signo. La función no asigna tiles ni comprueba el rango de destino.
bankNúmero de banco ROM de origen como u8. El banco y la dirección CPU deben corresponder al mapper del cartucho y a la ubicación del recurso. La selección de banco se restaura tras leer cada byte codificado. No selecciona el banco VRAM de destino ni puede representar un número de banco MBC5 de nueve bits.
srcPuntero CPU al flujo codificado en el banco indicado. La función incrementa la dirección, pero mantiene fijo el banco. Conserva todo el flujo, incluido el cero final, dentro de un mapeo legible; cruzar 0x7FFF no continúa automáticamente en el banco ROM siguiente.
Valor devuelto
Número de bytes escritos como u16. Un contador cero inmediato no produce bytes y devuelve 0. El terminador no se copia. La longitud descomprimida debe caber en u16; el retorno no incluye un código de error separado ni representa una cantidad de tiles.
Condiciones y límites
La codificación trabaja con bytes, no con texto. Por ejemplo, {3, 0x12, 2, 0xA5, 0} se expande a cinco bytes: 12 12 12 A5 A5. Esa lista de salida está en hexadecimal. Un valor cero en un par con count distinto de cero es un dato normal, no una marca de fin.
No hay argumentos para el tamaño codificado, la capacidad de salida o el tratamiento de datos malformados. Valida los recursos al crearlos o cargarlos, añade el cero final y calcula el tamaño descomprimido antes de decodificar. Un flujo inválido o sin terminador puede leer fuera del origen y escribir fuera del destino.
Con el LCD apagado, las repeticiones se escriben directamente. Encendido, la rutina espera un modo LCD escribible para cada byte de salida, usando DI/EI alrededor de la espera y la escritura. Por tanto, un flujo largo puede abarcar varios fotogramas; es una llamada síncrona, no una transferencia en cola. Esta ruta habilita las interrupciones después de cada byte en lugar de conservar un estado inicial deshabilitado.
En CGB, las escrituras usan el banco VBK seleccionado y no cambian esa selección. Elige VBK 0 para los datos de tiles del ejemplo. El argumento de banco sigue seleccionando la entrada ROM aunque se elija VBK 1 para la salida. El decodificador no gestiona las paletas CGB ni los atributos del mapa.
Durante una lectura con banco indicado, evita que una interrupción cambie el mapeo ROM o reutilice la rutina de copia lejana: estas funciones comparten el estado de gestión de bancos. Sitúa el decodificador de puntero normal y su origen en mapeos compatibles; cambiar al banco de la función no debe ocultar el puntero normal de origen.
rle_decode y rle_decode_far se declaran en rpg.h y se implementan en lib/rle.c. Compila esa implementación una sola vez con el juego. El ejemplo completo incluye rle.c después de #pragma bank 0 para mantener el código que llama y los decodificadores en el banco común; no pases además rle.c como entrada separada en este ejemplo. La función intrínseca __rle_decode_vram no necesita un archivo de implementación de biblioteca.
Ejemplo de uso
vram_count=__rle_decode_vram(0x8860,2,banked_rle); // Expand directly to 48 bytes of tile data.
if(*rle_bank_marker!=0xA1)failures++;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo descomprime el flujo del banco 2 directamente en VRAM en 0x8860, sin búfer de salida RAM. DIRECT VRAM muestra 048. La fila inferior es azul en CGB y debe contener los mismos tres patrones de tiles que las filas basadas en RAM. Este programa didáctico realiza la transferencia con el LCD apagado.
Cada fila contiene, de izquierda a derecha, un cuadrado relleno de 8×8, un contorno de 8×8 de un píxel de grosor y un rectángulo relleno de 4×8 que ocupa la mitad izquierda de un tile de 8×8. Están en las columnas de tiles 2, 5 y 8 (x = 16, 40 y 64 píxeles). Las tres filas empiezan en y = 32, 72 y 112 píxeles. DMG usa formas negras; CGB usa rojo, verde y azul por fila. FAILED debe ser 000. Los números son bytes: tres tiles GB de 16 bytes requieren 48 bytes descomprimidos.
La matriz de verificación comprueba 42 casos: flujo vacío, repetición de un byte, datos de valor cero, repetición de 255 bytes, salida de 510 bytes que cruza una página de destino y datos codificados que cruzan una página de origen. Compara los 528 bytes observados de destino y guarda, la longitud de salida y el mapeo ROM restaurado. Los casos de VRAM directa incluyen LCD encendido/apagado en DMG y CGB, además de VBK 1 en CGB. Las dos pantallas didácticas se comprueban tras 240 fotogramas del emulador. No cubre recursos inválidos, uso simultáneo por interrupciones ni hardware físico.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:191
Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.
kitaqgb/kitaqgb/CodeGenerator.cs:8673
if (funcName == "__rle_decode_vram")
{
if (args.Length != 3) Error(expr, "__rle_decode_vram(dst, bank, src) expects 3 arguments");
CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
CompileIntoA(args[1]); EmitPushAAsWord(); // bank
CompileIntoHL(args[2]); EmitAsm("PUSH_HL"); // src
EmitAsm("ADD_SP_IMM", new AsmOperand(-4, AddressMode.Relative)); // total(2) + scratch(2)
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("XOR_A");
EmitAsm("LD_HL_A");
EmitAsm("INC_HL");
EmitAsm("LD_HL_A");
AsmOperand loop = MakeUniqueLabel("rlevram_loop");
// Keep total, scratch, source, bank and destination in stack slots across repeated far-memory calls.
AsmOperand countDone = MakeUniqueLabel("rlevram_count_inc_done");
AsmOperand valueDone = MakeUniqueLabel("rlevram_value_inc_done");
AsmOperand finished = MakeUniqueLabel("rlevram_finished");
EmitLabel(loop);
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("PUSH_HL"); // &scratch
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A"); // src
EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
EmitAsm("LD_A_HL"); // bank
EmitAsm("POP_HL"); // &scratch
EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
EmitCallFarMemcpyHelper();
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("LD_A_HL"); // count
EmitAsm("OR_A");
EmitAsm("JP_Z", finished);
EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
EmitAsm("LD_HL_A"); // save count in scratch+1
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("LD_HL_A");
EmitAsm("JR_NC", countDone);
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("LD_HL_A");
EmitLabel(countDone);
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("PUSH_HL"); // &scratch
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A"); // src
EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
EmitAsm("LD_A_HL"); // bank
EmitAsm("POP_HL"); // &scratch
EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
EmitCallFarMemcpyHelper();
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A"); // value
EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A");
EmitAsm("LD_B_IMM", new AsmOperand(0, AddressMode.Immediate)); // count
EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E"); // dst
// Loading dst used D for its high byte. Recover the run value
// from stack scratch while preserving the completed HL pointer.
EmitAsm("PUSH_HL");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("POP_HL");
// Use the LCD-aware fill loop for each decoded run, then store its advanced destination pointer.
EmitVramMemsetLoop(true, "rlevram_fill");
EmitAsm("PUSH_HL");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
EmitAsm("POP_DE");
EmitAsm("LD_A_E");
EmitAsm("LD_HL_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_D");
EmitAsm("LD_HL_A"); // save dst
EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_B_A"); // count
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("ADD_B");
EmitAsm("LD_HL_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("LD_HL_A"); // total += count
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("LD_HL_A");
EmitAsm("JR_NC", valueDone);
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
EmitAsm("LD_HL_A");
EmitLabel(valueDone);
EmitAsm("JP", loop);
EmitLabel(finished);
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_E_A");
EmitAsm("INC_HL");
EmitAsm("LD_A_HL");
EmitAsm("LD_D_A");
EmitAsm("LD_H_D");
EmitAsm("LD_L_E");
EmitAsm("ADD_SP_IMM", new AsmOperand(10, AddressMode.Relative));
LastCallReturnsHL = true;
return;
}__rng8
Avanza una vez el generador pseudoaleatorio GB compartido y devuelve su nuevo byte alto. Usa esta función intrínseca si necesitas un byte sin enlazar las funciones envoltorio de rng.c.
Sintaxis
u8 __rng8();Valor devuelto
Un resultado u8 (0–255) tras avanzar una vez el generador compartido. Esto describe el tipo y los límites del valor, no que todos los bytes tengan la misma probabilidad.
Condiciones y límites
El paso GB suma 0x5D17 al estado de 16 bits con desbordamiento circular, aplica XOR al byte bajo actualizado con el alto y después suma ese byte bajo a una rotación de un bit a la izquierda del byte alto. Guarda ambos bytes actualizados y devuelve el alto.
Se acepta el estado cero. En la siguiente extracción de byte, el generador GB lo sustituye por 0x5AA5 antes de avanzar; por eso __rng_seed(0) seguido de __rng8() devuelve 122. Volver a sembrar repetidamente reinicia la secuencia en lugar de añadir aleatoriedad.
Estas llamadas comparten estado persistente. Para reproducir la secuencia hacen falta el mismo destino, semilla y orden de llamadas, incluidas las de otras funciones auxiliares. Mantén la semilla y las extracciones en un único contexto de ejecución; estas comprobaciones no demuestran que sea seguro usarlas simultáneamente desde una interrupción.
GB y FC usan algoritmos distintos y no prometen la misma secuencia con la misma semilla. Son generadores deterministas para juegos, no fuentes de aleatoriedad criptográfica; las pruebas comprueban el comportamiento de la implementación, no su calidad estadística ni su período.
Incluye rpg.h y compila lib/rng.c junto al juego cuando uses las funciones de biblioteca. Las intrínsecas __rng_seed y __rng8 las genera el compilador; un programa que solo use esas dos no necesita el objeto rng.c.
Ejemplo de uso
__rng_seed(0x1234);
first=__rng8(); // 2: the first draw from this seed on GB.
second=__rng8(); // 34: a second draw advances the same shared state.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo muestra que cada extracción avanza la secuencia. Tras la semilla 0x1234, las dos primeras llamadas __rng8 muestran FIRST 00002 y SECOND 00034. Repetir la semilla reinicia en 00002.
El programa completo muestra resultados decimales sobre blanco: texto negro en DMG y azul en CGB. FAILED debe indicar 00000. Las imágenes muestran la salida real del ejemplo en el emulador.
La verificación comparó por caso 32 salidas y ocho extracciones de byte posteriores con un modelo independiente en el equipo anfitrión: 93 casos GB y 18 FC, incluidos semillas constantes o en tiempo de ejecución y límites GB de rango, probabilidad y pesos. Las pantallas didácticas se comprobaron tras 240 fotogramas del emulador. No se probaron hardware físico ni calidad estadística.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:149
kitaqgb/kitaqgb/CodeGenerator.cs:8881
if (funcName == "__rng8")
{
if (args.Length != 0) Error(expr, "__rng8() expects 0 arguments");
EmitLoadA(RngStateLoVar);
EmitAsm("LD_B_A");
EmitLoadA(RngStateHiVar);
EmitAsm("LD_C_A");
// Zero state fallback for deterministic startup.
AsmOperand rngSeeded = MakeUniqueLabel("rng_seeded");
EmitAsm("LD_A_B");
EmitAsm("OR_C");
EmitAsm("JR_NZ", rngSeeded);
EmitAsm("LD_A_IMM", new AsmOperand(0xA5, AddressMode.Immediate));
EmitAsm("LD_B_A");
EmitAsm("LD_A_IMM", new AsmOperand(0x5A, AddressMode.Immediate));
EmitAsm("LD_C_A");
EmitLabel(rngSeeded);
// 16-bit mixed add/xor/rotate step.
EmitAsm("LD_A_B");
EmitAsm("ADD_A_IMM", new AsmOperand(0x17, AddressMode.Immediate));
EmitAsm("LD_B_A");
EmitAsm("LD_A_C");
EmitAsm("ADC_IMM", new AsmOperand(0x5D, AddressMode.Immediate));
EmitAsm("LD_C_A");
EmitAsm("LD_A_B");
EmitAsm("XOR_C");
EmitAsm("LD_B_A");
EmitAsm("LD_A_C");
EmitAsm("RLCA");
EmitAsm("ADD_B");
EmitAsm("LD_C_A");
EmitAsm("LD_A_B");
EmitStoreA(RngStateLoVar);
EmitAsm("LD_A_C");
EmitStoreA(RngStateHiVar);
EmitAsm("LD_A_C");
return;
}__rng_seed
Sustituye el estado de 16 bits del generador GB compartido. Usa una semilla fija para reproducir la misma secuencia durante la depuración. Establecer la semilla no extrae ningún valor aleatorio.
Sintaxis
void __rng_seed(u16 seed);Parámetros
seedSemilla de 16 bits sin signo, de 0 a 65535. Los dos bytes se almacenan tal como se reciben. Elige y registra la semilla antes de la secuencia de extracciones del juego que quieras reproducir.
Valor devuelto
Ninguno (void).
Condiciones y límites
Se acepta el estado cero. En la siguiente extracción de byte, el generador GB lo sustituye por 0x5AA5 antes de avanzar; por eso __rng_seed(0) seguido de __rng8() devuelve 122. Volver a sembrar repetidamente reinicia la secuencia en lugar de añadir aleatoriedad.
Estas llamadas comparten estado persistente. Para reproducir la secuencia hacen falta el mismo destino, semilla y orden de llamadas, incluidas las de otras funciones auxiliares. Mantén la semilla y las extracciones en un único contexto de ejecución; estas comprobaciones no demuestran que sea seguro usarlas simultáneamente desde una interrupción.
GB y FC usan algoritmos distintos y no prometen la misma secuencia con la misma semilla. Son generadores deterministas para juegos, no fuentes de aleatoriedad criptográfica; las pruebas comprueban el comportamiento de la implementación, no su calidad estadística ni su período.
Incluye rpg.h y compila lib/rng.c junto al juego cuando uses las funciones de biblioteca. Las intrínsecas __rng_seed y __rng8 las genera el compilador; un programa que solo use esas dos no necesita el objeto rng.c.
Ejemplo de uso
__rng_seed(0x1234);
repeated=__rng8(); // Reseeding reproduces the first byte, 2.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo muestra cómo reiniciar una secuencia de prueba. Con semilla 0x1234, FIRST y RESEEDED muestran 00002 y SECOND muestra 00034. La semilla de biblioteca reinicia el mismo estado, por lo que RNG8 también muestra 00002.
El programa completo muestra resultados decimales sobre blanco: texto negro en DMG y azul en CGB. FAILED debe indicar 00000. Las imágenes muestran la salida real del ejemplo en el emulador.
La verificación comparó por caso 32 salidas y ocho extracciones de byte posteriores con un modelo independiente en el equipo anfitrión: 93 casos GB y 18 FC, incluidos semillas constantes o en tiempo de ejecución y límites GB de rango, probabilidad y pesos. Las pantallas didácticas se comprobaron tras 240 fotogramas del emulador. No se probaron hardware físico ni calidad estadística.


Comando de compilación
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-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:150
kitaqgb/kitaqgb/CodeGenerator.cs:8868
if (funcName == "__rng_seed")
{
if (args.Length != 1) Error(expr, "__rng_seed(seed) expects 1 argument");
CompileIntoHL(args[0]);
EmitAsm("LD_A_L");
EmitStoreA(RngStateLoVar);
EmitAsm("LD_A_H");
EmitStoreA(RngStateHiVar);
return;
}__scroll_bg_set
Escribe SCX/SCY del fondo de inmediato y sincroniza los valores actuales y pendientes del software.
Sintaxis
void __scroll_bg_set(u8 scx, u8 scy);Parámetros
scxDesplazamiento horizontal de 0 a 255 píxeles.
scyDesplazamiento vertical de 0 a 255 píxeles.
Valor devuelto
No devuelve ningún valor.
Condiciones y límites
Borra el indicador de actualización pendiente del fondo. Las escrituras a mitad de fotograma afectan al barrido posterior; normalmente se llama durante VBlank.
Ejemplo de uso
__scroll_bg_set(19,27);result[5]=*((u8*)0xFF43);result[6]=*((u8*)0xFF42);__scroll_bg_set(0,0);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:151
kitaqgb/kitaqgb/CodeGenerator.cs:7507
if (funcName == "__scroll_bg_set")
{
if (args.Length != 2) Error(expr, "__scroll_bg_set(scx, scy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_B_A");
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitStoreA(ScrollBgXCurVar);
EmitStoreA(ScrollBgXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
EmitAsm("LD_A_B");
EmitStoreA(ScrollBgYCurVar);
EmitStoreA(ScrollBgYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__settile_col
Copia verticalmente len números de tile consecutivos de src, desde (x,y) en el mapa BG seleccionado por LCDC. El origen avanza un byte y el destino 32 por tile.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile_col(u8 x, u8 y, const u8* src, u8 len);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
lenCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
Para copiar horizontalmente, mantén x+len <= 32; verticalmente, y+len <= 32. No hay vuelta automática al llegar al borde del mapa.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_col
// Copy `len` successive tile numbers from `src` vertically, starting at `(x,y)` in the
// LCDC-selected BG map. The source advances one byte and the destination 32 bytes per
// tile. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_col(2, 3, example_tiles, 3);
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9CA2, 0) != 'C') { failures++; }
if (tile_example_read(0x9C63, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A
B
C

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_col.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_col.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:156
__settile_rect
Rellena un rectángulo de w por h en el mapa BG seleccionado por LCDC con un solo número de tile. Las filas están separadas por 32 bytes; repite un tile, no copia una matriz de imágenes.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
w, hAncho y alto en tiles de 8×8. Mantén
x+w <= 32ey+h <= 32. Si alguna dimensión es cero, no se escribe nada.tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_rect
// Fill a `w` by `h` rectangle in the LCDC-selected BG map with one tile number. Rows are
// 32 bytes apart; this repeats a tile, rather than copying an image array. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_rect(2, 3, 3, 2, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9C84, 0) != 'A') { failures++; }
if (tile_example_read(0x9C65, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
AAA
AAA

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:154
__settile_row
Copia horizontalmente len números de tile consecutivos de src, desde (x,y) en el mapa BG seleccionado por LCDC. Ambos punteros avanzan un byte por tile.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile_row(u8 x, u8 y, const u8* src, u8 len);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
lenCantidad de bytes, de 0 a 255. Cero no copia nada. El origen debe contener al menos esa cantidad de bytes legibles.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
Para copiar horizontalmente, mantén x+len <= 32; verticalmente, y+len <= 32. No hay vuelta automática al llegar al borde del mapa.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_row
// Copy `len` successive tile numbers from `src` horizontally, starting at `(x,y)` in the
// LCDC-selected BG map. Source and destination advance by one byte per tile. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_row(2, 3, example_tiles, 3);
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9C64, 0) != 'C') { failures++; }
if (tile_example_read(0x9C82, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_row.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_row.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:155
__settile_xy
Escribe el número de tile de una celda del mapa; selecciona una imagen existente, sin modificar sus píxeles.
El bit 3 de LCDC elige el mapa de fondo: cero usa 0x9800 y uno 0x9C00. Se suma 32*y + x a esa base.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settile_xy(u8 x, u8 y, u8 tile);Parámetros
x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
tileNúmero de tile, de 0 a 255. El bit 4 de LCDC determina cómo se localiza su imagen. No es una dirección de byte de VRAM.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settile_xy
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settile_xy(2, 3, 'A');
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9862, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
A

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_xy.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_xy.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:153
__settilemap_rect
Copia una matriz compacta de w*h números de tile a un rectángulo en (x,y) del mapa elegido por base. Las filas de origen tienen w bytes; el destino usa un paso de 32 bytes.
Espera a que la VRAM permita escribir. Con la LCD activa, controla las interrupciones y puede habilitarlas al terminar; no garantiza conservar su estado previo ni acabar en un solo VBlank.
Sintaxis
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);Parámetros
basebaseelige el mapa (0x9800o0x9C00); la dirección del byte esbase + 32*y + x.x, yCoordenadas en tiles de 8×8, de 0 a 31. Toda la escritura debe quedar dentro del mapa de 32×32; no se recortan las coordenadas.
w, hAncho y alto en tiles de 8×8. Mantén
x+w <= 32ey+h <= 32. Si alguna dimensión es cero, no se escribe nada.srcBytes contiguos en RAM o en un banco ROM visible. La secuencia de tiles contiene números; la de atributos, bytes de atributos CGB. La copia es inmediata y no conserva el puntero.
Valor devuelto
Ninguno (void).
Condiciones y límites
Los bytes se escriben en el banco VRAM seleccionado. En CGB, elige el banco 0 para los números de tile; la llamada no cambia de banco.
El ejemplo usa gb_common.h: m_init() carga la fuente ASCII incluida con los códigos de carácter como números de tile. m_wait(); M_LCDC=0; apaga el LCD durante VBlank para las transferencias sin espera. El valor final de LCDC elige el mapa visible.
Ejemplo de uso
// __settilemap_rect
// Copy a tightly packed `w*h` array of tile numbers to a rectangle at `(x,y)` in the map
// selected by `base`. Source rows have `w` bytes; destination rows have a 32-byte
// stride. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C','D','E','F'};
void main() {
u8 failures;
failures = 0;
tile_color_example_begin();
__settilemap_rect(0x9C00, 2, 3, 3, 2, example_tiles);
if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
if (tile_example_read(0x9C84, 0) != 'F') { failures++; }
if (tile_example_read(0x9C65, 0) != 0) { failures++; }
tile_example_finish(failures, 1);
}Resultado esperado
El mapa elegido muestra los caracteres escritos o el área rellenada. El programa lee los bytes de tiles y, en CGB, los atributos; FAILED CHECKS 000 indica que las comparaciones coinciden. Ambos mapas empiezan a cero y, antes de la llamada, BG y ventana seleccionan 0x9C00, lo que permite distinguir el mapa fijo del seleccionado. Las escrituras se hacen con el LCD apagado; no se mide la temporización con el LCD encendido.
En CGB, los caracteres del ejemplo y el texto de resultados aparecen en azul sobre blanco. La preparación asigna el azul al color de tinta de la paleta 0, sin modificar los atributos del mapa de tiles. En DMG, los mismos caracteres aparecen en negro sobre blanco.
Imagen esperada: el patrón siguiente empieza en las coordenadas de tile (x=2, y=3), equivalentes al píxel (16,24). Cada carácter ocupa un tile de 8×8. Lee de arriba abajo y de izquierda a derecha; los tiles circundantes quedan vacíos.
ABC
DEF

Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilemap_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilemap_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:158
__tile_addr
Calcula la dirección de CPU de una celda en un mapa de 32 tiles de ancho.
Sintaxis
u16 __tile_addr(u16 base, u8 x, u8 y);Parámetros
baseBase del mapa de tiles, normalmente $9800/$9C00.
xColumna, normalmente de 0 a 31.
yFila, normalmente de 0 a 31.
Valor devuelto
Devuelve (base + y*32 + x) módulo 65536 sin acceder a memoria.
Ejemplo de uso
result[4]=__tile_addr(0x9800,3,2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:180
kitaqgb/kitaqgb/CodeGenerator.cs:8355
if (funcName == "__tile_addr")
{
if (args.Length != 3) Error(expr, "__tile_addr(base, x, y) expects 3 arguments");
EmitTileMapAddressIntoHL(args[0], args[1], args[2]);
LastCallReturnsHL = true;
return;
}__vram_copy_dma
__vram_copy_dma es un alias del compilador para __vram_memcpy. Usa el mismo recuento en bytes y la misma transferencia inmediata por CPU, incluidas las reglas de acceso al LCD.
Copia inmediatamente len bytes de src a dst en orden creciente de direcciones. Vuelve al terminar; no encola el trabajo. Ambas direcciones avanzan un byte cada vez, sin saltos de fila del mapa ni conversión del formato gráfico.
Sintaxis
void __vram_copy_dma(u16 dst, const void* src, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.srcPuntero a los bytes de origen, válido y mapeado durante toda la llamada. Los datos se leen inmediatamente, sin conservarlos para un flush posterior. No se cambia de banco automáticamente. Usa rangos sin solapamiento: esta copia hacia delante no ofrece la semántica de memmove.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
El nombre no selecciona un motor DMA ni programa trabajo para otro HBlank. La llamada espera hasta terminar. Con el LCD activo, el mismo sondeo por CPU acepta el modo 0 (HBlank) o 1 (VBlank); no se configura HDMA de CGB.
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_copy_dma(0x98A2,strip,3); // This alias also uses CPU writes, not a DMA engine.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_copy_dma para escribir tres bytes del mapa en la casilla (2,5), píxel (16,40). Deben aparecer [1,2,3] de izquierda a derecha en un área de 24 × 8, con las casillas vecinas vacías. El programa completo sitúa cada copia o relleno probado en su propia fila.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:163
__vram_copy_hblank
__vram_copy_hblank es un alias del compilador para __vram_memcpy. Usa el mismo recuento en bytes y la misma transferencia inmediata por CPU, incluidas las reglas de acceso al LCD.
Copia inmediatamente len bytes de src a dst en orden creciente de direcciones. Vuelve al terminar; no encola el trabajo. Ambas direcciones avanzan un byte cada vez, sin saltos de fila del mapa ni conversión del formato gráfico.
Sintaxis
void __vram_copy_hblank(u16 dst, const void* src, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.srcPuntero a los bytes de origen, válido y mapeado durante toda la llamada. Los datos se leen inmediatamente, sin conservarlos para un flush posterior. No se cambia de banco automáticamente. Usa rangos sin solapamiento: esta copia hacia delante no ofrece la semántica de memmove.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
El nombre no selecciona un motor DMA ni programa trabajo para otro HBlank. La llamada espera hasta terminar. Con el LCD activo, el mismo sondeo por CPU acepta el modo 0 (HBlank) o 1 (VBlank); no se configura HDMA de CGB.
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_copy_hblank(0x9882,strip,3); // Immediate safe copy; no scheduled HBlank DMA.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_copy_hblank para escribir tres bytes del mapa en la casilla (2,4), píxel (16,32). Deben aparecer [1,2,3] de izquierda a derecha en un área de 24 × 8, con las casillas vecinas vacías. El programa completo sitúa cada copia o relleno probado en su propia fila.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:162
__vram_fill
__vram_fill es un alias del compilador para __vram_memset. Usa el mismo recuento en bytes y la misma transferencia inmediata por CPU, incluidas las reglas de acceso al LCD.
Rellena inmediatamente len bytes consecutivos desde dst con el byte value. Repite un byte, no un patrón gráfico completo. En el mapa se repite un índice de patrón; en los datos gráficos se modifican los planos de bits de los píxeles.
Sintaxis
void __vram_fill(u16 dst, u8 value, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.valueUn byte de 0 a 255 que se repite. Su efecto visible depende de si el destino contiene índices de tiles, planos de píxeles u otra estructura de bytes.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_fill(0x9902,1,computed_length());Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_fill para rellenar tres bytes del mapa desde la casilla (2,8), píxel (16,64), con el índice 1. Se espera una barra sólida de 24 × 8 con casillas vecinas vacías. El recuento procede de variables o del resultado de una función; los tres bytes deben conservar el valor 1.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:161
__vram_memcpy
Copia inmediatamente len bytes de src a dst en orden creciente de direcciones. Vuelve al terminar; no encola el trabajo. Ambas direcciones avanzan un byte cada vez, sin saltos de fila del mapa ni conversión del formato gráfico.
Sintaxis
void __vram_memcpy(u16 dst, const void* src, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.srcPuntero a los bytes de origen, válido y mapeado durante toda la llamada. Los datos se leen inmediatamente, sin conservarlos para un flush posterior. No se cambia de banco automáticamente. Usa rangos sin solapamiento: esta copia hacia delante no ofrece la semántica de memmove.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_memcpy(0x9842,strip,length_base+length_extra); // Three map bytes at tile (2,2).Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_memcpy para escribir tres bytes del mapa en la casilla (2,2), píxel (16,16). Deben aparecer [1,2,3] de izquierda a derecha en un área de 24 × 8, con las casillas vecinas vacías. El programa completo sitúa cada copia o relleno probado en su propia fila.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:146
__vram_memset
Rellena inmediatamente len bytes consecutivos desde dst con el byte value. Repite un byte, no un patrón gráfico completo. En el mapa se repite un índice de patrón; en los datos gráficos se modifican los planos de bits de los píxeles.
Sintaxis
void __vram_memset(u16 dst, u8 value, u16 len);Parámetros
dstDirección del primer byte de destino, normalmente en VRAM
0x8000–0x9FFF. Todo el intervalo debe ser válido. CGB usa el banco VRAM actual; en el mapa, el banco 0 contiene índices y el 1 atributos. Estas rutinas no eligen mapa mediante LCDC, saltan de fila ni asignan VRAM.valueUn byte de 0 a 255 que se repite. Su efecto visible depende de si el destino contiene índices de tiles, planos de píxeles u otra estructura de bytes.
lenNúmero de bytes consecutivos, de 0 a 65535, no de tiles. Mantén todo el rango dentro de origen válido y destino escribible; la API no comprueba límites ni evita el desbordamiento de direcciones de 16 bits.
Valor devuelto
Ninguno (void).
Condiciones y límites
Con el LCD encendido, cada escritura espera al modo STAT 0 o 1 con DI y termina con EI. Con el LCD apagado se usa un bucle normal de CPU. La llamada puede abarcar varios fotogramas, no conserva las interrupciones deshabilitadas al entrar y no configura registros DMA. La longitud cero no transfiere datos.
Ejemplo de uso
__vram_memset(0x98E2,1,length_base+length_extra); // Three solid tiles, not three tile patterns.Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Este ejemplo usa __vram_memset para rellenar tres bytes del mapa desde la casilla (2,7), píxel (16,56), con el índice 1. Se espera una barra sólida de 24 × 8 con casillas vecinas vacías. El recuento procede de variables o del resultado de una función; los tres bytes deben conservar el valor 1.
Los gráficos originales incluidos usan el patrón 1 para un cuadrado sólido de 8 × 8, el 2 para sus cuatro columnas izquierdas y el 3 para un triángulo alineado a la izquierda que crece un píxel por fila, de 1 a 8. DMG usa negro sobre blanco; CGB distingue las operaciones con colores y mantiene el texto negro.
El cuadrado en el píxel (16,88) debe sobrevivir a las llamadas seguras y sin espera de longitud cero. Con el LCD encendido, la copia y el relleno seguros añaden cuadrados en (16,96) y (32,96). FAILED CHECKS debe mostrar 000. Las pruebas de emulador comparan píxeles y contador. CGB usa el banco 0 en este ejemplo; aquí no se prueban otros bancos ni hardware físico.
En CGB, las filas desde MEMCPY hasta FILL UNSAFE repiten la secuencia rojo, azul y verde. El cuadrado de ZERO COUNT es rojo. En LCD ON, el izquierdo es azul y el derecho verde. El texto permanece negro. Las pruebas comprueban estos colores en cada píxel dibujado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/rpg.h:147
__wait_vblank
Con el LCD activo, espera a que termine el VBlank actual y después al siguiente LY≥144.
Sintaxis
void __wait_vblank();Valor devuelto
No devuelve ningún valor.
Condiciones y límites
Vuelve de inmediato si el LCD está apagado. Consulta LY sin activar interrupciones ni incrementar un contador de fotogramas de la biblioteca.
Ejemplo de uso
__wait_vblank();result[10]=*((u8*)0xFF44);M_LCDC=0;__wait_vblank();__wait_ly(200);result[11]=1;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Aprende selección de mapa, desplazamiento, anidamiento y esperas de línea. Las bases son 38912/39936; los tokens anidados, 0/1; los LY objetivo observados, 80/144. También comprueba que las esperas vuelven con el LCD apagado.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:138
kitaqgb/kitaqgb/CodeGenerator.cs:7451
if (funcName == "__wait_vblank")
{
if (args.Length != 0) Error(expr, "__wait_vblank() expects 0 arguments");
AsmOperand wbRet = MakeUniqueLabel("waitvb_ret");
AsmOperand wbExitVblank = MakeUniqueLabel("waitvb_exit");
AsmOperand wbEnterVblank = MakeUniqueLabel("waitvb_enter");
// If LCD is off, return immediately.
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
EmitAsm("JR_Z", wbRet);
// while (LY >= 144) {}
EmitLabel(wbExitVblank);
EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
EmitAsm("JR_C", wbEnterVblank);
EmitAsm("JR", wbExitVblank);
// while (LY < 144) {}
EmitLabel(wbEnterVblank);
EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
EmitAsm("JR_C", wbEnterVblank);
EmitLabel(wbRet);
return;
}__xy_in_rect
Comprueba si está dentro de un rectángulo, incluidos los bordes superior e izquierdo y excluidos el inferior y el derecho.
Sintaxis
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);Parámetros
xX del punto, de 0 a 255.
yY del punto, de 0 a 255.
rxBorde izquierdo del rectángulo, de 0 a 255.
ryBorde superior del rectángulo, de 0 a 255.
rwAncho de 0 a 255.
rhAlto de 0 a 255.
Valor devuelto
Devuelve 1 si está dentro y 0 si no. Un ancho o alto de cero representa un rectángulo vacío.
Condiciones y límites
Usa diferencias de coordenadas: un límite derecho superior a 255 no se desborda hacia la izquierda.
Ejemplo de uso
result[0]=__xy_in_rect(10,20,10,20,3,2);result[1]=__xy_in_rect(13,20,10,20,3,2);result[2]=__xy_in_rect(9,20,10,20,3,2);result[3]=__xy_in_rect(255,1,250,0,10,2);result[4]=__xy_in_rect(10,20,10,20,0,2);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Prueba el interior, los límites, un punto exterior y ancho cero: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Verifica la distancia 15, la distancia desbordada 254 y los índices de array 64/65280.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=noneImplementación y código fuente
kitaqgb/lib/rpg.h:182
kitaqgb/kitaqgb/CodeGenerator.cs:8436
if (funcName == "__xy_in_rect")
{
if (args.Length != 6) Error(expr, "__xy_in_rect(x, y, rx, ry, rw, rh) expects 6 arguments");
Expr[] foldedRectArgs = args.Select(FoldConstants).ToArray();
if (foldedRectArgs[0].Match(Tag.Integer, out int constX) &&
foldedRectArgs[1].Match(Tag.Integer, out int constY) &&
foldedRectArgs[2].Match(Tag.Integer, out int constRx) &&
foldedRectArgs[3].Match(Tag.Integer, out int constRy) &&
foldedRectArgs[4].Match(Tag.Integer, out int constRw) &&
foldedRectArgs[5].Match(Tag.Integer, out int constRh))
{
int x = constX & 0xFF;
int y = constY & 0xFF;
int rx = constRx & 0xFF;
int ry = constRy & 0xFF;
int rw = constRw & 0xFF;
int rh = constRh & 0xFF;
bool inside = x >= rx && y >= ry && (x - rx) < rw && (y - ry) < rh;
if (inside) EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
else EmitAsm("XOR_A");
return;
}
EmitPushExprAsWord(args[0]); // x
EmitPushExprAsWord(args[1]); // y
EmitPushExprAsWord(args[2]); // rx
EmitPushExprAsWord(args[3]); // ry
EmitPushExprAsWord(args[4]); // rw
EmitPushExprAsWord(args[5]); // rh
AsmOperand ret0 = MakeUniqueLabel("xyrect_ret0");
AsmOperand done = MakeUniqueLabel("xyrect_done");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_B_A"); // x
EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // rx
EmitAsm("LD_A_B");
EmitAsm("CP_C");
EmitAsm("JR_C", ret0);
EmitAsm("SUB_C");
EmitAsm("LD_B_A");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // rw
EmitAsm("LD_A_B");
EmitAsm("CP_C");
EmitAsm("JR_NC", ret0);
EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_B_A"); // y
EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // ry
EmitAsm("LD_A_B");
EmitAsm("CP_C");
EmitAsm("JR_C", ret0);
EmitAsm("SUB_C");
EmitAsm("LD_B_A");
EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
EmitAsm("LD_A_HL");
EmitAsm("LD_C_A"); // rh
EmitAsm("LD_A_B");
EmitAsm("CP_C");
EmitAsm("JR_NC", ret0);
EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
EmitAsm("JR", done);
EmitLabel(ret0);
EmitAsm("XOR_A");
EmitLabel(done);
EmitAsm("ADD_SP_IMM", new AsmOperand(12, AddressMode.Relative));
return;
}scroll.h — Desplazamiento del fondo, la ventana y las zonas divididas
Controla el desplazamiento inmediato o mediante búfer del fondo y la ventana y hasta ocho divisiones de raster. Distingue los valores actuales gestionados por las funciones de lectura, los registros de hardware activos y los valores pendientes.
__scroll_bg_add
Suma desplazamientos con signo a las coordenadas registradas del fondo y los aplica de inmediato.
Sintaxis
void __scroll_bg_add(s8 dx, s8 dy);Parámetros
dxDesplazamiento horizontal con signo en píxeles, de −128 a 127.
dyDesplazamiento vertical con signo en píxeles, de −128 a 127.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par. Los resultados vuelven al inicio módulo 256. Las sumas parten de los valores actuales registrados, no de los pendientes ni de los valores temporales de los registros de raster.
Ejemplo de uso
__scroll_bg_add(-20,3);result[22]=M_SCX;result[23]=M_SCY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:37
kitaqgb/kitaqgb/CodeGenerator.cs:7835
if (funcName == "__scroll_bg_add")
{
if (args.Length != 2) Error(expr, "__scroll_bg_add(dx, dy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitAsm("LD_B_L");
EmitLoadA(ScrollBgXCurVar);
EmitAsm("ADD_B");
EmitStoreA(ScrollBgXCurVar);
EmitStoreA(ScrollBgXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
EmitLoadA(ScrollBgYCurVar);
EmitAsm("ADD_C");
EmitStoreA(ScrollBgYCurVar);
EmitStoreA(ScrollBgYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_bg_set_buffered
Deja pendientes las dos coordenadas de desplazamiento del fondo para aplicarlas más tarde.
Sintaxis
void __scroll_bg_set_buffered(u8 scx, u8 scy);Parámetros
scxDesplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.
scyDesplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No cambia los valores actuales registrados ni los registros de hardware. Aplícalos con __scroll_flush o Scroll_Flush; dejarlos pendientes no espera a VBlank.
Ejemplo de uso
__scroll_bg_set_buffered(16,24);result[14]=M_SCX;result[15]=__scroll_bg_x_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:22
kitaqgb/kitaqgb/CodeGenerator.cs:7671
if (funcName == "__scroll_bg_set_buffered")
{
if (args.Length != 2) Error(expr, "__scroll_bg_set_buffered(scx, scy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_B_A");
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitStoreA(ScrollBgXNextVar);
EmitAsm("LD_A_B");
EmitStoreA(ScrollBgYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_bg_x_get
Lee el valor actual registrado del desplazamiento horizontal del fondo.
Sintaxis
u8 __scroll_bg_x_get();Valor devuelto
Devuelve el byte actual registrado, de 0 a 255. WX incluye su desplazamiento de hardware de siete píxeles.
Condiciones y límites
No es una lectura de un registro de hardware. No incluye los valores pendientes del búfer, las escrituras directas en registros ni los valores temporales aplicados por las divisiones de raster.
Ejemplo de uso
result[6]=__scroll_bg_x_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:13
kitaqgb/kitaqgb/CodeGenerator.cs:7571
if (funcName == "__scroll_bg_x_get")
{
if (args.Length != 0) Error(expr, "__scroll_bg_x_get() expects 0 arguments");
EnsureScrollState();
EmitLoadA(ScrollBgXCurVar);
return;
}__scroll_bg_x_set
Establece de inmediato el desplazamiento horizontal del fondo.
Sintaxis
void __scroll_bg_x_set(u8 scx);Parámetros
scxDesplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par.
Ejemplo de uso
__scroll_bg_x_set(250);result[4]=M_SCX;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:11
kitaqgb/kitaqgb/CodeGenerator.cs:7535
if (funcName == "__scroll_bg_x_set")
{
if (args.Length != 1) Error(expr, "__scroll_bg_x_set(scx) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollBgXCurVar);
EmitStoreA(ScrollBgXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
EmitLoadA(ScrollBgYCurVar);
EmitStoreA(ScrollBgYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_bg_x_set_buffered
Deja pendiente solo la X del fondo.
Sintaxis
void __scroll_bg_x_set_buffered(u8 scx);Parámetros
scxDesplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
Conserva el otro eje pendiente. Los valores actuales y los registros no cambian hasta aplicar el búfer; inicializa antes el par de coordenadas.
Ejemplo de uso
__scroll_bg_x_set_buffered(32);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:23
kitaqgb/kitaqgb/CodeGenerator.cs:7693
if (funcName == "__scroll_bg_x_set_buffered")
{
if (args.Length != 1) Error(expr, "__scroll_bg_x_set_buffered(scx) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollBgXNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_bg_y_get
Lee el valor actual registrado del desplazamiento vertical del fondo.
Sintaxis
u8 __scroll_bg_y_get();Valor devuelto
Devuelve el byte actual registrado, de 0 a 255. WX incluye su desplazamiento de hardware de siete píxeles.
Condiciones y límites
No es una lectura de un registro de hardware. No incluye los valores pendientes del búfer, las escrituras directas en registros ni los valores temporales aplicados por las divisiones de raster.
Ejemplo de uso
result[7]=__scroll_bg_y_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:14
kitaqgb/kitaqgb/CodeGenerator.cs:7580
if (funcName == "__scroll_bg_y_get")
{
if (args.Length != 0) Error(expr, "__scroll_bg_y_get() expects 0 arguments");
EnsureScrollState();
EmitLoadA(ScrollBgYCurVar);
return;
}__scroll_bg_y_set
Establece de inmediato el desplazamiento vertical del fondo.
Sintaxis
void __scroll_bg_y_set(u8 scy);Parámetros
scyDesplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par.
Ejemplo de uso
__scroll_bg_y_set(2);result[5]=M_SCY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:12
kitaqgb/kitaqgb/CodeGenerator.cs:7553
if (funcName == "__scroll_bg_y_set")
{
if (args.Length != 1) Error(expr, "__scroll_bg_y_set(scy) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollBgYCurVar);
EmitStoreA(ScrollBgYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
EmitLoadA(ScrollBgXCurVar);
EmitStoreA(ScrollBgXNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_bg_y_set_buffered
Deja pendiente solo la Y del fondo.
Sintaxis
void __scroll_bg_y_set_buffered(u8 scy);Parámetros
scyDesplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
Conserva el otro eje pendiente. Los valores actuales y los registros no cambian hasta aplicar el búfer; inicializa antes el par de coordenadas.
Ejemplo de uso
__scroll_bg_y_set_buffered(40);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:24
kitaqgb/kitaqgb/CodeGenerator.cs:7707
if (funcName == "__scroll_bg_y_set_buffered")
{
if (args.Length != 1) Error(expr, "__scroll_bg_y_set_buffered(scy) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollBgYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_flush
Aplica las coordenadas modificadas del fondo y la ventana al estado registrado y al hardware, y borra sus indicadores de actualización pendiente.
Sintaxis
void __scroll_flush();Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
Los pares de coordenadas sin cambios se dejan intactos. No enmascara interrupciones ni espera a VBlank; el código que llama debe organizar la sincronización necesaria.
Ejemplo de uso
__scroll_flush();result[18]=M_SCX;result[19]=M_SCY;result[20]=M_WX;result[21]=M_WY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:28
kitaqgb/kitaqgb/CodeGenerator.cs:7771
if (funcName == "__scroll_flush")
{
if (args.Length != 0) Error(expr, "__scroll_flush() expects 0 arguments");
EnsureScrollState();
AsmOperand flushSkipBg = MakeUniqueLabel("scroll_flush_skip_bg");
AsmOperand flushSkipWin = MakeUniqueLabel("scroll_flush_skip_win");
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
EmitAsm("JR_Z", flushSkipBg);
EmitLoadA(ScrollBgXNextVar);
EmitStoreA(ScrollBgXCurVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
EmitLoadA(ScrollBgYNextVar);
EmitStoreA(ScrollBgYCurVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
EmitLabel(flushSkipBg);
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
EmitAsm("JR_Z", flushSkipWin);
EmitLoadA(ScrollWinXNextVar);
EmitStoreA(ScrollWinXCurVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
EmitLoadA(ScrollWinYNextVar);
EmitStoreA(ScrollWinYCurVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
EmitLabel(flushSkipWin);
EmitAsm("XOR_A");
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_split_commit
Guarda la visibilidad base de la ventana y prepara el primer evento de línea de la tabla de divisiones.
Sintaxis
void __scroll_split_commit();Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
La tabla compartida admite ocho entradas. Añadirlas no las aplica ni las ordena, y las que exceden la capacidad se ignoran. Reconstrúyela con reset→push→commit en un límite de fotograma seguro y separa los eventos lo suficiente para que el manejador termine. La latencia de la interrupción puede hacer que la línea programada y el primer píxel afectado no coincidan. Una tabla no vacía activa VBlank/STAT en IE y ejecuta EI. Una tabla vacía desactiva la fuente LYC, pero no restaura IE ni el estado de interrupciones de la CPU.
Ejemplo de uso
__scroll_split_commit();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Divide la pantalla en tres franjas de desplazamiento con BG X=0→8→16. Aparecen tres rectángulos de 24×16 en (32,16), (24,80) y (16,112). Los eventos se programan en LY=64 y 104. Los rectángulos son negros en DMG y rojos en CGB.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kImplementación y código fuente
kitaqgb/lib/scroll.h:33
kitaqgb/kitaqgb/CodeGenerator.cs:7959
if (funcName == "__scroll_split_commit")
{
if (args.Length != 0) Error(expr, "__scroll_split_commit() expects 0 arguments");
EnsureScrollState();
UseScrollSplitHelpers = true;
EmitAsm("CALL", new AsmOperand("__kq_scroll_split_commit_core", AddressMode.Absolute));
return;
}
void AppendScrollHelpersIfUsed(List<Expr> rawLines)
{
bool needScrollHelpers = UseScrollSplitHelpers;
if (!needScrollHelpers)
{
foreach (var e in rawLines)
{
string m;
AsmOperand o;
if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
string target = o.Base.Value;
if (target == "__kq_scroll_split_reset_core" ||
target == "__kq_scroll_split_push_core" ||
target == "__kq_scroll_split_commit_core")
{
needScrollHelpers = true;
break;
}
}
}
if (!needScrollHelpers) return;
EnsureScrollState();
var injected = new List<Expr>();
injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));
// Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
string resetDone = "__kq_scroll_split_reset_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, resetDone));
injected.Add(Expr.MakeAsm("RET"));
// Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
string pushHaveRoom = "__kq_scroll_split_push_have_room";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("RET"));
// Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
string schedDisable = "__kq_scroll_schedule_next_disable";
string schedDisabled = "__kq_scroll_schedule_next_disabled";
string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, schedDisable));
// Do not rewrite a disabled STAT source: on DMG even writing zero
// can raise a fresh request and repeatedly re-enter the STAT handler.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, schedDisabled));
injected.Add(Expr.MakeAsm("RET"));
// Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
string applySkipWin = "__kq_scroll_apply_entry_skip_win";
string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
string applyDone = "__kq_scroll_apply_entry_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
injected.Add(Expr.MakeAsm("INC_HL")); // scx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // scy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_C_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_D_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // flags
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_H_A"));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.Make(Tag.Label, applySkipBg));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_D"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_E"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.Make(Tag.Label, applySkipWin));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipHide));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
// CGB palette data writes are blocked during pixel transfer. Wait through
// OAM search as well, so both color bytes fit in the following HBlank.
// The LY-zero entry runs in VBlank and does not wait here.
injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
injected.Add(Expr.Make(Tag.Label, applyDone));
injected.Add(Expr.MakeAsm("RET"));
// Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
string commitDisable = "__kq_scroll_split_commit_disable";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("EI"));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, commitDisable));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
// Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
// A first entry at LY zero is applied immediately before scheduling the following entry.
string vbDisable = "__kq_vblank_disable";
string vbFirstNonZero = "__kq_vblank_first_nonzero";
string vbDone = "__kq_vblank_done";
injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
// Set the existing fixed-address frame-ready flag used by the VBlank path.
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("OR_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDisable));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
// Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
string statDisable = "__kq_stat_disable";
string statDone = "__kq_stat_done";
injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
// DMG STAT writes can request an interrupt unrelated to LYC. Consume
// this entry only on its scheduled line; leave the index intact otherwise.
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, statDisable));
// Acknowledge an already-disabled source without writing STAT again.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, statDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
int insertAt = 0;
while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
rawLines.InsertRange(insertAt, injected);
}__scroll_split_push
Añade una división que cambia el desplazamiento del fondo en el evento de línea de barrido indicado.
Sintaxis
void __scroll_split_push(u8 ly, u8 scx, u8 scy);Parámetros
lyNúmero de línea del evento; usa 0–143 para eventos visibles y añádelos en orden ascendente. Una primera entrada en cero se aplica durante VBlank.
scxDesplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.
scyDesplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
La tabla compartida admite ocho entradas. Añadirlas no las aplica ni las ordena, y las que exceden la capacidad se ignoran. Reconstrúyela con reset→push→commit en un límite de fotograma seguro y separa los eventos lo suficiente para que el manejador termine. La latencia de la interrupción puede hacer que la línea programada y el primer píxel afectado no coincidan.
Ejemplo de uso
__scroll_split_push(64,8,0);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Divide la pantalla en tres franjas de desplazamiento con BG X=0→8→16. Aparecen tres rectángulos de 24×16 en (32,16), (24,80) y (16,112). Los eventos se programan en LY=64 y 104. Los rectángulos son negros en DMG y rojos en CGB.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kImplementación y código fuente
kitaqgb/lib/scroll.h:31
kitaqgb/kitaqgb/CodeGenerator.cs:7907
if (funcName == "__scroll_split_push")
{
if (args.Length != 3) Error(expr, "__scroll_split_push(ly, scx, scy) expects 3 arguments");
EnsureScrollState();
UseScrollSplitHelpers = true;
CompileIntoA(args[0]);
EmitStoreA(ScrollSplitTmpLyVar);
CompileIntoA(args[1]);
EmitStoreA(ScrollSplitTmpScxVar);
CompileIntoA(args[2]);
EmitStoreA(ScrollSplitTmpScyVar);
EmitAsm("XOR_A");
EmitStoreA(ScrollSplitTmpWxVar);
EmitStoreA(ScrollSplitTmpWyVar);
EmitAsm("LD_A_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate));
EmitStoreA(ScrollSplitTmpFlagsVar);
EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
return;
}
void AppendScrollHelpersIfUsed(List<Expr> rawLines)
{
bool needScrollHelpers = UseScrollSplitHelpers;
if (!needScrollHelpers)
{
foreach (var e in rawLines)
{
string m;
AsmOperand o;
if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
string target = o.Base.Value;
if (target == "__kq_scroll_split_reset_core" ||
target == "__kq_scroll_split_push_core" ||
target == "__kq_scroll_split_commit_core")
{
needScrollHelpers = true;
break;
}
}
}
if (!needScrollHelpers) return;
EnsureScrollState();
var injected = new List<Expr>();
injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));
// Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
string resetDone = "__kq_scroll_split_reset_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, resetDone));
injected.Add(Expr.MakeAsm("RET"));
// Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
string pushHaveRoom = "__kq_scroll_split_push_have_room";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("RET"));
// Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
string schedDisable = "__kq_scroll_schedule_next_disable";
string schedDisabled = "__kq_scroll_schedule_next_disabled";
string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, schedDisable));
// Do not rewrite a disabled STAT source: on DMG even writing zero
// can raise a fresh request and repeatedly re-enter the STAT handler.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, schedDisabled));
injected.Add(Expr.MakeAsm("RET"));
// Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
string applySkipWin = "__kq_scroll_apply_entry_skip_win";
string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
string applyDone = "__kq_scroll_apply_entry_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
injected.Add(Expr.MakeAsm("INC_HL")); // scx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // scy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_C_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_D_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // flags
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_H_A"));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.Make(Tag.Label, applySkipBg));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_D"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_E"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.Make(Tag.Label, applySkipWin));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipHide));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
// CGB palette data writes are blocked during pixel transfer. Wait through
// OAM search as well, so both color bytes fit in the following HBlank.
// The LY-zero entry runs in VBlank and does not wait here.
injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
injected.Add(Expr.Make(Tag.Label, applyDone));
injected.Add(Expr.MakeAsm("RET"));
// Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
string commitDisable = "__kq_scroll_split_commit_disable";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("EI"));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, commitDisable));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
// Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
// A first entry at LY zero is applied immediately before scheduling the following entry.
string vbDisable = "__kq_vblank_disable";
string vbFirstNonZero = "__kq_vblank_first_nonzero";
string vbDone = "__kq_vblank_done";
injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
// Set the existing fixed-address frame-ready flag used by the VBlank path.
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("OR_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDisable));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
// Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
string statDisable = "__kq_stat_disable";
string statDone = "__kq_stat_done";
injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
// DMG STAT writes can request an interrupt unrelated to LYC. Consume
// this entry only on its scheduled line; leave the index intact otherwise.
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, statDisable));
// Acknowledge an already-disabled source without writing STAT again.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, statDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
int insertAt = 0;
while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
rawLines.InsertRange(insertAt, injected);
}__scroll_split_push_ex
Añade una división cuyos indicadores seleccionan operaciones de fondo, ventana, visibilidad o color cero del fondo.
Sintaxis
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);Parámetros
lyNúmero de línea del evento; usa 0–143 para eventos visibles y añádelos en orden ascendente. Una primera entrada en cero se aplica durante VBlank.
scxDesplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.
scyDesplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.
wxByte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.
wyParte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.
flagsBits de operación: BG=1, posición de ventana=2, mostrar=4, ocultar=8, paleta de fondo CGB 0/color 0=16. Se descartan los bits desconocidos. El modo de color cero reutiliza scx/scy como bytes bajo/alto de RGB15.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
La tabla compartida admite ocho entradas. Añadirlas no las aplica ni las ordena, y las que exceden la capacidad se ignoran. Reconstrúyela con reset→push→commit en un límite de fotograma seguro y separa los eventos lo suficiente para que el manejador termine. La latencia de la interrupción puede hacer que la línea programada y el primer píxel afectado no coincidan. Mostrar tiene prioridad si están activados ambos bits de visibilidad. El color cero es exclusivo de CGB; al combinarlo con el movimiento de BG, los mismos bytes scx/scy se usan para ambas operaciones.
Ejemplo de uso
__scroll_split_push_ex(104,16,0,7,0,KQ_SCROLL_SPLIT_USE_BG|KQ_SCROLL_SPLIT_WIN_HIDE);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Divide la pantalla en tres franjas de desplazamiento con BG X=0→8→16. Aparecen tres rectángulos de 24×16 en (32,16), (24,80) y (16,112). Los eventos se programan en LY=64 y 104. Los rectángulos son negros en DMG y rojos en CGB.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kImplementación y código fuente
kitaqgb/lib/scroll.h:32
kitaqgb/kitaqgb/CodeGenerator.cs:7929
if (funcName == "__scroll_split_push_ex")
{
if (args.Length != 6) Error(expr, "__scroll_split_push_ex(ly, scx, scy, wx, wy, flags) expects 6 arguments");
EnsureScrollState();
UseScrollSplitHelpers = true;
if (TryGetU8Const(args[5], out int splitFlagsConst))
{
int unknownBits = splitFlagsConst & ~ScrollSplitFlagMask;
if (unknownBits != 0)
Warning(args[5], string.Format("__scroll_split_push_ex() ignores unknown flag bits 0x{0:X2}", unknownBits));
}
CompileIntoA(args[0]);
EmitStoreA(ScrollSplitTmpLyVar);
CompileIntoA(args[1]);
EmitStoreA(ScrollSplitTmpScxVar);
CompileIntoA(args[2]);
EmitStoreA(ScrollSplitTmpScyVar);
CompileIntoA(args[3]);
EmitStoreA(ScrollSplitTmpWxVar);
CompileIntoA(args[4]);
EmitStoreA(ScrollSplitTmpWyVar);
CompileIntoA(args[5]);
EmitStoreA(ScrollSplitTmpFlagsVar);
EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
return;
}
void AppendScrollHelpersIfUsed(List<Expr> rawLines)
{
bool needScrollHelpers = UseScrollSplitHelpers;
if (!needScrollHelpers)
{
foreach (var e in rawLines)
{
string m;
AsmOperand o;
if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
string target = o.Base.Value;
if (target == "__kq_scroll_split_reset_core" ||
target == "__kq_scroll_split_push_core" ||
target == "__kq_scroll_split_commit_core")
{
needScrollHelpers = true;
break;
}
}
}
if (!needScrollHelpers) return;
EnsureScrollState();
var injected = new List<Expr>();
injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));
// Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
string resetDone = "__kq_scroll_split_reset_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, resetDone));
injected.Add(Expr.MakeAsm("RET"));
// Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
string pushHaveRoom = "__kq_scroll_split_push_have_room";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("RET"));
// Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
string schedDisable = "__kq_scroll_schedule_next_disable";
string schedDisabled = "__kq_scroll_schedule_next_disabled";
string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, schedDisable));
// Do not rewrite a disabled STAT source: on DMG even writing zero
// can raise a fresh request and repeatedly re-enter the STAT handler.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, schedDisabled));
injected.Add(Expr.MakeAsm("RET"));
// Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
string applySkipWin = "__kq_scroll_apply_entry_skip_win";
string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
string applyDone = "__kq_scroll_apply_entry_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
injected.Add(Expr.MakeAsm("INC_HL")); // scx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // scy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_C_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_D_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // flags
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_H_A"));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.Make(Tag.Label, applySkipBg));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_D"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_E"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.Make(Tag.Label, applySkipWin));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipHide));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
// CGB palette data writes are blocked during pixel transfer. Wait through
// OAM search as well, so both color bytes fit in the following HBlank.
// The LY-zero entry runs in VBlank and does not wait here.
injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
injected.Add(Expr.Make(Tag.Label, applyDone));
injected.Add(Expr.MakeAsm("RET"));
// Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
string commitDisable = "__kq_scroll_split_commit_disable";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("EI"));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, commitDisable));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
// Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
// A first entry at LY zero is applied immediately before scheduling the following entry.
string vbDisable = "__kq_vblank_disable";
string vbFirstNonZero = "__kq_vblank_first_nonzero";
string vbDone = "__kq_vblank_done";
injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
// Set the existing fixed-address frame-ready flag used by the VBlank path.
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("OR_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDisable));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
// Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
string statDisable = "__kq_stat_disable";
string statDone = "__kq_stat_done";
injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
// DMG STAT writes can request an interrupt unrelated to LYC. Consume
// this entry only on its scheduled line; leave the index intact otherwise.
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, statDisable));
// Acknowledge an already-disabled source without writing STAT again.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, statDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
int insertAt = 0;
while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
rawLines.InsertRange(insertAt, injected);
}__scroll_split_reset
Pone a cero el número y el índice de las divisiones, desactiva su procesamiento y borra el bit de habilitación de la interrupción LYC en STAT.
Sintaxis
void __scroll_split_reset();Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
La tabla compartida admite ocho entradas. Añadirlas no las aplica ni las ordena, y las que exceden la capacidad se ignoran. Reconstrúyela con reset→push→commit en un límite de fotograma seguro y separa los eventos lo suficiente para que el manejador termine. La latencia de la interrupción puede hacer que la línea programada y el primer píxel afectado no coincidan. No borra los bytes de la tabla ni IE, ni desactiva las interrupciones de la CPU.
Ejemplo de uso
__scroll_split_reset();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Divide la pantalla en tres franjas de desplazamiento con BG X=0→8→16. Aparecen tres rectángulos de 24×16 en (32,16), (24,80) y (16,112). Los eventos se programan en LY=64 y 104. Los rectángulos son negros en DMG y rojos en CGB.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kImplementación y código fuente
kitaqgb/lib/scroll.h:30
kitaqgb/kitaqgb/CodeGenerator.cs:7897
if (funcName == "__scroll_split_reset")
{
if (args.Length != 0) Error(expr, "__scroll_split_reset() expects 0 arguments");
EnsureScrollState();
UseScrollSplitHelpers = true;
EmitAsm("CALL", new AsmOperand("__kq_scroll_split_reset_core", AddressMode.Absolute));
return;
}
void AppendScrollHelpersIfUsed(List<Expr> rawLines)
{
bool needScrollHelpers = UseScrollSplitHelpers;
if (!needScrollHelpers)
{
foreach (var e in rawLines)
{
string m;
AsmOperand o;
if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
string target = o.Base.Value;
if (target == "__kq_scroll_split_reset_core" ||
target == "__kq_scroll_split_push_core" ||
target == "__kq_scroll_split_commit_core")
{
needScrollHelpers = true;
break;
}
}
}
if (!needScrollHelpers) return;
EnsureScrollState();
var injected = new List<Expr>();
injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));
// Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
string resetDone = "__kq_scroll_split_reset_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, resetDone));
injected.Add(Expr.MakeAsm("RET"));
// Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
string pushHaveRoom = "__kq_scroll_split_push_have_room";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDI_HL_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("RET"));
// Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
string schedDisable = "__kq_scroll_schedule_next_disable";
string schedDisabled = "__kq_scroll_schedule_next_disabled";
string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, schedDisable));
// Do not rewrite a disabled STAT source: on DMG even writing zero
// can raise a fresh request and repeatedly re-enter the STAT handler.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, schedDisabled));
injected.Add(Expr.MakeAsm("RET"));
// Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
string applySkipWin = "__kq_scroll_apply_entry_skip_win";
string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
string applyDone = "__kq_scroll_apply_entry_done";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
injected.Add(Expr.MakeAsm("INC_HL")); // scx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // scy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_C_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wx
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_D_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // wy
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("INC_HL")); // flags
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_H_A"));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.Make(Tag.Label, applySkipBg));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_D"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_E"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.Make(Tag.Label, applySkipWin));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipHide));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
injected.Add(Expr.MakeAsm("LD_A_H"));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
// CGB palette data writes are blocked during pixel transfer. Wait through
// OAM search as well, so both color bytes fit in the following HBlank.
// The LY-zero entry runs in VBlank and does not wait here.
injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
injected.Add(Expr.MakeAsm("LD_A_C"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
injected.Add(Expr.Make(Tag.Label, applyDone));
injected.Add(Expr.MakeAsm("RET"));
// Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
string commitDisable = "__kq_scroll_split_commit_disable";
injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("EI"));
injected.Add(Expr.MakeAsm("RET"));
injected.Add(Expr.Make(Tag.Label, commitDisable));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("RET"));
// Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
// A first entry at LY zero is applied immediately before scheduling the following entry.
string vbDisable = "__kq_vblank_disable";
string vbFirstNonZero = "__kq_vblank_first_nonzero";
string vbDone = "__kq_vblank_done";
injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
// Set the existing fixed-address frame-ready flag used by the VBlank path.
injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
injected.Add(Expr.MakeAsm("OR_B"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
injected.Add(Expr.MakeAsm("XOR_A"));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDisable));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, vbDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
// Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
string statDisable = "__kq_stat_disable";
string statDone = "__kq_stat_done";
injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("PUSH_BC"));
injected.Add(Expr.MakeAsm("PUSH_DE"));
injected.Add(Expr.MakeAsm("PUSH_HL"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
injected.Add(Expr.MakeAsm("PUSH_AF"));
injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
injected.Add(Expr.MakeAsm("OR_A"));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_B"));
injected.Add(Expr.MakeAsm("LD_E_A"));
injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LD_L_A"));
injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
injected.Add(Expr.MakeAsm("ADD_HL_HL"));
injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("ADD_HL_DE"));
// DMG STAT writes can request an interrupt unrelated to LYC. Consume
// this entry only on its scheduled line; leave the index intact otherwise.
injected.Add(Expr.MakeAsm("LD_A_HL"));
injected.Add(Expr.MakeAsm("LD_B_A"));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("CP_B"));
injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("INC_A"));
injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.Make(Tag.Label, statDisable));
// Acknowledge an already-disabled source without writing STAT again.
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
injected.Add(Expr.Make(Tag.Label, statDone));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
injected.Add(Expr.MakeAsm("POP_HL"));
injected.Add(Expr.MakeAsm("POP_DE"));
injected.Add(Expr.MakeAsm("POP_BC"));
injected.Add(Expr.MakeAsm("POP_AF"));
injected.Add(Expr.MakeAsm("RETI"));
int insertAt = 0;
while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
rawLines.InsertRange(insertAt, injected);
}__scroll_win_add
Suma desplazamientos con signo a las coordenadas registradas de la ventana y los aplica de inmediato.
Sintaxis
void __scroll_win_add(s8 dx, s8 dy);Parámetros
dxDesplazamiento horizontal con signo en píxeles, de −128 a 127.
dyDesplazamiento vertical con signo en píxeles, de −128 a 127.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par. Los resultados vuelven al inicio módulo 256. Las sumas parten de los valores actuales registrados, no de los pendientes ni de los valores temporales de los registros de raster.
Ejemplo de uso
__scroll_win_add(4,-40);result[24]=M_WX;result[25]=M_WY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:38
kitaqgb/kitaqgb/CodeGenerator.cs:7866
if (funcName == "__scroll_win_add")
{
if (args.Length != 2) Error(expr, "__scroll_win_add(dx, dy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_C_A");
EmitAsm("POP_HL");
EmitAsm("LD_B_L");
EmitLoadA(ScrollWinXCurVar);
EmitAsm("ADD_B");
EmitStoreA(ScrollWinXCurVar);
EmitStoreA(ScrollWinXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
EmitLoadA(ScrollWinYCurVar);
EmitAsm("ADD_C");
EmitStoreA(ScrollWinYCurVar);
EmitStoreA(ScrollWinYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_hide
Desactiva el bit de ventana de LCDC y el estado registrado de visibilidad de la ventana.
Sintaxis
void __scroll_win_hide();Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No cambia las coordenadas ni la selección del mapa. Para que la ventana sea visible, el LCD y las funciones de fondo y ventana deben estar activados, y la ventana debe estar dentro del área visible.
Ejemplo de uso
__scroll_win_hide();result[27]=M_LCDC&32;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:36
kitaqgb/kitaqgb/CodeGenerator.cs:7821
if (funcName == "__scroll_win_hide")
{
if (args.Length != 0) Error(expr, "__scroll_win_hide() expects 0 arguments");
EnsureScrollState();
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
EmitAsm("XOR_A");
EmitStoreA(ScrollWinVisibleVar);
return;
}__scroll_win_set
Establece de inmediato WX y WY de la ventana.
Sintaxis
void __scroll_win_set(u8 wx, u8 wy);Parámetros
wxByte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.
wyParte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par.
Ejemplo de uso
__scroll_win_set(15,8);result[8]=M_WX;result[9]=M_WY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:16
kitaqgb/kitaqgb/CodeGenerator.cs:7589
if (funcName == "__scroll_win_set")
{
if (args.Length != 2) Error(expr, "__scroll_win_set(wx, wy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_B_A");
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitStoreA(ScrollWinXCurVar);
EmitStoreA(ScrollWinXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
EmitAsm("LD_A_B");
EmitStoreA(ScrollWinYCurVar);
EmitStoreA(ScrollWinYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_set_buffered
Deja pendientes las dos coordenadas de la ventana para aplicarlas más tarde.
Sintaxis
void __scroll_win_set_buffered(u8 wx, u8 wy);Parámetros
wxByte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.
wyParte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No cambia los valores actuales registrados ni los registros de hardware. Aplícalos con __scroll_flush o Scroll_Flush; dejarlos pendientes no espera a VBlank.
Ejemplo de uso
__scroll_win_set_buffered(39,32);result[16]=M_WX;result[17]=__scroll_win_x_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:25
kitaqgb/kitaqgb/CodeGenerator.cs:7721
if (funcName == "__scroll_win_set_buffered")
{
if (args.Length != 2) Error(expr, "__scroll_win_set_buffered(wx, wy) expects 2 arguments");
EnsureScrollState();
CompileIntoA(args[0]);
EmitPushAAsWord();
CompileIntoA(args[1]);
EmitAsm("LD_B_A");
EmitAsm("POP_HL");
EmitAsm("LD_A_L");
EmitStoreA(ScrollWinXNextVar);
EmitAsm("LD_A_B");
EmitStoreA(ScrollWinYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_show
Activa el bit de ventana de LCDC y el estado registrado de visibilidad de la ventana.
Sintaxis
void __scroll_win_show();Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No cambia las coordenadas ni la selección del mapa. Para que la ventana sea visible, el LCD y las funciones de fondo y ventana deben estar activados, y la ventana debe estar dentro del área visible.
Ejemplo de uso
__scroll_win_show();result[26]=M_LCDC&32;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:35
kitaqgb/kitaqgb/CodeGenerator.cs:7807
if (funcName == "__scroll_win_show")
{
if (args.Length != 0) Error(expr, "__scroll_win_show() expects 0 arguments");
EnsureScrollState();
EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
EmitAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
EmitStoreA(ScrollWinVisibleVar);
return;
}__scroll_win_x_get
Lee el valor WX actual registrado de la ventana.
Sintaxis
u8 __scroll_win_x_get();Valor devuelto
Devuelve el byte actual registrado, de 0 a 255. WX incluye su desplazamiento de hardware de siete píxeles.
Condiciones y límites
No es una lectura de un registro de hardware. No incluye los valores pendientes del búfer, las escrituras directas en registros ni los valores temporales aplicados por las divisiones de raster.
Ejemplo de uso
result[12]=__scroll_win_x_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:19
kitaqgb/kitaqgb/CodeGenerator.cs:7653
if (funcName == "__scroll_win_x_get")
{
if (args.Length != 0) Error(expr, "__scroll_win_x_get() expects 0 arguments");
EnsureScrollState();
EmitLoadA(ScrollWinXCurVar);
return;
}__scroll_win_x_set
Establece de inmediato solo la coordenada WX sin ajustar de la ventana.
Sintaxis
void __scroll_win_x_set(u8 wx);Parámetros
wxByte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par.
Ejemplo de uso
__scroll_win_x_set(23);result[10]=M_WX;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:17
kitaqgb/kitaqgb/CodeGenerator.cs:7617
if (funcName == "__scroll_win_x_set")
{
if (args.Length != 1) Error(expr, "__scroll_win_x_set(wx) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollWinXCurVar);
EmitStoreA(ScrollWinXNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
EmitLoadA(ScrollWinYCurVar);
EmitStoreA(ScrollWinYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_x_set_buffered
Deja pendiente solo WX de la ventana.
Sintaxis
void __scroll_win_x_set_buffered(u8 wx);Parámetros
wxByte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
Conserva el otro eje pendiente. Los valores actuales y los registros no cambian hasta aplicar el búfer; inicializa antes el par de coordenadas.
Ejemplo de uso
__scroll_win_x_set_buffered(55);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:26
kitaqgb/kitaqgb/CodeGenerator.cs:7743
if (funcName == "__scroll_win_x_set_buffered")
{
if (args.Length != 1) Error(expr, "__scroll_win_x_set_buffered(wx) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollWinXNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_y_get
Lee el valor WY actual registrado de la ventana.
Sintaxis
u8 __scroll_win_y_get();Valor devuelto
Devuelve el byte actual registrado, de 0 a 255. WX incluye su desplazamiento de hardware de siete píxeles.
Condiciones y límites
No es una lectura de un registro de hardware. No incluye los valores pendientes del búfer, las escrituras directas en registros ni los valores temporales aplicados por las divisiones de raster.
Ejemplo de uso
result[13]=__scroll_win_y_get();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:20
kitaqgb/kitaqgb/CodeGenerator.cs:7662
if (funcName == "__scroll_win_y_get")
{
if (args.Length != 0) Error(expr, "__scroll_win_y_get() expects 0 arguments");
EnsureScrollState();
EmitLoadA(ScrollWinYCurVar);
return;
}__scroll_win_y_set
Establece de inmediato solo la coordenada WY de la ventana.
Sintaxis
void __scroll_win_y_set(u8 wy);Parámetros
wyParte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
No espera. Los ajustes y sumas inmediatos sincronizan el par de coordenadas pendiente afectado con los valores actuales y borran su indicador de modificación. Incluso ajustar de inmediato un solo eje cancela el otro eje pendiente del mismo par.
Ejemplo de uso
__scroll_win_y_set(16);result[11]=M_WY;Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:18
kitaqgb/kitaqgb/CodeGenerator.cs:7635
if (funcName == "__scroll_win_y_set")
{
if (args.Length != 1) Error(expr, "__scroll_win_y_set(wy) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollWinYCurVar);
EmitStoreA(ScrollWinYNextVar);
EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
EmitLoadA(ScrollWinXCurVar);
EmitStoreA(ScrollWinXNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}__scroll_win_y_set_buffered
Deja pendiente solo WY de la ventana.
Sintaxis
void __scroll_win_y_set_buffered(u8 wy);Parámetros
wyParte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.
Valor devuelto
No devuelve ningún valor. Esta entrada describe el estado afectado y cuándo surte efecto.
Condiciones y límites
Conserva el otro eje pendiente. Los valores actuales y los registros no cambian hasta aplicar el búfer; inicializa antes el par de coordenadas.
Ejemplo de uso
__scroll_win_y_set_buffered(48);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo distingue valores inmediatos y pendientes, y muestra el desbordamiento de las sumas con signo. BG X vale 250 antes de aplicar el búfer y 16 después; WX pasa de 23 a 39. Sumar −20 a BG X da 252 y sumar −40 a WY da 248. El ejemplo visual también muestra un rectángulo de fondo y un marco de ventana independiente.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64kEjemplo de uso
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
Con la cámara en (8,16), el rectángulo rojo de 32×24 aparece en pantalla en (24,16). La ventana muestra un marco azul de 64×64 en (96,80) y un cuadrado verde de 16×16 en (112,96). En DMG todas las figuras son negras.


Comando de compilación
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=64kImplementación y código fuente
kitaqgb/lib/scroll.h:27
kitaqgb/kitaqgb/CodeGenerator.cs:7757
if (funcName == "__scroll_win_y_set_buffered")
{
if (args.Length != 1) Error(expr, "__scroll_win_y_set_buffered(wy) expects 1 argument");
EnsureScrollState();
CompileIntoA(args[0]);
EmitStoreA(ScrollWinYNextVar);
EmitLoadA(ScrollDirtyVar);
EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
EmitStoreA(ScrollDirtyVar);
return;
}sprite.h — Reserva y posición de OBJ, metasprites y animación
Los sprites son objetos móviles de hardware, normalmente de 8×8 u 8×16 píxeles, descritos en OAM, la memoria de atributos de objetos. Reserva entradas, configura posición, patrón y atributos en la copia de OAM y transfiérela. Un metasprite reúne varias entradas en un objeto. GB ofrece 40 entradas y un máximo de 10 por línea; FC, 64 y un máximo de 8 por línea.
Atributos GB: bit 4 elige OBP1 en DMG; 5/6 reflejan X/Y; 7 coloca el sprite detrás de píxeles de fondo con índice distinto de cero. En CGB, 0..2 eligen la paleta de objetos y 3 el banco VRAM de patrones; el bit de paleta DMG no selecciona colores nativos CGB.
El ejemplo usa la biblioteca GB completa. KQ_SPRITE_MINIMAL_RUNTIME excluye de sprite.c sprite_free, sprite_set_pos, sprite_count_used, las dos consultas de líneas, metasprite_draw y anim_update. No lo definas si utilizas esas funciones.
__oam_dma
Copia 160 bytes de una página de memoria a la OAM del hardware (0xFE00–0xFE9F), sustituyendo las 40 entradas de sprites. La función retorna cuando termina la transferencia.
Sintaxis
void __oam_dma(u16 src_ptr);Parámetros
src_ptrDirección de origen en bytes. Solo se utiliza el byte alto:
0xC480selecciona0xC400–0xC49F. Prepare 160 bytes legibles en una dirección múltiplo de 256. Use una página admitida por el hardware (0x00–0xDF) y mantenga mapeado el banco de ROM o RAM necesario. El compilador no comprueba la dirección ni la alineación.
Valor devuelto
Ninguno (void).
Condiciones y límites
Cada entrada contiene cuatro bytes: Y, X, número de mosaico y atributos. Las coordenadas en pantalla son (X−8, Y−16). Un valor Y de 0 oculta una entrada sin usar. Esta función intrínseca copia datos en bruto; no reserva sprites ni actualiza los contadores o el búfer auxiliar de la biblioteca de sprites.
Llámela durante VBlank o con la LCD apagada. La función no espera a VBlank ni cambia la configuración de la LCD. La transferencia del hardware tarda 160 ciclos máquina de la CPU, además del tiempo de preparación y retorno de la función.
Los programas que contienen esta llamada reservan 8 bytes de HRAM para la rutina de transferencia. La rutina se inicializa en cada llamada y espera en HRAM mientras la DMA está activa. Las fuentes de interrupción se enmascaran temporalmente; se conservan IE y el estado de habilitación de IME. La atención de interrupciones se retrasa hasta que termina la transferencia.
Ejemplo de uso
// raw_oam is a 160-byte array at address 0xC400.
m_wait();
__oam_dma((u16)raw_oam);Este fragmento muestra la llamada. Antes de incorporarlo a un programa completo, cumple los requisitos de inicialización y de los búferes indicados.
Resultado esperado
El ejemplo construye directamente una página OAM completa. Muestra una flecha de 8×8 hacia la derecha en (88,32), su reflejo horizontal en (88,56) y un cuadrado de 8×8 en (88,80). En CGB, la flecha tiene bordes rojos, cuerpo verde y detalles azules en la punta; el cuadrado es verde. DMG usa tonos de gris. Las demás entradas quedan ocultas. La verificación comprueba los píxeles reales en ambos modos de hardware del emulador; no se ha probado en una consola física.


Comando de compilación
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\oam_dma.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\oam_dma.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasmImplementación y código fuente
kitaqgb/lib/sprite.h:46
// Copy one 160-byte, page-aligned OAM source and return after DMA completes.
if (funcName == "__oam_dma")
{
if (args.Length != 1) Error(expr, "__oam_dma(src_ptr) expects 1 argument");
CompileIntoHL(args[0]); // Evaluate once; hardware uses only the high byte.
// Mask interrupt sources without changing IME, including calls made inside an ISR.
EmitAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute));
EmitAsm("PUSH_AF");
EmitAsm("XOR_A");
EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
// LDH [FF46],A; LD B,40; DEC B; JR NZ,-3; RET.
// Initialize on every call, so no startup copy or readiness flag is needed.
byte[] dmaStub = { 0xE0, 0x46, 0x06, 0x28, 0x05, 0x20, 0xFD, 0xC9 };
for (int i = 0; i < dmaStub.Length; i++)
{
EmitAsm("LD_A_IMM", new AsmOperand(dmaStub[i], AddressMode.Immediate));
EmitAsm("LDH_MEM_A", new AsmOperand((OamDmaStubAddr + i) & 0xFF, AddressMode.HighMem));
}
EmitAsm("LD_A_H");
// CALL pushes before DMA starts; RET reads the stack only after the wait.
EmitAsm("CALL", new AsmOperand(OamDmaStubAddr, AddressMode.Absolute));
EmitAsm("POP_AF");
EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
return;
}Referencia de sintaxis de comandos
La siguiente ayuda se capturó para esta edición del código fuente. Consúltala para conocer los argumentos opcionales, los valores predeterminados y los valores admitidos. Sustituye los nombres de archivo ilustrativos por tus propios archivos. Las respuestas capturadas de las herramientas conservan su redacción original.
kitaqgb
KITAQGB native Rust compiler
Commands:
disasm ROM -o dis.s
header ROM -o patched.gb [--title=NAME --cgb=dmg|cgb|cgb_only ...]
assemble-ir INPUT.assembly.json -o output.gb [header options]
tokenize SOURCE.c ... -I INCLUDE_DIR -o tokens.json
parse SOURCE.c ... -I INCLUDE_DIR -o ast.json
lower SOURCE.c ... -I INCLUDE_DIR -o lowered.json
emit-ir SOURCE.c ... -I INCLUDE_DIR -o raw.assembly.json
compile SOURCE.c ... -I INCLUDE_DIR -o output.gb [-O0|-O1]
optimize-ir INPUT.assembly.json -o optimized.json [-O0|-O1]
strip-ir INPUT.assembly.json -o stripped.json [--strip-keep=LABELS]
C compilation, aggregates, banked calls and hardware intrinsics are implemented and covered by differential tests. Utility commands: kqhelp, symfind, src2asm, romdiff, template, snippet, conventions, fixhint, irsum, attrviz, recipe, test.
Development and recovery:
SOURCE.c ... --watch [--watch-max-builds=N]
devserver SOURCE.c ... [--once --poll-ms=N --debounce-ms=N --max-builds=N]
recipe [--history=FILE --out=FILE --script=FILE --last=N --stdout]
SOURCE.c ... --minimize[=FILE] [--minimize-out=FILE --minimize-work=DIR --minimize-quick --minimize-trace=final|all]
SOURCE.c ... --minimize-on-fail --repro-pack[=DIR]
SOURCE.c ... --debug-output --disasm-changed[=GIT_BASE]
Watch loops stop with Ctrl+C; the Rust API also accepts a CancellationToken. See PORT_STATUS.md for executed verification and compatibility limits.Sintaxis de comandos para desarrolladores
Estos usos proceden de Program.DebugTools.cs y Program.VibeTools.cs. La comparación de ROM, la búsqueda de símbolos y las plantillas son subcomandos distintos de la compilación C habitual.
usage: kitaqgb kqhelp <KQxxxx>usage: kitaqgb symfind <pattern> [--map=<.map>] [--dbg=<.dbg>] [--regex] [--ignore-case] [--out=<file>|-]usage: kitaqgb src2asm <source.c:line> [--disasm=<dis.s>] [--funcsizes=<.funcsizes.txt>] [--context=N] [--out=<file>|-]usage: kitaqgb romdiff <old.gb> <new.gb> [--old-func=<.funcsizes.txt>] [--new-func=<.funcsizes.txt>] [--old-map=<.map>] [--new-map=<.map>] [--out=<file>|-]usage: kitaqgb template [out.c] [--out=<file.c>] [--cgb] [--overwrite] [--no-build-script] [--no-readme]usage: kitaqgb conventions [--out=<file>|--stdout]usage: kitaqgb snippet list | get <id> [--out=<file>|-] | emit <category|all> [--out=<file>|-]usage: kitaqgb fixhint [diag.json|stderr.log] [--in=<file>] [--out=<file>|-] [--max=N]usage: kitaqgb irsum [--ir=<syntax_tree_lowered.txt>] [--asm=<assembly_code.txt>] [--top=N] [--out=<file>|-]usage: kitaqgb devserver [--poll-ms=N] [--debounce-ms=N] [--once] [--max-builds=N] [--deps=<file>] <compile args...>usage: kitaqgb recipe [--history=<file>] [--last=N] [--out=<file>] [--script=<file>] [--stdout]Opciones largas reconocidas por el analizador
Los nombres se han extraído de src/cli.rs e incluyen opciones de compatibilidad y de investigación. Consulta la ayuda y el manejador correspondiente para determinar si cada opción requiere un valor.
--abi--abi-diff-report--abi-verify--attach--auto-minimize--bank-sim--cache--cart--cgb--cgb-consistency--cross-bank-report--debug-out--debug-output--deps-out--dest--diag-json--disasm--disasm-changed--disasm-changed-out--emit-ai-metadata--emit-path-manifest--farcall-suggest--fast--fast-build--func-size-report--header-logo--help--hotspot-report--include-dir--machine-readable--max-errors--minimize-on-fail--no-banner--no-cache--no-disasm--no-header-logo--no-rst--opt-diff--permissive--profile--ramsize--repro-check--repro-pack--rom-header--rom-title--romsize--rst--rst-disable--rst-enable--rst-exclude--rst-max-calls--rst-max-vectors--rst-off--rst-on--rst-report--rst-safe--rst-speed-safe--rst-unsafe--rst-use-38--sgb--stack-bank--stack-reserve--stack-top--strict--trace--trace-out--verify-cgb-symbols--version--vlist--vlist-outApéndice: índice de instrucciones de ensamblador
Esta tabla se extrae de src/asm_info.rs. Describe los nombres internos del compilador y los formatos de operandos. Las entradas con destinos de salto o direcciones de memoria son ejemplos de sintaxis, no programas independientes. Consulta las convenciones de llamada y la generación de código para conocer los registros preservados y los cambios de indicadores.
| opcode | Instrucción | Formato | Ejemplo de sintaxis |
|---|---|---|---|
| 00 | NOP | IMP | NOP |
| F3 | DI | IMP | DI |
| FB | EI | IMP | EI |
| 76 | HALT | IMP | HALT |
| 10 | STOP | IMM8 | STOP #1 |
| C9 | RET | IMP | RET |
| C0 | RET_NZ | IMP | RET_NZ |
| C8 | RET_Z | IMP | RET_Z |
| D0 | RET_NC | IMP | RET_NC |
| D8 | RET_C | IMP | RET_C |
| D9 | RETI | IMP | RETI |
| C3 | JP | ABS | JP 0xC000 |
| E9 | JP_HL | IMP | JP_HL |
| 18 | JR | REL | JR +target |
| 20 | JR_NZ | REL | JR_NZ +target |
| 28 | JR_Z | REL | JR_Z +target |
| 30 | JR_NC | REL | JR_NC +target |
| 38 | JR_C | REL | JR_C +target |
| CD | CALL | ABS | CALL 0xC000 |
| C4 | CALL_NZ | ABS | CALL_NZ 0xC000 |
| CC | CALL_Z | ABS | CALL_Z 0xC000 |
| D4 | CALL_NC | ABS | CALL_NC 0xC000 |
| DC | CALL_C | ABS | CALL_C 0xC000 |
| C2 | JP_NZ | ABS | JP_NZ 0xC000 |
| CA | JP_Z | ABS | JP_Z 0xC000 |
| D2 | JP_NC | ABS | JP_NC 0xC000 |
| DA | JP_C | ABS | JP_C 0xC000 |
| C7 | RST_00 | IMP | RST_00 |
| CF | RST_08 | IMP | RST_08 |
| D7 | RST_10 | IMP | RST_10 |
| DF | RST_18 | IMP | RST_18 |
| E7 | RST_20 | IMP | RST_20 |
| EF | RST_28 | IMP | RST_28 |
| F7 | RST_30 | IMP | RST_30 |
| FF | RST_38 | IMP | RST_38 |
| 3E | LD_A_IMM | IMM8 | LD_A_IMM #1 |
| 06 | LD_B_IMM | IMM8 | LD_B_IMM #1 |
| 0E | LD_C_IMM | IMM8 | LD_C_IMM #1 |
| 16 | LD_D_IMM | IMM8 | LD_D_IMM #1 |
| 1E | LD_E_IMM | IMM8 | LD_E_IMM #1 |
| 26 | LD_H_IMM | IMM8 | LD_H_IMM #1 |
| 2E | LD_L_IMM | IMM8 | LD_L_IMM #1 |
| 7F | LD_A_A | IMP | LD_A_A |
| 78 | LD_A_B | IMP | LD_A_B |
| 79 | LD_A_C | IMP | LD_A_C |
| 7A | LD_A_D | IMP | LD_A_D |
| 7B | LD_A_E | IMP | LD_A_E |
| 7C | LD_A_H | IMP | LD_A_H |
| 7D | LD_A_L | IMP | LD_A_L |
| 47 | LD_B_A | IMP | LD_B_A |
| 40 | LD_B_B | IMP | LD_B_B |
| 41 | LD_B_C | IMP | LD_B_C |
| 42 | LD_B_D | IMP | LD_B_D |
| 43 | LD_B_E | IMP | LD_B_E |
| 44 | LD_B_H | IMP | LD_B_H |
| 45 | LD_B_L | IMP | LD_B_L |
| 4F | LD_C_A | IMP | LD_C_A |
| 48 | LD_C_B | IMP | LD_C_B |
| 49 | LD_C_C | IMP | LD_C_C |
| 4A | LD_C_D | IMP | LD_C_D |
| 4B | LD_C_E | IMP | LD_C_E |
| 4C | LD_C_H | IMP | LD_C_H |
| 4D | LD_C_L | IMP | LD_C_L |
| 57 | LD_D_A | IMP | LD_D_A |
| 50 | LD_D_B | IMP | LD_D_B |
| 51 | LD_D_C | IMP | LD_D_C |
| 52 | LD_D_D | IMP | LD_D_D |
| 53 | LD_D_E | IMP | LD_D_E |
| 54 | LD_D_H | IMP | LD_D_H |
| 55 | LD_D_L | IMP | LD_D_L |
| 5F | LD_E_A | IMP | LD_E_A |
| 58 | LD_E_B | IMP | LD_E_B |
| 59 | LD_E_C | IMP | LD_E_C |
| 5A | LD_E_D | IMP | LD_E_D |
| 5B | LD_E_E | IMP | LD_E_E |
| 5C | LD_E_H | IMP | LD_E_H |
| 5D | LD_E_L | IMP | LD_E_L |
| 67 | LD_H_A | IMP | LD_H_A |
| 60 | LD_H_B | IMP | LD_H_B |
| 61 | LD_H_C | IMP | LD_H_C |
| 62 | LD_H_D | IMP | LD_H_D |
| 63 | LD_H_E | IMP | LD_H_E |
| 64 | LD_H_H | IMP | LD_H_H |
| 65 | LD_H_L | IMP | LD_H_L |
| 6F | LD_L_A | IMP | LD_L_A |
| 68 | LD_L_B | IMP | LD_L_B |
| 69 | LD_L_C | IMP | LD_L_C |
| 6A | LD_L_D | IMP | LD_L_D |
| 6B | LD_L_E | IMP | LD_L_E |
| 6C | LD_L_H | IMP | LD_L_H |
| 6D | LD_L_L | IMP | LD_L_L |
| EA | LD_MEM_A | ABS | LD_MEM_A 0xC000 |
| FA | LD_A_MEM | ABS | LD_A_MEM 0xC000 |
| E0 | LDH_MEM_A | LDH | LDH_MEM_A 0x40 |
| F0 | LDH_A_MEM | LDH | LDH_A_MEM 0x40 |
| E2 | LD_C_MEM_A | IMP | LD_C_MEM_A |
| F2 | LD_A_MEM_C | IMP | LD_A_MEM_C |
| 77 | LD_HL_A | IMP | LD_HL_A |
| 7E | LD_A_HL | IMP | LD_A_HL |
| 46 | LD_B_HL | IMP | LD_B_HL |
| 4E | LD_C_HL | IMP | LD_C_HL |
| 56 | LD_D_HL | IMP | LD_D_HL |
| 5E | LD_E_HL | IMP | LD_E_HL |
| 66 | LD_H_HL | IMP | LD_H_HL |
| 6E | LD_L_HL | IMP | LD_L_HL |
| 70 | LD_HL_B | IMP | LD_HL_B |
| 71 | LD_HL_C | IMP | LD_HL_C |
| 72 | LD_HL_D | IMP | LD_HL_D |
| 73 | LD_HL_E | IMP | LD_HL_E |
| 74 | LD_HL_H | IMP | LD_HL_H |
| 75 | LD_HL_L | IMP | LD_HL_L |
| 36 | LD_HL_REF_IMM | IMM8 | LD_HL_REF_IMM #1 |
| 12 | LD_DE_A | IMP | LD_DE_A |
| 1A | LD_A_DE | IMP | LD_A_DE |
| 02 | LD_BC_A | IMP | LD_BC_A |
| 0A | LD_A_BC | IMP | LD_A_BC |
| 22 | LDI_HL_A | IMP | LDI_HL_A |
| 2A | LDI_A_HL | IMP | LDI_A_HL |
| 32 | LDD_HL_A | IMP | LDD_HL_A |
| 3A | LDD_A_HL | IMP | LDD_A_HL |
| 01 | LD_BC_IMM | IMM16 | LD_BC_IMM #0xC000 |
| 11 | LD_DE_IMM | IMM16 | LD_DE_IMM #0xC000 |
| 21 | LD_HL_IMM | IMM16 | LD_HL_IMM #0xC000 |
| 31 | LD_SP_IMM | IMM16 | LD_SP_IMM #0xC000 |
| F9 | LD_SP_HL | IMP | LD_SP_HL |
| F8 | LD_HL_SP_IMM | REL | LD_HL_SP_IMM +target |
| E8 | ADD_SP_IMM | REL | ADD_SP_IMM +target |
| 08 | LD_MEM_SP | IMM16 | LD_MEM_SP #0xC000 |
| C5 | PUSH_BC | IMP | PUSH_BC |
| C1 | POP_BC | IMP | POP_BC |
| D5 | PUSH_DE | IMP | PUSH_DE |
| D1 | POP_DE | IMP | POP_DE |
| E5 | PUSH_HL | IMP | PUSH_HL |
| E1 | POP_HL | IMP | POP_HL |
| F5 | PUSH_AF | IMP | PUSH_AF |
| F1 | POP_AF | IMP | POP_AF |
| 3C | INC_A | IMP | INC_A |
| 04 | INC_B | IMP | INC_B |
| 0C | INC_C | IMP | INC_C |
| 14 | INC_D | IMP | INC_D |
| 1C | INC_E | IMP | INC_E |
| 24 | INC_H | IMP | INC_H |
| 2C | INC_L | IMP | INC_L |
| 34 | INC_HL_REF | IMP | INC_HL_REF |
| 3D | DEC_A | IMP | DEC_A |
| 05 | DEC_B | IMP | DEC_B |
| 0D | DEC_C | IMP | DEC_C |
| 15 | DEC_D | IMP | DEC_D |
| 1D | DEC_E | IMP | DEC_E |
| 25 | DEC_H | IMP | DEC_H |
| 2D | DEC_L | IMP | DEC_L |
| 35 | DEC_HL_REF | IMP | DEC_HL_REF |
| 87 | ADD_A | IMP | ADD_A |
| 80 | ADD_B | IMP | ADD_B |
| 81 | ADD_C | IMP | ADD_C |
| 82 | ADD_D | IMP | ADD_D |
| 83 | ADD_E | IMP | ADD_E |
| 84 | ADD_H | IMP | ADD_H |
| 85 | ADD_L | IMP | ADD_L |
| 86 | ADD_HL_REF | IMP | ADD_HL_REF |
| C6 | ADD_A_IMM | IMM8 | ADD_A_IMM #1 |
| 8F | ADC_A | IMP | ADC_A |
| 88 | ADC_B | IMP | ADC_B |
| 89 | ADC_C | IMP | ADC_C |
| 8A | ADC_D | IMP | ADC_D |
| 8B | ADC_E | IMP | ADC_E |
| 8C | ADC_H | IMP | ADC_H |
| 8D | ADC_L | IMP | ADC_L |
| 8E | ADC_HL_REF | IMP | ADC_HL_REF |
| CE | ADC_IMM | IMM8 | ADC_IMM #1 |
| 97 | SUB_A | IMP | SUB_A |
| 90 | SUB_B | IMP | SUB_B |
| 91 | SUB_C | IMP | SUB_C |
| 92 | SUB_D | IMP | SUB_D |
| 93 | SUB_E | IMP | SUB_E |
| 94 | SUB_H | IMP | SUB_H |
| 95 | SUB_L | IMP | SUB_L |
| 96 | SUB_HL_REF | IMP | SUB_HL_REF |
| D6 | SUB_IMM | IMM8 | SUB_IMM #1 |
| 9F | SBC_A | IMP | SBC_A |
| 98 | SBC_B | IMP | SBC_B |
| 99 | SBC_C | IMP | SBC_C |
| 9A | SBC_D | IMP | SBC_D |
| 9B | SBC_E | IMP | SBC_E |
| 9C | SBC_H | IMP | SBC_H |
| 9D | SBC_L | IMP | SBC_L |
| 9E | SBC_HL_REF | IMP | SBC_HL_REF |
| DE | SBC_IMM | IMM8 | SBC_IMM #1 |
| A7 | AND_A | IMP | AND_A |
| A0 | AND_B | IMP | AND_B |
| A1 | AND_C | IMP | AND_C |
| A2 | AND_D | IMP | AND_D |
| A3 | AND_E | IMP | AND_E |
| A4 | AND_H | IMP | AND_H |
| A5 | AND_L | IMP | AND_L |
| A6 | AND_HL_REF | IMP | AND_HL_REF |
| E6 | AND_IMM | IMM8 | AND_IMM #1 |
| B7 | OR_A | IMP | OR_A |
| B0 | OR_B | IMP | OR_B |
| B1 | OR_C | IMP | OR_C |
| B2 | OR_D | IMP | OR_D |
| B3 | OR_E | IMP | OR_E |
| B4 | OR_H | IMP | OR_H |
| B5 | OR_L | IMP | OR_L |
| B6 | OR_HL_REF | IMP | OR_HL_REF |
| F6 | OR_IMM | IMM8 | OR_IMM #1 |
| AF | XOR_A | IMP | XOR_A |
| A8 | XOR_B | IMP | XOR_B |
| A9 | XOR_C | IMP | XOR_C |
| AA | XOR_D | IMP | XOR_D |
| AB | XOR_E | IMP | XOR_E |
| AC | XOR_H | IMP | XOR_H |
| AD | XOR_L | IMP | XOR_L |
| AE | XOR_HL_REF | IMP | XOR_HL_REF |
| EE | XOR_IMM | IMM8 | XOR_IMM #1 |
| BF | CP_A | IMP | CP_A |
| B8 | CP_B | IMP | CP_B |
| B9 | CP_C | IMP | CP_C |
| BA | CP_D | IMP | CP_D |
| BB | CP_E | IMP | CP_E |
| BC | CP_H | IMP | CP_H |
| BD | CP_L | IMP | CP_L |
| BE | CP_HL_REF | IMP | CP_HL_REF |
| FE | CP_IMM | IMM8 | CP_IMM #1 |
| 03 | INC_BC | IMP | INC_BC |
| 0B | DEC_BC | IMP | DEC_BC |
| 13 | INC_DE | IMP | INC_DE |
| 1B | DEC_DE | IMP | DEC_DE |
| 23 | INC_HL | IMP | INC_HL |
| 2B | DEC_HL | IMP | DEC_HL |
| 33 | INC_SP | IMP | INC_SP |
| 3B | DEC_SP | IMP | DEC_SP |
| 09 | ADD_HL_BC | IMP | ADD_HL_BC |
| 19 | ADD_HL_DE | IMP | ADD_HL_DE |
| 29 | ADD_HL_HL | IMP | ADD_HL_HL |
| 39 | ADD_HL_SP | IMP | ADD_HL_SP |
| 07 | RLCA | IMP | RLCA |
| 17 | RLA | IMP | RLA |
| 0F | RRCA | IMP | RRCA |
| 1F | RRA | IMP | RRA |
| CB | PREFIX_CB | IMP | PREFIX_CB |
| 2F | CPL | IMP | CPL |
| 3F | CCF | IMP | CCF |
| 37 | SCF | IMP | SCF |
| 27 | DAA | IMP | DAA |