KITAQ SISTEMA DE DESARROLLO
MANUAL DE USO 2026.09

SERIE KITAQ • EDICIÓN DE REFERENCIA

KITAQGB

Programación en C para Game Boy

Desde tu primera línea hasta la ejecución, la observación y la repetición de pruebas.
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.

OSkitaqgb
Windows x64kitaqgb.exe
Linux x64 (musl)bin/linux-x86_64/kitaqgb
Linux x64 (GNU)bin/linux-gnu-x86_64/kitaqgb
macOS ARM64bin/macos-arm64/kitaqgb
macOS Intelbin/macos-x86_64/kitaqgb
macOS Universalbin/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.

sh
kitaqgb game.c -I lib -o game.gb --cart=romonly --romsize=32k

1. Conocer KITAQGB

KITAQGB es un compilador de la familia C para GB/CGB desarrollado a partir de NORCAL, de Zachtronics.

NORCAL toma su nombre del norte de California (Northern California). Inspirándose en esa referencia geográfica, el autor eligió el nombre KITAQGB a partir de Kitakyushu, la ciudad donde nació y creció. KITAQ + GB combina Game Boy con 北九 (キタキュー, Kitakyū), el apodo de Kitakyushu, en la prefectura de Fukuoka, Japón. KITAQ se pronuncia como el japonés «キタキュー»; la guía de pronunciación en inglés es kee-tah-KYOO, con transcripción fonética /ˌkiːtɑːˈkjuː/. La Q final suena como el nombre de la letra Q en inglés. KITAQGB se lee kee-tah-KYOO jee bee, pronunciando G y B por separado en inglés.

El nombre KITAQGB tiene dos significados. Kernel-Informed Toolchain for AI-Quality Game Boy Development expresa el objetivo de una cadena de herramientas que entiende la máquina de destino y apoya tanto a quienes programan como a la IA generativa.

El otro significado es Kids' Imagination Transformed into Actual Quests in Game Boy Forests: una herramienta que convierte la imaginación infantil en aventuras reales en los bosques de Game Boy. Expresa el deseo de transformar pequeñas ideas, garabatos y prototipos creados con ayuda de la IA en aventuras que se puedan jugar de verdad.

Lee archivos C, genera instrucciones de CPU y las empaqueta en una ROM. Su lenguaje, bibliotecas y convenciones de llamada difieren de los de un compilador de escritorio.

GB tiene una CPU de 8 bits y poca memoria. La mayoría de los gráficos usan tiles de 8×8 píxeles. Los sprites son imágenes pequeñas que pueden colocarse de forma independiente. El texto también necesita tiles: no hay que dar por sentado un sistema universal de impresión de caracteres. Los ejemplos reordenan los glifos de ascii.c según ASCII para GB sin cambiar sus bits; la edición FC conserva las mismas formas en disposición NES CHR.

2. Requisitos y compilación del compilador

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.

sh
cd kitaqgb
cargo test --locked --tests
cargo build --locked --release

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.

OSkitaqgb
Windows x64kitaqgb.exe
Linux x64 (musl)bin/linux-x86_64/kitaqgb
Linux x64 (GNU)bin/linux-gnu-x86_64/kitaqgb
macOS ARM64bin/macos-arm64/kitaqgb
macOS Intelbin/macos-x86_64/kitaqgb
macOS Universalbin/macos-universal/kitaqgb
powershell
.\scripts\build.ps1
.\kitaqgb.exe --help
sh
sh scripts/build.sh

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.

3. Tu primer programa

samples/gb_hello.c utiliza las funciones auxiliares de pantalla y texto de gb_common.h. Mantén ese encabezado junto a los archivos de la fuente. #include incorpora declaraciones o definiciones de otro archivo.

c
#include "gb_common.h"
void main() {
    m_init();
    m_text(2, 5, "HELLO WORLD");
    m_number(42);
    while (1) { m_wait(); }
}
powershell
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\gb_hello.c -I .\kitaq-docs\samples -o .\out\hello.gb --profile=dev --rst-disable --stack-bank=fixed
.\kokura\kokura-cli.exe .\out\hello.gb --run-frames 120 --png .\out\hello.png --dump-report .\out\hello.json

El resultado esperado es HELLO WORLD y 042 en pantalla. Los nombres que empiezan por m_ pertenecen a las funciones didácticas del encabezado común; no son comandos estándar de KITAQGB. Entre las operaciones intrínsecas están __wait_vblank y __vram_copy.

4. Sintaxis básica y tipos

Termina las sentencias con ; y agrúpalas con { }. // inicia un comentario de línea y /* ... */ delimita uno de bloque. Las mayúsculas y minúsculas importan. Usa void main() como entrada. En esta versión GB, void main(void) produce un error de sintaxis: no copies sin cambios la declaración de entrada de FC.

TipoSignificado y rango
u8Entero de 8 bits sin signo, de 0 a 255
s8Entero de 8 bits con signo, de -128 a 127
u16Entero de 16 bits sin signo, de 0 a 65535
s16Entero de 16 bits con signo, de -32768 a 32767
voidSin valor de retorno
T*Puntero a datos de tipo T

Empieza usando estos nombres cortos. No supongas que int, long, float, double o los encabezados estándar funcionan como en un ordenador de escritorio. Aquí char representa 8 bits sin signo; usa s8 o s16 explícitamente para operaciones con signo.

c
u8 lives = 3;
u16 score;
score = (u16)200 + 100;

(u16) es una conversión de tipo. Asignar un valor pequeño que ya se desbordó a una variable mayor no recupera los bits perdidos: amplía los operandos antes de calcular. Para movimientos fraccionarios, usa la biblioteca de punto fijo.

5. Expresiones y operadores

GrupoOperadoresEjemplo o significado
Aritmética+ - * / %n / 10 obtiene el cociente entero; n % 10, el resto
Comparación== != < <= > >=lives == 0 comprueba igualdad
Lógica! && / ||Negación, ambas condiciones, cualquiera de ellas
Bits& / | / ^ ~ << >>Máscaras de botones y conjuntos de bits
Asignación= += -= y formas relacionadasx += 1 actualiza un valor
Incremento++ --i++ suma uno
Seleccióncondition ? A : BElige un valor según la condición
Punteros&variable / *pObtiene una dirección o accede a su contenido

No confundas = con ==. Usa paréntesis en expresiones complejas y evita acumular llamadas y efectos secundarios en una sola sentencia. No dividas por cero ni accedas fuera de un array. sizeof da el tamaño en bytes y offsetof el desplazamiento de un miembro.

Resultados de las expresiones y evaluación

Los operadores de comparación ==, !=, <, <=, >, >= y los operadores lógicos !, &&, || devuelven 0 para falso y 1 para verdadero. Puedes guardar el resultado en un u16, pasarlo como argumento, devolverlo o usarlo en operaciones aritméticas. Por ejemplo, score = 500 + (lives != 0); produce 501 si queda alguna vida y 500 en caso contrario.

&& omite la evaluación del operando derecho cuando el izquierdo es cero. || la omite cuando el izquierdo no es cero. En pointer != 0 && pointer->active != 0, un puntero nulo impide el acceso al miembro. Los dos bytes de un valor de 16 bits intervienen en la comprobación lógica, por lo que 256 es verdadero. Los operadores de bits & y | no realizan evaluación en cortocircuito.

++value devuelve el valor actualizado; value++, el valor original. Estos operadores también admiten elementos de arrays, punteros desreferenciados y miembros de estructuras. buffer[index()]++ llama a index() una sola vez. Para u16 *p, p++ avanza dos bytes hasta el siguiente elemento, mientras que (*p)++ incrementa el valor apuntado.

sizeof(array) da el tamaño en bytes del array completo; sizeof(pointer) es 2. Para u16 values[9];, sizeof(values) es 18. Esto incluye arrays en ROM, arrays locales y miembros de tipo array. sizeof(function()) examina el tipo de retorno sin llamar a la función.

La declaración y la definición de una función deben coincidir en los tipos y el orden de los parámetros. Sus nombres pueden ser distintos; el cuerpo usa los de la definición. Por ejemplo, u8 next(u8 input); puede definirse como u8 next(u8 value) { return (u8)(value + 1); }. Los parámetros y las variables locales ocultan las variables globales con el mismo nombre.

Seleccionar arrays o cadenas con ?: produce un puntero al tipo de elemento seleccionado. Puedes pasarlo directamente, como en show(ready ? "READY" : "WAIT");. Para los arrays u16 llamados a y b, (ready ? a : b) + 1 avanza dos bytes hasta el segundo elemento del array elegido. No copia el array.

condition ? yes : no evalúa la condición y después solo la rama seleccionada. La condición y las ramas pueden incluir desplazamientos con cantidades de bits variables. Por ejemplo, on = (pattern & (0x80 >> bit)) != 0 ? 4 : 2; elige cuatro o dos según el bit indicado. Tampoco se ejecutan las llamadas a funciones de un operando derecho omitido por && o ||. Un continue en un bucle for ejecuta una vez la expresión de actualización antes de reevaluar la condición; en while y do ... while, pasa a la condición.

6. Condiciones y bucles

c
if (lives == 0) { game_over = 1; }
else { score = score + 10; }
for (i = 0; i < 4; i++) { table[i] = i; }
while (running != 0) { update_game(); }
do { count++; } while (count < 3);

break sale de un bucle o switch, continue pasa a la siguiente iteración y return termina una función. Para continuar deliberadamente desde un caso de switch al siguiente, escribe fallthrough;. La continuación implícita genera un diagnóstico. gb_control.c contiene un programa completo.

7. Funciones, arrays y estructuras

c
u8 add(u8 a, u8 b) { return (u8)(a + b); }
typedef struct { u8 x; u8 y; } Point;
Point player;
u8 tiles[4];

Los índices empiezan en cero: un array de cuatro elementos admite del 0 al 3. player.x accede a un miembro y pointer->x lo hace mediante un puntero. El analizador admite estructuras, uniones y enumeraciones, pero su disposición depende de los tipos y de __packed / __aligned. Comprueba sizeof antes de compartir datos con hardware o formatos binarios.

La ABI Legacy predeterminada coloca los argumentos y el almacenamiento local en posiciones fijas. No presupongas recursión ni reentrada desde interrupciones como en un equipo de escritorio. __stackcall y --abi=stack son opciones avanzadas de convención de llamada. Si combinas convenciones, examina los informes de ABI y verifica la ejecución. --abi=stack no admite llamadas a callbacks mediante punteros a función. Compila los ejemplos completos de callbacks de sistema y escenas con la ABI Legacy predeterminada, tal como indican sus comandos de compilación.

8. Varios archivos y preprocesador

Pon tipos, constantes y declaraciones en encabezados, y los cuerpos de funciones en archivos .c. Usa #pragma once o guardas de inclusión para evitar duplicados. La compilación condicional admite #define, #undef, #if, #ifdef, #ifndef, #elif, #else y #endif.

powershell
.\kitaqgb\kitaqgb.exe main.c game.c .\kitaqgb\lib\input.c -I .\kitaqgb\lib -o .\out\game.gb --profile=dev

-I añade un directorio de búsqueda. Incluir un encabezado no enlaza su implementación: enumera los .c necesarios en el comando. Añadir todas las fuentes sin seleccionar puede duplicar registros o manejadores de interrupción.

9. ROM, memoria y bancos

ROM almacena código y constantes; WRAM, variables; VRAM, gráficos; OAM, descripciones de sprites. Cambiar de banco modifica la memoria física visible en una dirección de CPU. Un puntero de 16 bits por sí solo no identifica datos de otro banco.

c
#pragma bank 1
__prg_rom u8 level_data[] = { 1, 2, 3, 4 };

__prg_rom sitúa datos en ROM. __location(0xFF40) selecciona una dirección fija, y __wram / __hram, regiones de memoria. Con #pragma bank o #pragma fixed_bank, revisa el mapa y garantiza el acceso al código y los datos usados por interrupciones.

powershell
.\kitaqgb\kitaqgb.exe main.c -o .\out\game.gbc --cart=mbc5 --romsize=256k --ramsize=32k --cgb=cgb --rom-title=MYGAME --stack-bank=fixed --emit-ai-metadata=.\out\build.json

--cgb=dmg declara software para DMG, --cgb=cgb para ambos modos y --cgb=cgb_only exclusivo de CGB. Un juego de modo dual debe detectar la máquina antes de usar funciones exclusivas de CGB. Un indicador en el encabezado no implementa esa lógica dentro del juego.

10. Actualizar gráficos con seguridad

VBlank es el intervalo entre fotogramas. Escribir en VRAM u OAM en un momento inadecuado puede corromper la imagen o perder actualizaciones. Carga los recursos iniciales con la pantalla apagada; después usa operaciones seguras o la cola de VRAM. Las funciones _unsafe y _fast exigen que el llamador garantice un intervalo de transferencia válido.

Alinea el búfer de OAM DMA a un límite de 256 bytes. En GB, __oam_dma recibe la dirección de origen; la operación de FC con el mismo nombre no recibe argumentos.

11. Opciones y archivos generados

-o elige la salida, -O0 / -O1 la optimización y --profile=dev|release|test un conjunto de ajustes. --no-disasm evita la salida desensamblada. --debug-out=... y --trace-out=... seleccionan destinos de investigación. Adapta la cantidad de salida a una compilación rápida o a un análisis detallado.

powershell
.\kitaqgb\kitaqgb.exe main.c -I .\kitaqgb\lib -o .\out\game.gb --profile=dev --diag-json=.\out\compile.json --emit-ai-metadata=.\out\build.json --debug-out=.\out\debug

.map registra nombres y ubicación; .funcsizes.txt, tamaños de funciones; .dbg2.json / .source_map.txt, la relación entre ejecución y fuentes; .build_report.json, el resumen. Generar JSON con --emit-ai-metadata hace más clara la conexión con SARAKURA.

12. Leer el primer error

Empieza por el archivo, la línea y el número KQ del primer error. Los siguientes pueden ser consecuencias del mismo fallo de sintaxis. Ante un símbolo indefinido, comprueba declaración, implementación e inclusión en la compilación. Si la ROM no cabe, revisa tamaños de recursos y funciones y la distribución por bancos.

powershell
.\kitaqgb\kitaqgb.exe kqhelp KQ1002
.\kitaqgb\kitaqgb.exe symfind main --map=.\out\game.map
.\kitaqgb\kitaqgb.exe romdiff .\out\before.gb .\out\after.gb

13. Ensamblador en línea

__asm { ... } acepta los nombres de instrucciones de KITAQGB, no cualquier fuente escrita para otro ensamblador de GB. Algunas grafías internas son del tipo LD_A_IMM. Antes de usarlo, comprende los argumentos, retornos, registros preservados y efectos sobre la pila. El apéndice enumera instrucciones y formatos de operandos.

c
void nop_example() {
    __asm { NOP }
}

14. Ejemplo práctico: desarrollar un juego con IA

Completa los requisitos y entrega el prompt íntegro a tu asistente de IA. Incluye implementación, pruebas en el emulador, análisis con SARAKURA y verificación de las correcciones.

· Descargar Markdown

Completa los requisitos y entrega este documento íntegro al asistente de IA. Los comandos suponen que los repositorios kitaqgb, kitaqfc, kokura, kurosaki, sarakura y kitaq-docs, junto con el proyecto game-gb o game-fc, comparten un directorio padre. Ejecútalos desde ese directorio y adapta las rutas al entorno real.

Requisitos

  • Título del juego: <completar>
  • Género y mecánica principal: <completar>
  • Controles y condiciones de éxito y fracaso: <completar>
  • Pantallas, niveles, enemigos y objetos obligatorios: <completar>
  • Estilo visual, música y efectos de sonido: <completar e indicar los recursos proporcionados>
  • Guardado, comunicación, periféricos y otros requisitos: <completar o ninguno>
  • Directorio del proyecto: <completar>
  • Condiciones de redistribución: <por ejemplo, código y recursos originales aptos para publicarse con licencia MIT>
  • Máquina de destino: <Game Boy original / compatibilidad con GB y CGB / solo CGB>
  • Rendimiento: <por ejemplo, 60 actualizaciones de la lógica por segundo durante el juego normal; definir lo aceptable en escenas exigentes>

Trabajo solicitado

Implementa el juego con KITAQGB y sus bibliotecas. Utiliza KOKURA para ejecutar y depurar, y SARAKURA para organizar los diagnósticos y comparar los resultados antes y después de una corrección.

Repite este ciclo hasta cumplir los criterios de aceptación: concretar la especificación → implementar un cambio pequeño → compilar → aplicar entradas y observar → investigar la causa → corregir → repetir las pruebas en las mismas condiciones. Un plan, un listado de código o una compilación correcta no bastan para dar el trabajo por terminado.

Comprobar el entorno y los criterios de aceptación

  1. Lee las instrucciones del directorio de trabajo, los README, los manuales HTML y las cabeceras e implementaciones de las bibliotecas que vayas a usar. Registra las rutas de los ejecutables y sus versiones o hashes SHA-256. Comprueba los comandos con la salida real de --help y las API con el código fuente.
  2. Define criterios verificables para entradas, imagen, sonido, progreso y frecuencia de actualización. Por ejemplo: pulsar y soltar START inicia la partida; una colisión resta una vida; la pausa silencia el audio indicado y al continuar se reanuda la reproducción.
  3. Pregunta solo por ambigüedades importantes. Resuelve de forma autónoma las decisiones habituales y reversibles. No rebajes los requisitos ni los criterios de aceptación.
  4. Primero ejecuta un pequeño ejemplo incluido con el compilador, el emulador y SARAKURA. Esto comprueba la conexión entre herramientas, no la finalización del juego solicitado.

Implementar una primera versión jugable

  • Utiliza el dialecto C de KITAQGB y void main(). No des por disponibles las API de C de escritorio o GBDK. Incluye los archivos .c necesarios, no solo sus declaraciones; comprueba inicialización, unidades, signo, rangos, vida útil de los búferes y bancos ROM.
  • Planifica las actualizaciones de VRAM/OAM, VBlank, interrupciones, pila, bancos ROM/WRAM y límites de tiles y sprites. La capacidad total y libre de la cola de transferencias es distinta de la capacidad y el espacio libre de la VRAM física.
  • Un juego para DMG no debe depender de funciones exclusivas de CGB. Si admite ambos modos, pruébalos por separado.
  • Para letras, números y símbolos, utiliza la fuente original proporcionada en ascii.c y comprueba la correspondencia entre caracteres y tiles.
  • Conecta primero arranque, título, personaje controlable, éxito o fracaso y reinicio. Amplía el contenido después.
  • Conserva los originales editables de gráficos, música y efectos, así como los pasos de generación. Comprueba que la compilación consume realmente los datos exportados.
  • Escribe los comentarios del código en inglés y los informes de progreso en español. Mantén los informes estándar de SARAKURA en inglés.

Vincular cada compilación con su ejecución

Separa las salidas por iteración, por ejemplo en out/iter-001. Registra comandos, códigos de salida y hashes de código, recursos, herramientas, ROM y metadatos. Nunca ejecutes una ROM anterior después de una compilación fallida. Los mapas, mapas de código fuente y datos de depuración deben corresponder a la misma compilación que la ROM.

Este es un ejemplo de comprobación básica para DMG. Prepara main.c y las implementaciones de biblioteca necesarias; adapta opciones y secuencia de entrada al juego.

powershell
$iteration = '.\game-gb\out\iter-001'
New-Item -ItemType Directory -Force $iteration | Out-Null

# Include all additional implementation units required by the game.
& '.\kitaqgb\kitaqgb.exe' '.\game-gb\src\main.c' `
  -I '.\kitaqgb\lib' -o "$iteration\game.gb" `
  --profile=dev --rst-disable --stack-bank=fixed --no-disasm `
  "--emit-ai-metadata=$iteration\build.json"
if ($LASTEXITCODE -ne 0) { throw 'Build failed; inspect the build log.' }

# This sequence presses START once, with released intervals on both sides.
& '.\kokura\kokura-cli.exe' "$iteration\game.gb" `
  --hardware dmg --run-frames 300 `
  --input-seq 'NONE:60;START:1;NONE:239' `
  --png "$iteration\frame.png" --record-wav "$iteration\audio.wav" `
  --dump-report "$iteration\run.json" `
  --emit-diagnostics "$iteration\events.jsonl"
if ($LASTEXITCODE -ne 0) { throw 'Emulator run failed; inspect the run log.' }

& '.\sarakura\sarakura.exe' gb analyze `
  --metadata "$iteration\build.json" --events "$iteration\events.jsonl" `
  --frames 300 --out "$iteration\analysis" --fail-on error
if ($LASTEXITCODE -ne 0) { throw 'Inspect the analysis report and fix the cause.' }

--hardware dmg selecciona la Game Boy original. Al probar CGB o ambos modos, ajusta de forma coherente la cabecera ROM y el hardware del emulador. La secuencia pulsa START una vez entre intervalos con los botones sueltos. Ejecutar 300 fotogramas no equivale a probar el juego completo.

Comprobar imagen, sonido, estado y rendimiento

  • Guarda escenarios que distingan pulsar, mantener y soltar. Recorre todas las rutas especificadas: arranque, inicio, movimiento, acciones, colisiones, desplazamiento, cambios de nivel, fin de partida, reinicio, pausa y, cuando corresponda, guardado o comunicación.
  • Conserva PNG de fotogramas relevantes, entradas, informes de ejecución, JSONL de diagnóstico, WAV y las observaciones necesarias de estado o memoria. Comprueba los fotogramas alcanzados y el motivo de parada. Abre las imágenes: una sola captura no demuestra movimiento ni respuesta a los controles. Compara contadores, posiciones y cambios de estado con lo esperado; revisa bordes de pantalla, límites de tiles y atributos, y escenas con muchos sprites.
  • Comprueba música, efectos, reproducción simultánea, cortes, pausa y reanudación. Crear un WAV no demuestra que el sonido sea correcto. Si no puedes escucharlo, distingue las comprobaciones numéricas o de forma de onda de las cualidades audibles aún sin verificar.
  • Mide escenas exigentes, trabajo de la CPU de destino, actualizaciones y transferencias; en FC, incluye el trabajo de NMI. La velocidad del emulador en el equipo anfitrión no es la frecuencia del juego ni prueba la velocidad en hardware real. Continuar con --allow-unimplemented no demuestra soporte para la función ausente.

Analizar, corregir y volver a probar

  • Proporciona a SARAKURA los metadatos de la ROM probada y el JSONL de diagnóstico de esa ejecución. Una traza CPU o un informe ordinario no los sustituyen. --frames establece condiciones de análisis; SARAKURA no ejecuta la ROM ni modifica automáticamente el código.
  • Lee report.html, ai_diagnostics.json, repair_prompt.md y retest_plan.json. Contrasta los diagnósticos con reproducción, imágenes, audio y código. Distingue ubicaciones o causas inferidas de hechos comprobados, y bucles de espera normales de bloqueos. Evalúa las advertencias una por una y registra eventos no soportados o límites del análisis. No ocultes advertencias con filtros ni acortes las pruebas para conseguir un resultado favorable.
  • Reduce cada fallo a un caso mínimo, corrige su causa y recompila. Si procede del compilador o emulador, aísla el defecto del código del juego y añade comprobaciones de regresión para la corrección de la herramienta.
  • Repite las pruebas con las mismas entradas, semilla aleatoria, máquina y norma de vídeo, mapper, fotogramas observados y ajustes de diagnóstico. Cada ROM requiere sus metadatos; no reutilices estados guardados a ciegas tras cambiar código o distribución de RAM.
powershell
& '.\sarakura\sarakura.exe' baseline-delta `
  --baseline '.\game-gb\out\iter-001\analysis' `
  --current '.\game-gb\out\iter-002\analysis' `
  --out '.\game-gb\out\delta.json' --markdown '.\game-gb\out\delta.md' `
  --fail-on-new error --fail-on-regression error --enforce

Usa las diferencias de diagnóstico junto con la aceptación de controles, gráficos y audio. Si el mismo fallo se repite, revisa las pruebas y la hipótesis en lugar de encadenar cambios arbitrarios.

Criterios de finalización y entregables

Repite todos los escenarios obligatorios con la ROM final compilada a partir del código y los ajustes entregados. La invencibilidad, entradas automáticas de prueba u otro mapper, por sí solos, no verifican una partida normal en la versión final. Entrega una tabla de requisitos y pruebas, explica las advertencias restantes e identifica lo no comprobado o no soportado. Si no se ha probado en hardware físico, indícalo explícitamente.

Entrega código fuente, identificación de herramientas y bibliotecas, recursos editables, scripts reproducibles de compilación y pruebas, ROM, evidencia final y un README de instalación, controles y limitaciones conocidas. Incluye reproducciones y el programa de pruebas cuando sean necesarios. Publica o envía archivos al exterior solo dentro del alcance autorizado expresamente. Elimina compilaciones intermedias y trazas temporales innecesarias tras verificarlas, conservando fuentes, recursos, entregables y evidencia de regresión necesaria.

Si el entorno o los permisos impiden una comprobación obligatoria, comunica los pasos exactos de reproducción y la acción necesaria. No des el trabajo por terminado.

Ubicación del código fuente

El código fuente del compilador y el archivo de proyecto están en el subdirectorio que lleva el nombre del repositorio. El ejecutable está en la raíz y las bibliotecas, en lib/. Consulta la estructura de directorios para conocer las rutas y los requisitos de compilación.

Programas 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.

Mostrar el juego de caracteres incluido: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Su primer texto en pantalla: La pantalla muestra HELLO WORLD y 042. El cero inicial corresponde al formato decimal de tres dígitos.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Calcular con un valor intermedio más amplio: 030 representa el resultado calculado, 30.
Ejemplo en pantalla: 030 representa el resultado calculado, 30.

Descargar código fuente / Estado de la verificación

powershell
.\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
c
// 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.

Bifurcaciones y bucles: 042 es el resultado final de estos bucles y bifurcaciones.
Ejemplo en pantalla: 042 es el resultado final de estos bucles y bifurcaciones.

Descargar código fuente / Estado de la verificación

powershell
.\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
c
// 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.
Ejemplo en pantalla: 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.

Descargar código fuente / Estado de la verificación

powershell
.\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
c
// 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.

Banderas de bits y copia de búfer: el primer byte de destino se muestra como 010; el bit 3 aporta 8 y el bit 1 aporta 2.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Contar nuevas pulsaciones de un botón: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Multiplicar valores de punto fijo Q8.8: 012 significa 3 × 4 = 12. Es el entero convertido, no el valor interno escalado por 256.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Asignar desde un pool fijo de objetos: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Registrar un valor de diagnóstico en RAM: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Guardar un historial de posiciones: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Leer el contador de fotogramas de la biblioteca: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Seleccionar el ID de la escena actual: 001 es el ID actual devuelto por scene_get_current. El ejemplo no registra callbacks de escena ni dibujo de transiciones.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Convertir coordenadas del mundo en coordenadas de pantalla: 052 menos 010 da el valor mostrado, 042. Es un cálculo de coordenadas; no desplaza el fondo.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.
Ejemplo en pantalla: el último byte, out[2], se muestra como 042. Los tres bytes descomprimidos deben contener 42.

Descargar código fuente / Estado de la verificación

powershell
.\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
c
// 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.

Leer y escribir una casilla del tablero: 042 es el valor leído de nuevo. El programa no dibuja el tablero de 4×4.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Avanzar una caja un paso de física: 042 es la coordenada X resultante, medida en píxeles enteros en este ejemplo. No se dibuja una caja ni una animación continua.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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();  }
}
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.

Leer datos de ROM con un argumento de banco: El segundo byte de destino se muestra como 042. Este ejemplo lee ROM fija; no demuestra una transferencia entre distintos bancos conmutables.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Cargar datos por ID de recurso: El segundo byte se muestra como 042. Esto demuestra la búsqueda y copia de recursos, no el renderizado de imágenes.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Asignar un proyectil: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Configurar una paleta de fondo de CGB: Ejecútelo en modo CGB para ver texto violeta y 042 sobre blanco. La salida monocroma de DMG no permite comprobar estos colores.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Desplazar el fondo horizontalmente: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Encolar una actualización de tile: Busque un único 4 en la coordenada de tile del fondo (3,8). Es una actualización de tile, no un contador numérico.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Mostrar un sprite mediante DMA de OAM: aparece una letra A inmóvil en la posición indicada para el sprite. En un juego, compruebe si falló la asignación antes de utilizar el ID devuelto.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Generar un valor aleatorio reproducible: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Guardar una bandera de estado del juego: 001 significa que el indicador 12 es verdadero. El ejemplo opera sobre indicadores en RAM y no los conserva al apagar y encender.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Avanzar un círculo un paso de física: 042 es la coordenada X entera resultante. El ejemplo no dibuja un contorno circular ni una animación de colisión.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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.

Procesar un efecto de sonido en cada fotograma: 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.
Ejemplo en pantalla: 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

powershell
.\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
c
// 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

c
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);

Parámetros

dst

Destino 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.

bank

Selector 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.

src

Puntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.

len

Cantidad 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

c
__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.

__farmemcpy: DMG
DMG
__farmemcpy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementació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

c
void __vram_copy(u16 dst, const void* src, u16 len);

Parámetros

dst

Direcció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.

src

Puntero 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.

len

Nú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

c
__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.

__vram_copy: DMG
DMG
__vram_copy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __farcall_ptr(u8 bank, const void* func);

Parámetros

bank

Nú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.

func

Direcció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

c
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.

__farcall_ptr: DMG
DMG
__farcall_ptr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/bank.h:15

kitaqgb/kitaqgb/CodeGenerator.cs:8335

csharp
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

c
u16 __farpeek16(u8 bank, const void* addr);

Parámetros

bank

Nú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.

addr

Puntero 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

c
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.

__farpeek16: DMG
DMG
__farpeek16: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/bank.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:8295

csharp
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

c
u8 __farpeek8(u8 bank, const void* addr);

Parámetros

bank

Nú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.

addr

Puntero 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

c
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.

__farpeek8: DMG
DMG
__farpeek8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/bank.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:8262

csharp
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

c
u8 __cgb_is_cgb();

Valor devuelto

u8: 0 para modo DMG, 1 para modo CGB.

Ejemplo de uso

c
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.

__cgb_is_cgb: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_is_cgb: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_is_cgb: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_is_cgb: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/cgb_palette.h:8

Notas de la declaración (original)
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

c
void __cgb_safe_set_bcpd(u8 value);

Parámetros

value

Un 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

c
__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.

__cgb_safe_set_bcpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bcpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bcpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bcpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
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

c
__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.

__cgb_safe_set_bcps: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bcps: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bcps: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bcps: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
void __cgb_safe_set_ocpd(u8 value);

Parámetros

value

Un 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

c
__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.

__cgb_safe_set_ocpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_ocpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_ocpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_ocpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
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

c
__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.

__cgb_safe_set_ocps: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_ocps: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_ocps: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_ocps: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
void __cgb_safe_set_vbk(u8 value);

Parámetros

value

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.

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

c
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.

__cgb_safe_set_vbk: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_vbk: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_vbk: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_vbk: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:28

Notas de la declaración (original)
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

c
void __settile(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settile: DMG
DMG
__settile: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:31

Notas de la declaración (original)
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

c
void __settile_bulk(u16 dest, const u8* src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settile_bulk: DMG
DMG
__settile_bulk: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:48

Notas de la declaración (original)
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

c
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settile_bulk_fast: DMG
DMG
__settile_bulk_fast: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:50

Notas de la declaración (original)
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

c
void __settile_unsafe(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settile_unsafe: DMG
DMG
__settile_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:33

Notas de la declaración (original)
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

c
void __settileat(u16 base, u8 x, u8 y, u8 tile);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settileat: DMG
DMG
__settileat: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:35

Notas de la declaración (original)
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

c
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settileat_unsafe: DMG
DMG
__settileat_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:37

Notas de la declaración (original)
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

c
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settileatattr: DMG
DMG
__settileatattr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:62

Notas de la declaración (original)
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

c
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settileatattr_unsafe: DMG
DMG
__settileatattr_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:64

Notas de la declaración (original)
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

c
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settileatcgb: DMG
DMG
__settileatcgb: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:66

Notas de la declaración (original)
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

c
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settileatcgb_unsafe: DMG
DMG
__settileatcgb_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:68

Notas de la declaración (original)
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

c
void __settileattr(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settileattr: DMG
DMG
__settileattr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:53

Notas de la declaración (original)
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

c
void __settileattr_bulk(u16 dest, const u8* src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settileattr_bulk: DMG
DMG
__settileattr_bulk: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:89

Notas de la declaración (original)
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

c
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settileattr_bulk_fast: DMG
DMG
__settileattr_bulk_fast: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:91

Notas de la declaración (original)
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

c
void __settileattr_unsafe(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settileattr_unsafe: DMG
DMG
__settileattr_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:55

Notas de la declaración (original)
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

c
void __settilebg(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settilebg: DMG
DMG
__settilebg: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:43

Notas de la declaración (original)
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

c
void __settilebg_unsafe(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settilebg_unsafe: DMG
DMG
__settilebg_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:45

Notas de la declaración (original)
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

c
void __settilebgattr(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settilebgattr: DMG
DMG
__settilebgattr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:80

Notas de la declaración (original)
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

c
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settilebgattr_unsafe: DMG
DMG
__settilebgattr_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:82

Notas de la declaración (original)
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

c
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilebgcgb: DMG
DMG
__settilebgcgb: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:84

Notas de la declaración (original)
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

c
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilebgcgb_unsafe: DMG
DMG
__settilebgcgb_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:86

Notas de la declaración (original)
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

c
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilecgb: DMG
DMG
__settilecgb: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:57

Notas de la declaración (original)
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

c
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

tile_src, attr_src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settilecgb_bulk: DMG
DMG
__settilecgb_bulk: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:93

Notas de la declaración (original)
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

c
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);

Parámetros

dest

Dirección del primer byte en VRAM. Para un mapa, usa 0x9800–0x9FFF y mantén toda la transferencia dentro del mapa elegido.

tile_src, attr_src

Bytes 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.

count

Cantidad 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

c
// __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.

text
ABC
__settilecgb_bulk_fast: DMG
DMG
__settilecgb_bulk_fast: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:95

Notas de la declaración (original)
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

c
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilecgb_unsafe: DMG
DMG
__settilecgb_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:59

Notas de la declaración (original)
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

c
void __settilewin(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settilewin: DMG
DMG
__settilewin: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:39

Notas de la declaración (original)
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

c
void __settilewin_unsafe(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settilewin_unsafe: DMG
DMG
__settilewin_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:41

Notas de la declaración (original)
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

c
void __settilewinattr(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settilewinattr: DMG
DMG
__settilewinattr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:71

Notas de la declaración (original)
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

c
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);

Parámetros

x, y

Coordenadas 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.

attr

Byte 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

c
// __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.

text
A
__settilewinattr_unsafe: DMG
DMG
__settilewinattr_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:73

Notas de la declaración (original)
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

c
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilewincgb: DMG
DMG
__settilewincgb: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:75

Notas de la declaración (original)
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

c
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Parámetros

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
A
__settilewincgb_unsafe: DMG
DMG
__settilewincgb_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/cgb_tile.h:77

Notas de la declaración (original)
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

c
u16 __readpadex(u8 prev_keys);

Parámetros

prev_keys

La 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

c
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.

__readpadex: DMG
DMG
__readpadex: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-cache
Implementació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

c
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

symbol

Nombre 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

c
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.

__bankof: DMG
DMG
__bankof: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:432

kitaqgb/kitaqgb/CodeGenerator.cs:7360

csharp
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

c
void __bankswitch(u8 bank);

Parámetros

bank

Nú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

c
__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.

__bankswitch: DMG
DMG
__bankswitch: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/bank.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7268

csharp
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

c
__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

value

Un 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

c
__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.

__cgb_safe_set_bgpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bgpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bgpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bgpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
__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

c
__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.

__cgb_safe_set_bgpi: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bgpi: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bgpi: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bgpi: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_hdma1: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma1: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma1: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_hdma2: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma2: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma2: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_hdma3: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma3: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma3: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_hdma4: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma4: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma4: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_hdma5: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma5: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma5: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
__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

value

Un 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

c
__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.

__cgb_safe_set_obpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_obpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_obpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_obpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
__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

c
__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.

__cgb_safe_set_obpi: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_obpi: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_obpi: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_obpi: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementació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

c
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

value

Valor 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

c
__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.

__cgb_safe_set_svbk: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:1862

__critical_enter

Incrementa la profundidad de anidamiento del software y ejecuta DI.

Sintaxis

c
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

c
*((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.

__critical_enter: DMG
DMG
__critical_enter: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7970

kitaqgb/kitaqgb/CodeGenerator.cs:7970

csharp
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

c
void __critical_leave(u8 token);

Parámetros

token

Token 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

c
__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.

__critical_leave: DMG
DMG
__critical_leave: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7984

kitaqgb/kitaqgb/CodeGenerator.cs:7984

csharp
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

c
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Parámetros

a0

Palabra sin signo de 0 a 65535.

a1

Palabra sin signo de 0 a 65535.

b0

Coeficiente sin signo de 0 a 255.

b1

Coeficiente 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

c
// 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.

__dot2_q8_8: DMG
DMG
__dot2_q8_8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11575

kitaqgb/kitaqgb/CodeGenerator.cs:11575

csharp
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

c
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parámetros

a0

Palabra sin signo de 0 a 65535.

a1

Palabra sin signo de 0 a 65535.

a2

Palabra sin signo de 0 a 65535.

b0

Coeficiente sin signo de 0 a 255.

b1

Coeficiente sin signo de 0 a 255.

b2

Coeficiente 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

c
// 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.

__dot3_q8_8: DMG
DMG
__dot3_q8_8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11550

kitaqgb/kitaqgb/CodeGenerator.cs:11550

csharp
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

c
__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

bank

Nú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.

func

Nombre 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

c
__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.

__farcall: DMG
DMG
__farcall: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7288

kitaqgb/kitaqgb/CodeGenerator.cs:7288

csharp
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

c
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

c
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.

__getwinmapbase: DMG
DMG
__getwinmapbase: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10642

kitaqgb/kitaqgb/CodeGenerator.cs:10642

csharp
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

c
u16 __mac16(u16 acc, u16 a, u8 b);

Parámetros

acc

Acumulador de 16 bits que se va a sumar.

a

Palabra sin signo de 0 a 65535.

b

Coeficiente 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

c
// 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.

__mac16: DMG
DMG
__mac16: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11499

kitaqgb/kitaqgb/CodeGenerator.cs:11499

csharp
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

c
u16 __mul16x8(u16 a, u8 b);

Parámetros

a

Palabra sin signo de 0 a 65535.

b

Coeficiente 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

c
// 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.

__mul16x8: DMG
DMG
__mul16x8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11467

kitaqgb/kitaqgb/CodeGenerator.cs:11467

csharp
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

c
u8 __mul8x8_hi(u8 a, u8 b);

Parámetros

a

Byte sin signo de 0 a 255.

b

Byte sin signo de 0 a 255.

Valor devuelto

floor(a*b/256), en el rango 0..254.

Ejemplo de uso

c
// 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.

__mul8x8_hi: DMG
DMG
__mul8x8_hi: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11452

kitaqgb/kitaqgb/CodeGenerator.cs:11452

csharp
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

c
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

state

Puntero 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.

keys

Máscara completa de botones mantenidos con el formato GB BTN_*. Pasa una lectura de __readpad, el byte bajo de __readpadex o input_current; la función de repetición no lee el hardware.

trigger

Bits recién pulsados en esta actualización, con el mismo formato que keys: normalmente keys & ~previous_keys o el byte alto de __readpadex. Incluye solo bits presentes también en keys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.

mask

Bits 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

c
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.

__padrep: DMG
DMG
__padrep: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    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

c
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

state

Puntero 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.

keys

Máscara completa de botones mantenidos con el formato GB BTN_*. Pasa una lectura de __readpad, el byte bajo de __readpadex o input_current; la función de repetición no lee el hardware.

trigger

Bits recién pulsados en esta actualización, con el mismo formato que keys: normalmente keys & ~previous_keys o el byte alto de __readpadex. Incluye solo bits presentes también en keys. 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

c
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.

__padrep_down: DMG
DMG
__padrep_down: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11069

csharp
{
    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

c
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

state

Puntero 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.

das

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.

arr

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.

Valor devuelto

Ninguno (void).

Ejemplo de uso

c
__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.

__padrep_init: DMG
DMG
__padrep_init: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10808

csharp
{
    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

c
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

state

Puntero 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.

keys

Máscara completa de botones mantenidos con el formato GB BTN_*. Pasa una lectura de __readpad, el byte bajo de __readpadex o input_current; la función de repetición no lee el hardware.

trigger

Bits recién pulsados en esta actualización, con el mismo formato que keys: normalmente keys & ~previous_keys o el byte alto de __readpadex. Incluye solo bits presentes también en keys. 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

c
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.

__padrep_lr: DMG
DMG
__padrep_lr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10864

csharp
{
    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

c
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

state

Puntero 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.

keys

Máscara completa de botones mantenidos con el formato GB BTN_*. Pasa una lectura de __readpad, el byte bajo de __readpadex o input_current; la función de repetición no lee el hardware.

trigger

Bits recién pulsados en esta actualización, con el mismo formato que keys: normalmente keys & ~previous_keys o el byte alto de __readpadex. Incluye solo bits presentes también en keys. Pasar siempre la máscara mantenida reinicia erróneamente la pulsación inicial en cada llamada.

mask

Bits 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

c
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.

__padrep_mask: DMG
DMG
__padrep_mask: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    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

c
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

state

Puntero 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

c
__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.

__padrep_reset: DMG
DMG
__padrep_reset: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10840

csharp
{
    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

c
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

c
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.

__readpad: DMG
DMG
__readpad: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-cache
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10669

csharp
{
    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

c
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

c
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.

__readpadbtn: DMG
DMG
__readpadbtn: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-cache
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10729

csharp
{
    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

c
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

c
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.

__readpaddir: DMG
DMG
__readpaddir: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-cache
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10706

csharp
{
    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

c
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);

Parámetros

a0

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a1

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b0

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b1

Patró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

c
// 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.

__sdot2_q1_7: DMG
DMG
__sdot2_q1_7: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11732

kitaqgb/kitaqgb/CodeGenerator.cs:11732

csharp
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

c
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Parámetros

a0

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a1

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b0

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b1

Patró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

c
// 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.

__sdot2_q8_8: DMG
DMG
__sdot2_q8_8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11654

kitaqgb/kitaqgb/CodeGenerator.cs:11654

csharp
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

c
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parámetros

a0

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a1

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a2

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b0

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b1

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b2

Patró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

c
// 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.

__sdot3_q1_7: DMG
DMG
__sdot3_q1_7: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11790

kitaqgb/kitaqgb/CodeGenerator.cs:11790

csharp
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

c
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parámetros

a0

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a1

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

a2

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b0

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b1

Patrón de bits de un byte con signo, coeficiente de −128 a 127.

b2

Patró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

c
// 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.

__sdot3_q8_8: DMG
DMG
__sdot3_q8_8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11767

kitaqgb/kitaqgb/CodeGenerator.cs:11767

csharp
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

c
__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, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settile_fast: DMG
DMG
__settile_fast: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:9321

csharp
{
                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

c
__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

buf

Bú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 son 0,1,32,33 desde allí.

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
AB
CD
__settilebg16_buf: DMG
DMG
__settilebg16_buf: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10564

csharp
{
                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

c
__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

buf

Bú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, count

El orden es y,x,count. y es una fila de celdas de 16 píxeles (0–15); x y count se miden en tiles de 8 píxeles. Mantén x+count <= 32. Para una celda en (2,3), usa y=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

c
// __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.

text
AB
CD
__settilebg16_flush: DMG
DMG
__settilebg16_flush: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10581

csharp
{
                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

c
__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, attrbuf

Bú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 son 0,1,32,33 desde allí.

x, y

Coordenadas 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.

tile

Nú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.

attr

Byte 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

c
// __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.

text
AB
CD
__settilebg16cgb_buf: DMG
DMG
__settilebg16cgb_buf: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10572

csharp
{
                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

c
__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, attrbuf

Bú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, count

El orden es y,x,count. y es una fila de celdas de 16 píxeles (0–15); x y count se miden en tiles de 8 píxeles. Mantén x+count <= 32. Para una celda en (2,3), usa y=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

c
// __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.

text
AB
CD
__settilebg16cgb_flush: DMG
DMG
__settilebg16cgb_flush: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:10589

csharp
{
                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

c
u16 __smac16(u16 acc, u16 a, u8 b);

Parámetros

acc

Acumulador de 16 bits que se va a sumar.

a

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b

Patró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

c
// 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.

__smac16: DMG
DMG
__smac16: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11516

kitaqgb/kitaqgb/CodeGenerator.cs:11516

csharp
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

c
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);

Parámetros

acc

Acumulador de 16 bits que se va a sumar.

a

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b

Patró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

c
// 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.

__smac16_q1_7: DMG
DMG
__smac16_q1_7: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11533

kitaqgb/kitaqgb/CodeGenerator.cs:11533

csharp
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

c
u16 __smul16x8_q1_7(u16 a, u8 b);

Parámetros

a

Patrón de bits de 16 bits con signo que representa de −32768 a 32767.

b

Patró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

c
// 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.

__smul16x8_q1_7: DMG
DMG
__smul16x8_q1_7: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:11490

kitaqgb/kitaqgb/CodeGenerator.cs:11490

csharp
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

c
u8 __sram_read8(u16 address);

Parámetros

address

Direcció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

c
*((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.

__sram_read8: DMG
DMG
__sram_read8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:8825

kitaqgb/kitaqgb/CodeGenerator.cs:8825

csharp
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

c
void __sram_write8(u16 address, u8 value);

Parámetros

address

Dirección de CPU en $A000..$BFFF.

value

Valor 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

c
__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.

__sram_write8: DMG
DMG
__sram_write8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:8846

kitaqgb/kitaqgb/CodeGenerator.cs:8846

csharp
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

c
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

c
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.

__svbk_get: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7387

csharp
{
                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

c
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

bank

Banco 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

c
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.

__svbk_set: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7397

csharp
{
                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

c
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

dst

Direcció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.

src

Puntero 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.

len

Nú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

c
// 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.

__vram_memcpy_unsafe: DMG
DMG
__vram_memcpy_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:8104

csharp
{
                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

c
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

dst

Direcció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.

value

Un 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.

len

Nú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

c
__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.

__vram_memset_unsafe: DMG
DMG
__vram_memset_unsafe: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:8196

csharp
{
                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

c
void __wait_ly(u8 ly);

Parámetros

ly

Lí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

c
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.

__wait_ly: DMG
DMG
__wait_ly: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/kitaqgb/CodeGenerator.cs:7483

kitaqgb/kitaqgb/CodeGenerator.cs:7483

csharp
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

c
s16 __smul16x8(s16 a, s8 b);

Parámetros

a

Valor de 16 bits con signo al que se aplica el factor de escala.

b

Coeficiente 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

c
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.

__smul16x8: DMG — 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.
DMG — 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.
__smul16x8: CGB — 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.
CGB — 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.

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/physics2d.h:9

Notas de la declaración (original)
Multiply a signed word by a signed byte; divide by 256, truncating toward zero.

kitaqgb/kitaqgb/CodeGenerator.cs:11478

csharp
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

c
void __bit_clear(u8* p, u16 bit_index);

Parámetros

p

Puntero 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 u16 desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona 0x01 en el byte 0; el 7, 0x80 en el byte 0; el 8, 0x01 en el byte 1. En general, el desplazamiento es index / 8 y la máscara 1 << (index % 8). Para un array de N bytes, exige index < 8*N y 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

c
__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.

__bit_clear: DMG
DMG
__bit_clear: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:187

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
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

c
void __bit_set(u8* p, u16 bit_index);

Parámetros

p

Puntero 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 u16 desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona 0x01 en el byte 0; el 7, 0x80 en el byte 0; el 8, 0x01 en el byte 1. En general, el desplazamiento es index / 8 y la máscara 1 << (index % 8). Para un array de N bytes, exige index < 8*N y 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

c
__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.

__bit_set: DMG
DMG
__bit_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:186

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
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

c
u8 __bit_test(const u8* p, u16 bit_index);

Parámetros

p

Puntero 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 u16 desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona 0x01 en el byte 0; el 7, 0x80 en el byte 0; el 8, 0x01 en el byte 1. En general, el desplazamiento es index / 8 y la máscara 1 << (index % 8). Para un array de N bytes, exige index < 8*N y 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

c
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.

__bit_test: DMG
DMG
__bit_test: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:185

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
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

c
void __bit_toggle(u8* p, u16 bit_index);

Parámetros

p

Puntero 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 u16 desde cero (0–65535), no un desplazamiento de byte ni una máscara. El índice 0 selecciona 0x01 en el byte 0; el 7, 0x80 en el byte 0; el 8, 0x01 en el byte 1. En general, el desplazamiento es index / 8 y la máscara 1 << (index % 8). Para un array de N bytes, exige index < 8*N y 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

c
__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.

__bit_toggle: DMG
DMG
__bit_toggle: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:188

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
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

c
void __copy16(void* dst, const void* src);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

src

Puntero 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

c
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.

__copy16: DMG
DMG
__copy16: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:177

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
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

c
void __copy32(void* dst, const void* src);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

src

Puntero 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

c
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.

__copy32: DMG
DMG
__copy32: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:178

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
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

c
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);

Parámetros

dst

Destino 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.

bank

Selector 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.

src

Puntero cercano a bytes legibles del banco de ROM seleccionado. No incluye el número de banco.

len

Cantidad 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

c
__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.

__far_memcpy: DMG
DMG
__far_memcpy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementació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

c
void __fill_tilemap(u16 dst, u8 value, u16 len);

Parámetros

dst

Direcció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.

value

Un 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.

len

Nú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

c
__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.

__fill_tilemap: DMG
DMG
__fill_tilemap: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

c
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.

__getbgmapbase: DMG
DMG
__getbgmapbase: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:140

kitaqgb/kitaqgb/CodeGenerator.cs:10619

csharp
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

c
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);

Parámetros

x1

Coordenada X del primer punto, de 0 a 255.

y1

Coordenada Y del primer punto, de 0 a 255.

x2

Coordenada X del segundo punto, de 0 a 255.

y2

Coordenada 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

c
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.

__manhattan: DMG
DMG
__manhattan: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:183

kitaqgb/kitaqgb/CodeGenerator.cs:8516

csharp
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

c
u16 __map_index(u8 x, u8 y, u8 width);

Parámetros

x

Columna de 0 a 255, normalmente menor que width.

y

Fila de 0 a 255.

width

Celdas por fila, de 0 a 255.

Valor devuelto

Devuelve y*width+x como u16; no valida las coordenadas.

Ejemplo de uso

c
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.

__map_index: DMG
DMG
__map_index: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:181

kitaqgb/kitaqgb/CodeGenerator.cs:8365

csharp
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

c
void __memcpy(void* dst, const void* src, u16 count);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

src

Puntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.

count

Nú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

c
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.

__memcpy: DMG
DMG
__memcpy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:142

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
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

c
void __memcpy_small(void* dst, const void* src, u8 len);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

src

Puntero al primer byte legible del origen. Todo el intervalo debe permanecer accesible y sin cambios durante la copia.

len

Nú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

c
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.

__memcpy_small: DMG
DMG
__memcpy_small: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:175

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
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

c
void __memset(void* dst, u8 value, u16 count);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

value

Byte que se repite en cada dirección del destino: 0–255. Usa 0 para poner el intervalo a cero. Para inicializar un array de u16 con un número distinto de cero, asigna ese valor a cada elemento.

count

Nú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

c
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.

__memset: DMG
DMG
__memset: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:141

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
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

c
void __memset_small(void* dst, u8 value, u8 len);

Parámetros

dst

Puntero al primer byte escribible del destino. Reserva espacio contiguo para toda la operación; la función no asigna un búfer.

value

Byte que se repite en cada dirección del destino: 0–255. Usa 0 para poner el intervalo a cero. Para inicializar un array de u16 con un número distinto de cero, asigna ese valor a cada elemento.

len

Nú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

c
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.

__memset_small: DMG
DMG
__memset_small: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:176

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
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

c
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);

Parámetros

dst

Direcció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.

bank

Nú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.

src

Puntero 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

c
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.

__rle_decode_vram: DMG
DMG
__rle_decode_vram: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:191

Notas de la declaración (original)
Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.

kitaqgb/kitaqgb/CodeGenerator.cs:8673

csharp
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

c
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

c
__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.

__rng8: DMG
DMG
__rng8: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:149

kitaqgb/kitaqgb/CodeGenerator.cs:8881

csharp
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

c
void __rng_seed(u16 seed);

Parámetros

seed

Semilla 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

c
__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.

__rng_seed: DMG
DMG
__rng_seed: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementación y código fuente

kitaqgb/lib/rpg.h:150

kitaqgb/kitaqgb/CodeGenerator.cs:8868

csharp
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

c
void __scroll_bg_set(u8 scx, u8 scy);

Parámetros

scx

Desplazamiento horizontal de 0 a 255 píxeles.

scy

Desplazamiento 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

c
__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.

__scroll_bg_set: DMG
DMG
__scroll_bg_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:151

kitaqgb/kitaqgb/CodeGenerator.cs:7507

csharp
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

c
void __settile_col(u8 x, u8 y, const u8* src, u8 len);

Parámetros

x, y

Coordenadas 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.

src

Bytes 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.

len

Cantidad 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

c
// __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.

text
A
B
C
__settile_col: DMG
DMG
__settile_col: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);

Parámetros

x, y

Coordenadas 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, h

Ancho y alto en tiles de 8×8. Mantén x+w <= 32 e y+h <= 32. Si alguna dimensión es cero, no se escribe nada.

tile

Nú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

c
// __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.

text
AAA
AAA
__settile_rect: DMG
DMG
__settile_rect: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __settile_row(u8 x, u8 y, const u8* src, u8 len);

Parámetros

x, y

Coordenadas 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.

src

Bytes 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.

len

Cantidad 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

c
// __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.

text
ABC
__settile_row: DMG
DMG
__settile_row: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __settile_xy(u8 x, u8 y, u8 tile);

Parámetros

x, y

Coordenadas 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.

tile

Nú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

c
// __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.

text
A
__settile_xy: DMG
DMG
__settile_xy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);

Parámetros

base

base elige el mapa (0x9800 o 0x9C00); la dirección del byte es base + 32*y + x.

x, y

Coordenadas 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, h

Ancho y alto en tiles de 8×8. Mantén x+w <= 32 e y+h <= 32. Si alguna dimensión es cero, no se escribe nada.

src

Bytes 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

c
// __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.

text
ABC
DEF
__settilemap_rect: DMG
DMG
__settilemap_rect: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
u16 __tile_addr(u16 base, u8 x, u8 y);

Parámetros

base

Base del mapa de tiles, normalmente $9800/$9C00.

x

Columna, normalmente de 0 a 31.

y

Fila, normalmente de 0 a 31.

Valor devuelto

Devuelve (base + y*32 + x) módulo 65536 sin acceder a memoria.

Ejemplo de uso

c
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.

__tile_addr: DMG
DMG
__tile_addr: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:180

kitaqgb/kitaqgb/CodeGenerator.cs:8355

csharp
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

c
void __vram_copy_dma(u16 dst, const void* src, u16 len);

Parámetros

dst

Direcció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.

src

Puntero 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.

len

Nú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

c
__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.

__vram_copy_dma: DMG
DMG
__vram_copy_dma: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __vram_copy_hblank(u16 dst, const void* src, u16 len);

Parámetros

dst

Direcció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.

src

Puntero 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.

len

Nú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

c
__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.

__vram_copy_hblank: DMG
DMG
__vram_copy_hblank: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __vram_fill(u16 dst, u8 value, u16 len);

Parámetros

dst

Direcció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.

value

Un 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.

len

Nú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

c
__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.

__vram_fill: DMG
DMG
__vram_fill: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __vram_memcpy(u16 dst, const void* src, u16 len);

Parámetros

dst

Direcció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.

src

Puntero 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.

len

Nú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

c
__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.

__vram_memcpy: DMG
DMG
__vram_memcpy: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
void __vram_memset(u16 dst, u8 value, u16 len);

Parámetros

dst

Direcció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.

value

Un 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.

len

Nú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

c
__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.

__vram_memset: DMG
DMG
__vram_memset: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementació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

c
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

c
__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.

__wait_vblank: DMG
DMG
__wait_vblank: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:138

kitaqgb/kitaqgb/CodeGenerator.cs:7451

csharp
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

c
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);

Parámetros

x

X del punto, de 0 a 255.

y

Y del punto, de 0 a 255.

rx

Borde izquierdo del rectángulo, de 0 a 255.

ry

Borde superior del rectángulo, de 0 a 255.

rw

Ancho de 0 a 255.

rh

Alto 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

c
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.

__xy_in_rect: DMG
DMG
__xy_in_rect: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=none
Implementación y código fuente

kitaqgb/lib/rpg.h:182

kitaqgb/kitaqgb/CodeGenerator.cs:8436

csharp
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

c
void __scroll_bg_add(s8 dx, s8 dy);

Parámetros

dx

Desplazamiento horizontal con signo en píxeles, de −128 a 127.

dy

Desplazamiento 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

c
__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.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:37

kitaqgb/kitaqgb/CodeGenerator.cs:7835

csharp
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

c
void __scroll_bg_set_buffered(u8 scx, u8 scy);

Parámetros

scx

Desplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.

scy

Desplazamiento 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

c
__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.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:22

kitaqgb/kitaqgb/CodeGenerator.cs:7671

csharp
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

c
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

c
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.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:7571

csharp
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

c
void __scroll_bg_x_set(u8 scx);

Parámetros

scx

Desplazamiento 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

c
__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.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7535

csharp
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

c
void __scroll_bg_x_set_buffered(u8 scx);

Parámetros

scx

Desplazamiento 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

c
__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.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:23

kitaqgb/kitaqgb/CodeGenerator.cs:7693

csharp
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

c
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

c
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.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:14

kitaqgb/kitaqgb/CodeGenerator.cs:7580

csharp
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

c
void __scroll_bg_y_set(u8 scy);

Parámetros

scy

Desplazamiento 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

c
__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.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:7553

csharp
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

c
void __scroll_bg_y_set_buffered(u8 scy);

Parámetros

scy

Desplazamiento 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

c
__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.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:24

kitaqgb/kitaqgb/CodeGenerator.cs:7707

csharp
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

c
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

c
__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.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:28

kitaqgb/kitaqgb/CodeGenerator.cs:7771

csharp
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

c
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

c
__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.

__scroll_split_commit: DMG
DMG
__scroll_split_commit: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:33

kitaqgb/kitaqgb/CodeGenerator.cs:7959

csharp
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

c
void __scroll_split_push(u8 ly, u8 scx, u8 scy);

Parámetros

ly

Nú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.

scx

Desplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.

scy

Desplazamiento 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

c
__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.

__scroll_split_push: DMG
DMG
__scroll_split_push: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:31

kitaqgb/kitaqgb/CodeGenerator.cs:7907

csharp
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

c
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);

Parámetros

ly

Nú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.

scx

Desplazamiento horizontal del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve a la izquierda.

scy

Desplazamiento vertical del fondo, de 0 a 255 píxeles; al aumentarlo, el fondo se mueve hacia arriba.

wx

Byte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.

wy

Parte superior de la ventana en píxeles Y de pantalla, de 0 a 255; las líneas visibles van de 0 a 143.

flags

Bits 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

c
__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.

__scroll_split_push_ex: DMG
DMG
__scroll_split_push_ex: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:32

kitaqgb/kitaqgb/CodeGenerator.cs:7929

csharp
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

c
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

c
__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.

__scroll_split_reset: DMG
DMG
__scroll_split_reset: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:30

kitaqgb/kitaqgb/CodeGenerator.cs:7897

csharp
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

c
void __scroll_win_add(s8 dx, s8 dy);

Parámetros

dx

Desplazamiento horizontal con signo en píxeles, de −128 a 127.

dy

Desplazamiento 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

c
__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.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:38

kitaqgb/kitaqgb/CodeGenerator.cs:7866

csharp
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

c
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

c
__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.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:36

kitaqgb/kitaqgb/CodeGenerator.cs:7821

csharp
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

c
void __scroll_win_set(u8 wx, u8 wy);

Parámetros

wx

Byte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.

wy

Parte 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

c
__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.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:16

kitaqgb/kitaqgb/CodeGenerator.cs:7589

csharp
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

c
void __scroll_win_set_buffered(u8 wx, u8 wy);

Parámetros

wx

Byte WX sin ajustar, de 0 a 255: suma 7 a la X de pantalla deseada; el borde izquierdo usa 7.

wy

Parte 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

c
__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.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:25

kitaqgb/kitaqgb/CodeGenerator.cs:7721

csharp
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

c
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

c
__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.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:35

kitaqgb/kitaqgb/CodeGenerator.cs:7807

csharp
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

c
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

c
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.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:19

kitaqgb/kitaqgb/CodeGenerator.cs:7653

csharp
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

c
void __scroll_win_x_set(u8 wx);

Parámetros

wx

Byte 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

c
__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.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:17

kitaqgb/kitaqgb/CodeGenerator.cs:7617

csharp
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

c
void __scroll_win_x_set_buffered(u8 wx);

Parámetros

wx

Byte 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

c
__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.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:26

kitaqgb/kitaqgb/CodeGenerator.cs:7743

csharp
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

c
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

c
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.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:20

kitaqgb/kitaqgb/CodeGenerator.cs:7662

csharp
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

c
void __scroll_win_y_set(u8 wy);

Parámetros

wy

Parte 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

c
__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.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:18

kitaqgb/kitaqgb/CodeGenerator.cs:7635

csharp
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

c
void __scroll_win_y_set_buffered(u8 wy);

Parámetros

wy

Parte 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

c
__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.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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=64k

Ejemplo de uso

c
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.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implementación y código fuente

kitaqgb/lib/scroll.h:27

kitaqgb/kitaqgb/CodeGenerator.cs:7757

csharp
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

c
void __oam_dma(u16 src_ptr);

Parámetros

src_ptr

Dirección de origen en bytes. Solo se utiliza el byte alto: 0xC480 selecciona 0xC400–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

c
// 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.

__oam_dma: DMG
DMG
__oam_dma: CGB
CGB

Programa de ejemplo completo

Comando de compilación

powershell
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-disasm
Implementación y código fuente

kitaqgb/lib/sprite.h:46

csharp
// 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
help
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
usage: kitaqgb kqhelp <KQxxxx>
usage
usage: kitaqgb symfind <pattern> [--map=<.map>] [--dbg=<.dbg>] [--regex] [--ignore-case] [--out=<file>|-]
usage
usage: kitaqgb src2asm <source.c:line> [--disasm=<dis.s>] [--funcsizes=<.funcsizes.txt>] [--context=N] [--out=<file>|-]
usage
usage: kitaqgb romdiff <old.gb> <new.gb> [--old-func=<.funcsizes.txt>] [--new-func=<.funcsizes.txt>] [--old-map=<.map>] [--new-map=<.map>] [--out=<file>|-]
usage
usage: kitaqgb template [out.c] [--out=<file.c>] [--cgb] [--overwrite] [--no-build-script] [--no-readme]
usage
usage: kitaqgb conventions [--out=<file>|--stdout]
usage
usage: kitaqgb snippet list | get <id> [--out=<file>|-] | emit <category|all> [--out=<file>|-]
usage
usage: kitaqgb fixhint [diag.json|stderr.log] [--in=<file>] [--out=<file>|-] [--max=N]
usage
usage: kitaqgb irsum [--ir=<syntax_tree_lowered.txt>] [--asm=<assembly_code.txt>] [--top=N] [--out=<file>|-]
usage
usage: kitaqgb devserver [--poll-ms=N] [--debounce-ms=N] [--once] [--max-builds=N] [--deps=<file>] <compile args...>
usage
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-out

Apé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.

opcodeInstrucciónFormatoEjemplo de sintaxis
00NOPIMPNOP
F3DIIMPDI
FBEIIMPEI
76HALTIMPHALT
10STOPIMM8STOP #1
C9RETIMPRET
C0RET_NZIMPRET_NZ
C8RET_ZIMPRET_Z
D0RET_NCIMPRET_NC
D8RET_CIMPRET_C
D9RETIIMPRETI
C3JPABSJP 0xC000
E9JP_HLIMPJP_HL
18JRRELJR +target
20JR_NZRELJR_NZ +target
28JR_ZRELJR_Z +target
30JR_NCRELJR_NC +target
38JR_CRELJR_C +target
CDCALLABSCALL 0xC000
C4CALL_NZABSCALL_NZ 0xC000
CCCALL_ZABSCALL_Z 0xC000
D4CALL_NCABSCALL_NC 0xC000
DCCALL_CABSCALL_C 0xC000
C2JP_NZABSJP_NZ 0xC000
CAJP_ZABSJP_Z 0xC000
D2JP_NCABSJP_NC 0xC000
DAJP_CABSJP_C 0xC000
C7RST_00IMPRST_00
CFRST_08IMPRST_08
D7RST_10IMPRST_10
DFRST_18IMPRST_18
E7RST_20IMPRST_20
EFRST_28IMPRST_28
F7RST_30IMPRST_30
FFRST_38IMPRST_38
3ELD_A_IMMIMM8LD_A_IMM #1
06LD_B_IMMIMM8LD_B_IMM #1
0ELD_C_IMMIMM8LD_C_IMM #1
16LD_D_IMMIMM8LD_D_IMM #1
1ELD_E_IMMIMM8LD_E_IMM #1
26LD_H_IMMIMM8LD_H_IMM #1
2ELD_L_IMMIMM8LD_L_IMM #1
7FLD_A_AIMPLD_A_A
78LD_A_BIMPLD_A_B
79LD_A_CIMPLD_A_C
7ALD_A_DIMPLD_A_D
7BLD_A_EIMPLD_A_E
7CLD_A_HIMPLD_A_H
7DLD_A_LIMPLD_A_L
47LD_B_AIMPLD_B_A
40LD_B_BIMPLD_B_B
41LD_B_CIMPLD_B_C
42LD_B_DIMPLD_B_D
43LD_B_EIMPLD_B_E
44LD_B_HIMPLD_B_H
45LD_B_LIMPLD_B_L
4FLD_C_AIMPLD_C_A
48LD_C_BIMPLD_C_B
49LD_C_CIMPLD_C_C
4ALD_C_DIMPLD_C_D
4BLD_C_EIMPLD_C_E
4CLD_C_HIMPLD_C_H
4DLD_C_LIMPLD_C_L
57LD_D_AIMPLD_D_A
50LD_D_BIMPLD_D_B
51LD_D_CIMPLD_D_C
52LD_D_DIMPLD_D_D
53LD_D_EIMPLD_D_E
54LD_D_HIMPLD_D_H
55LD_D_LIMPLD_D_L
5FLD_E_AIMPLD_E_A
58LD_E_BIMPLD_E_B
59LD_E_CIMPLD_E_C
5ALD_E_DIMPLD_E_D
5BLD_E_EIMPLD_E_E
5CLD_E_HIMPLD_E_H
5DLD_E_LIMPLD_E_L
67LD_H_AIMPLD_H_A
60LD_H_BIMPLD_H_B
61LD_H_CIMPLD_H_C
62LD_H_DIMPLD_H_D
63LD_H_EIMPLD_H_E
64LD_H_HIMPLD_H_H
65LD_H_LIMPLD_H_L
6FLD_L_AIMPLD_L_A
68LD_L_BIMPLD_L_B
69LD_L_CIMPLD_L_C
6ALD_L_DIMPLD_L_D
6BLD_L_EIMPLD_L_E
6CLD_L_HIMPLD_L_H
6DLD_L_LIMPLD_L_L
EALD_MEM_AABSLD_MEM_A 0xC000
FALD_A_MEMABSLD_A_MEM 0xC000
E0LDH_MEM_ALDHLDH_MEM_A 0x40
F0LDH_A_MEMLDHLDH_A_MEM 0x40
E2LD_C_MEM_AIMPLD_C_MEM_A
F2LD_A_MEM_CIMPLD_A_MEM_C
77LD_HL_AIMPLD_HL_A
7ELD_A_HLIMPLD_A_HL
46LD_B_HLIMPLD_B_HL
4ELD_C_HLIMPLD_C_HL
56LD_D_HLIMPLD_D_HL
5ELD_E_HLIMPLD_E_HL
66LD_H_HLIMPLD_H_HL
6ELD_L_HLIMPLD_L_HL
70LD_HL_BIMPLD_HL_B
71LD_HL_CIMPLD_HL_C
72LD_HL_DIMPLD_HL_D
73LD_HL_EIMPLD_HL_E
74LD_HL_HIMPLD_HL_H
75LD_HL_LIMPLD_HL_L
36LD_HL_REF_IMMIMM8LD_HL_REF_IMM #1
12LD_DE_AIMPLD_DE_A
1ALD_A_DEIMPLD_A_DE
02LD_BC_AIMPLD_BC_A
0ALD_A_BCIMPLD_A_BC
22LDI_HL_AIMPLDI_HL_A
2ALDI_A_HLIMPLDI_A_HL
32LDD_HL_AIMPLDD_HL_A
3ALDD_A_HLIMPLDD_A_HL
01LD_BC_IMMIMM16LD_BC_IMM #0xC000
11LD_DE_IMMIMM16LD_DE_IMM #0xC000
21LD_HL_IMMIMM16LD_HL_IMM #0xC000
31LD_SP_IMMIMM16LD_SP_IMM #0xC000
F9LD_SP_HLIMPLD_SP_HL
F8LD_HL_SP_IMMRELLD_HL_SP_IMM +target
E8ADD_SP_IMMRELADD_SP_IMM +target
08LD_MEM_SPIMM16LD_MEM_SP #0xC000
C5PUSH_BCIMPPUSH_BC
C1POP_BCIMPPOP_BC
D5PUSH_DEIMPPUSH_DE
D1POP_DEIMPPOP_DE
E5PUSH_HLIMPPUSH_HL
E1POP_HLIMPPOP_HL
F5PUSH_AFIMPPUSH_AF
F1POP_AFIMPPOP_AF
3CINC_AIMPINC_A
04INC_BIMPINC_B
0CINC_CIMPINC_C
14INC_DIMPINC_D
1CINC_EIMPINC_E
24INC_HIMPINC_H
2CINC_LIMPINC_L
34INC_HL_REFIMPINC_HL_REF
3DDEC_AIMPDEC_A
05DEC_BIMPDEC_B
0DDEC_CIMPDEC_C
15DEC_DIMPDEC_D
1DDEC_EIMPDEC_E
25DEC_HIMPDEC_H
2DDEC_LIMPDEC_L
35DEC_HL_REFIMPDEC_HL_REF
87ADD_AIMPADD_A
80ADD_BIMPADD_B
81ADD_CIMPADD_C
82ADD_DIMPADD_D
83ADD_EIMPADD_E
84ADD_HIMPADD_H
85ADD_LIMPADD_L
86ADD_HL_REFIMPADD_HL_REF
C6ADD_A_IMMIMM8ADD_A_IMM #1
8FADC_AIMPADC_A
88ADC_BIMPADC_B
89ADC_CIMPADC_C
8AADC_DIMPADC_D
8BADC_EIMPADC_E
8CADC_HIMPADC_H
8DADC_LIMPADC_L
8EADC_HL_REFIMPADC_HL_REF
CEADC_IMMIMM8ADC_IMM #1
97SUB_AIMPSUB_A
90SUB_BIMPSUB_B
91SUB_CIMPSUB_C
92SUB_DIMPSUB_D
93SUB_EIMPSUB_E
94SUB_HIMPSUB_H
95SUB_LIMPSUB_L
96SUB_HL_REFIMPSUB_HL_REF
D6SUB_IMMIMM8SUB_IMM #1
9FSBC_AIMPSBC_A
98SBC_BIMPSBC_B
99SBC_CIMPSBC_C
9ASBC_DIMPSBC_D
9BSBC_EIMPSBC_E
9CSBC_HIMPSBC_H
9DSBC_LIMPSBC_L
9ESBC_HL_REFIMPSBC_HL_REF
DESBC_IMMIMM8SBC_IMM #1
A7AND_AIMPAND_A
A0AND_BIMPAND_B
A1AND_CIMPAND_C
A2AND_DIMPAND_D
A3AND_EIMPAND_E
A4AND_HIMPAND_H
A5AND_LIMPAND_L
A6AND_HL_REFIMPAND_HL_REF
E6AND_IMMIMM8AND_IMM #1
B7OR_AIMPOR_A
B0OR_BIMPOR_B
B1OR_CIMPOR_C
B2OR_DIMPOR_D
B3OR_EIMPOR_E
B4OR_HIMPOR_H
B5OR_LIMPOR_L
B6OR_HL_REFIMPOR_HL_REF
F6OR_IMMIMM8OR_IMM #1
AFXOR_AIMPXOR_A
A8XOR_BIMPXOR_B
A9XOR_CIMPXOR_C
AAXOR_DIMPXOR_D
ABXOR_EIMPXOR_E
ACXOR_HIMPXOR_H
ADXOR_LIMPXOR_L
AEXOR_HL_REFIMPXOR_HL_REF
EEXOR_IMMIMM8XOR_IMM #1
BFCP_AIMPCP_A
B8CP_BIMPCP_B
B9CP_CIMPCP_C
BACP_DIMPCP_D
BBCP_EIMPCP_E
BCCP_HIMPCP_H
BDCP_LIMPCP_L
BECP_HL_REFIMPCP_HL_REF
FECP_IMMIMM8CP_IMM #1
03INC_BCIMPINC_BC
0BDEC_BCIMPDEC_BC
13INC_DEIMPINC_DE
1BDEC_DEIMPDEC_DE
23INC_HLIMPINC_HL
2BDEC_HLIMPDEC_HL
33INC_SPIMPINC_SP
3BDEC_SPIMPDEC_SP
09ADD_HL_BCIMPADD_HL_BC
19ADD_HL_DEIMPADD_HL_DE
29ADD_HL_HLIMPADD_HL_HL
39ADD_HL_SPIMPADD_HL_SP
07RLCAIMPRLCA
17RLAIMPRLA
0FRRCAIMPRRCA
1FRRAIMPRRA
CBPREFIX_CBIMPPREFIX_CB
2FCPLIMPCPL
3FCCFIMPCCF
37SCFIMPSCF
27DAAIMPDAA