KITAQ 개발 시스템
사용 설명서 2026.09

KITAQ SERIES • 참고 판본

KITAQFC 라이브러리

NES 라이브러리와 주변기기

첫 코드 한 줄부터 실행, 관찰, 재시험까지.
소스 스냅샷: 2026년 9월 14일 · 한국어판: 2026년 9월 14일

네이티브 컴파일러로 라이브러리 사용

lib/는 콘솔 ROM에 컴파일되는 C 헤더와 소스입니다. PC용 컴파일러와 보조 도구는 Rust로 구현되었으며 게임 소스, 대상 콘솔 라이브러리 및 API는 C로 사용합니다. 제공된 SOURCE_MANIFEST.json과 LIBRARY_MERGE.json은 선택한 라이브러리 소스를 기록합니다.

kitaqfc · Rust 네이티브 컴파일러와 보조 도구 · GitHub Actions

Windows, Linux, macOS ARM 및 macOS Intel에서 네이티브 빌드와 실행 검증이 성공했습니다. KITAQGB는 환경별 테스트 48개와 보조 도구 검사 395개, KITAQFC는 55개와 401개를 통과했습니다. Rust 1.85도 검증했습니다. PUBLIC_DISTRIBUTION.json은 배치한 바이너리의 해시와 검증 출처를 기록합니다. 공개 GitHub Actions는 이 소스를 독립적으로 빌드하고 검증합니다.

저장된 참조 출력으로 ROM 바이트, 진단 및 보조 도구 형식을 검사합니다. 이전 C# 에뮬레이터 검증은 원래 소스 지문에 해당하는 과거 기록입니다. 모든 Rust API, 실제 하드웨어 또는 FDS BIOS를 통한 완전한 게임 시작을 입증하지는 않습니다. 원본 PNG 변환 스크립트는 없어 사양으로 재구현했으며 원본과의 바이트 일치는 확인할 수 없습니다.

KITAQGB · SHA-256 · KITAQFC · SHA-256

현재 컴파일러와 라이브러리 배포 구성 — 2026년 10월 4일

fc.h에는 physics3d와 audio_vblank의 선언이 포함됩니다. 필요한 구현 파일은 별도로 링크하세요. entity_update_all과 entity_draw_all은 활성 슬롯을 ID 오름차순으로 콜백에 전달합니다. 콜백을 공통 뱅크 0에 배치하거나 호출하는 동안 해당 PRG 뱅크를 유지하세요. ZX0 사용 시 zx0.h를 명시적으로 포함하세요. audio_vblank 레코드 순서는 delay, CH1, CH2, CH3, CH4입니다.

현재 C# 빌드와 이전 배포판을 29개 입력으로 비교했습니다. 허용된 입력 15개의 ROM 바이트가 일치했으며 나머지 입력 14개에 대한 거부 진단도 일치했습니다. 추가 ROM 두 개를 KUROSAKI에서 각각 4프레임 실행하여 엔티티 할당, 콜백, 슬롯 재사용과 부호 있는 연산, KQBody3D 배치, 오디오 큐 상수를 확인했습니다. 보고서에는 예상 RAM 18바이트와 실제 값이 기록되어 있습니다. 오디오 재생이나 모든 API를 검증한 것은 아니며 이전 회귀 시험 결과는 BINARY_BUILD.json에서 구분합니다.

소스 예제와 실행 검증 기록

1. 필요한 구성 요소 선택

fc.h는 선언을 모으고, core.h는 자료형을, intrinsics.h는 컴파일러 내장 연산을 선언합니다. 일반 C 라이브러리 함수는 대응하는 .c 구현이 필요합니다. 헤더만으로 정의되는 매크로나 내장 함수는 같은 이름의 소스 파일이 필요하지 않을 수도 있습니다.

powershell
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio.c .\kitaq-docs\samples\fc_sound.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\font.chr --mapper=nrom -o .\out\sound.nes

-I가 이름이 비슷한 GB 라이브러리를 가리키지 않도록 하세요. 예를 들어 NES의 __oam_dma()는 인수가 없지만, GB에서는 포인터를 전달합니다.

2. 런타임과 시스템

runtime.c는 PPU 레지스터, OAM 섀도, VRAM 큐를 다루는 C 도우미를 제공합니다. __vramq_* 같은 내장 경로도 있습니다. 이름이 비슷한 별개 큐를 섞지 말고 NMI 처리기가 실제로 어느 데이터를 소비하는지 확인하세요.

system_init는 프레임 상태를 초기화하고 NMI를 활성화합니다. system_wait_vblank는 NMI를 기다린 뒤 소프트웨어 프레임 수를 증가시키고, 등록된 콜백을 동기적으로 한 번 호출합니다. 콜백을 해제하려면 system_set_vblank_callback에 0을 전달하세요. 콜백은 NMI 핸들러 내부가 아니라 대기 함수를 호출한 처리 흐름에서 실행됩니다.

3. PPU, 타일, 속성, 팔레트

ppu_direct.h는 PPU 직접 조작, vram_queue.h는 NMI 중 갱신을 제공합니다. tilemap / nametable_asset은 테이블과 리소스, attribute는 속성, palette는 팔레트를 다룹니다. 초기 로딩과 프레임별 작업을 구분하세요.

배경과 스프라이트 팔레트는 각각 16바이트 그룹입니다. 값은 RGB 성분이 아니라 NES 색 코드입니다. 속성은 타일 묶음의 색을 선택하므로 한 타일의 팔레트를 바꾸려다 이웃 타일까지 바뀔 수 있습니다.

ppu.h와 ppu.c는 화면 제어, 32바이트 팔레트 전송, 네임테이블 전체 초기화를 제공합니다. nes_ppu_seek_bytes(hi,lo)는 주소 래치를 초기화하고 두 바이트로 주소를 설정합니다. runtime.c에서 주소를 한 워드로 받는 nes_ppu_seek(address)와 함께 링크할 수 있습니다. 전송과 초기화 전에는 화면 렌더링을 비활성화하세요.

4. OAM, 메타스프라이트, 공정한 표시

NES는 최대 64개의 스프라이트를 지원하며, 일반적으로 한 주사선에는 8개까지 표시합니다. 같은 줄에 적이나 탄환이 9개 이상 있으면 동시에 모두 보일 수 없습니다. 메타스프라이트는 여러 OBJ로 하나의 이미지를 만듭니다. 할당 범위와 종료 표식 형식을 확인하세요.

oam_fair.h, oam_fair_impl.h는 우선순위를 유지하면서 후보 순서를 순환시킵니다. 플레이어나 HUD를 우선 표시하고 덜 중요한 오브젝트를 번갈아 보이게 할 수 있습니다. OAM 순서를 바꿔도 하드웨어의 주사선 제한 자체는 바뀌지 않습니다.

5. 입력, 반복 입력, 주변기기

input.c는 NES의 원시 버튼 값을 GB 스타일의 BTN_* 마스크로 바꿉니다. NES 원시 A 값은 0x01, 라이브러리 BTN_A는 0x10입니다. BTN_A를 KUROSAKI의 --pad1에 그대로 넘기지 마세요.

pad는 기본 입력을 읽고 input_repeat는 길게 누를 때의 반복을 처리합니다. zapper, keyboard, rob, mic, midi는 저수준 장치 인터페이스입니다. 연결되지 않은 기기에서 0이 읽혔다고 정상 동작이 검증된 것은 아닙니다. 각 기기의 연결 조건을 확인하세요.

keyboard.h — 키보드 매트릭스 입력

연결 상태의 신호 패턴 검사, 전체 18바이트 스캔과 한 행/그룹 읽기를 제공합니다. 문자 변환이나 키 반복 기능은 구현하지 않습니다.

__fkb_detect · __fkb_scan · __fkb_read_row_col

zapper.h — 광선총 입력 신호

각 포트의 원시 비트, 방아쇠와 광센서 신호를 읽습니다. 접미사 없는 함수는 포트 2를 선택합니다. 조준 좌표와 명중 판정은 게임 코드에서 처리합니다.

__zapper_raw1 · __zapper_raw2 · __zapper_trigger1 · __zapper_trigger2 · __zapper_light1 · __zapper_light2 · __zapper_trigger · __zapper_light

intrinsics.h — 1비트 직렬 입출력

외부 어댑터용 OUT0 출력과 D4 입력을 제공합니다. 각 연산은 한 비트를 처리하며, 타이밍과 바이트 구분은 호출자가 제어합니다.

__serial_tx_bit · __serial_rx_bit

이 화면은 다운로드 가능한 ROM을 수정 없이 KUROSAKI에서 실행한 것입니다. 현재 입력 버스는 게임패드만 모델링하며 키보드·광선총·시리얼 어댑터 신호는 모델링하지 않습니다. 외부 입력 비트가 0으로 읽히므로 이 캡처로 해당 장치를 검증할 수 없습니다. 실제 키 입력, 광 감지, 통신은 미검증입니다.

6. 소리

audio_vblank.h를 사용하면 NMI로 4채널 음악을 구동할 수 있습니다. 레퍼런스에서 RAM 큐 보충, 음 유지와 정지, 반복 재생을 설명하며, 완전한 예제에서 캡처한 오디오도 제공합니다.

nes_apu_init 이후 nes_sfx_square1, nes_sfx_square2, nes_sfx_triangle, nes_sfx_noise로 기본 음원을 사용할 수 있습니다. period 인수는 헤르츠 단위 주파수가 아니라 하드웨어 타이머 주기입니다. fc_sound.c는 짧은 펄스음 예제입니다.

DMC 샘플에는 주소, 길이, 정렬, 재생 속도 제약이 있습니다. 포인터를 전달하기 전에 맵 배치를 확인하세요. DMC DMA는 컨트롤러 읽기에 간섭할 가능성도 있으므로 안전한 읽기 방식과 KUROSAKI 진단을 함께 사용하세요.

VRC6는 추가 펄스·톱니파 채널, VRC7은 FM 레지스터, FDS는 웨이브테이블 음원을 제공합니다. 매퍼를 맞추고 결과를 녹음하세요. 이 API는 GB의 Audio_* 드라이버와 별개입니다.

7. 장면, 액터, 엔티티

actor와 entity는 고정 배열에 게임 오브젝트를 저장하고, scene은 장면 상태를 관리합니다. 용량이 가득 찼는지 검사하고, 삭제한 ID는 더 이상 사용하지 마세요. 장면 전환·갱신·그리기는 등록된 콜백을 동기적으로 호출합니다. 각 API의 호출 순서와 재진입 제한을 확인하세요.

chain은 ChainBody를 통한 관절형 몸체 추종과 Chain을 통한 위치 이력을 제공합니다. collision은 사각형 등 도형의 접촉을 검사합니다. 이동 → 충돌 → 그리기 순서를 일정하게 유지하면 충돌 판정이 한 프레임 뒤처지는 문제를 피할 수 있습니다.

8. 수학과 물리

fixed.h는 Q8.8 연산, math_fast와 math_fixed는 수치 연산, math_lut는 표 기반 계산을 제공합니다. physics2d는 상자 운동 적분, 중력, AABB 접촉, 표면 반응을 처리합니다. physics3d는 회전하지 않는 3D 상자, 반발, 충격 값을 처리합니다. 월드와 몸체 배열을 확보하여 초기화하고 매개변수를 설정한 뒤 step 함수를 호출하세요. 위치와 속도는 호출자가 선택한 일관된 정수 단위를 사용합니다.

Q5.3의 소수 부분은 1/8픽셀 단위입니다. 정수 좌표, 소수 부분, 속도, 방향을 따로 유지하고, 덧셈과 자리올림을 게임 코드에서 처리하세요. fc_subpixel.c는 2/8픽셀을 8번 더해 40픽셀에서 42픽셀로 이동합니다. Q8.8의 1.0인 256을 Q5.3에 그대로 쓰면 안 됩니다.

8.1 뷰포트 크기를 선택할 수 있는 와이어프레임

wire3d.c는 직접 만든 3D 모델을 어두운 배경 위에 청록색 선으로 그립니다. 정수 회전 연산, 역수 표를 이용한 원근 투영, CPU 측 픽셀 버퍼, CHR RAM 패턴 테이블 두 개를 사용합니다. 상용 게임의 코드·모델·그래픽 데이터는 포함하지 않습니다. 소스는 DAISUKE OBA가 MIT 라이선스로 제공합니다.

wire3d.c를 포함하기 전에 뷰포트 크기를 64×48, 96×64, 128×96 중에서 선택하세요. WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 둘 다 정의합니다. 기본값은 64×48입니다. 뷰포트는 256×240 배경의 중앙에 배치됩니다. 크기가 커지면 사용하는 타일 수와 그리는 선의 길이가 늘어나므로, 실제 장면의 처리 시간을 측정하여 선택하세요.

c
#define WIRE3D_FC_WIDTH 96
#define WIRE3D_FC_HEIGHT 64
#include "wire3d.c"

void main(void) {
    Wire3DFC_Init();
    Wire3DFC_BeginFrame();
    Wire3DFC_DrawLine2D(4,4,91,59);
    Wire3DFC_DrawLine2D(91,4,4,59);
    Wire3DFC_EndFrame();
    while(1) {}
}

이 예제는 두 대각선을 교차하도록 그려서 선택한 뷰포트와 선이 차지하는 영역을 확인합니다. 소스 안에서 이미 구현 파일을 포함하므로 빌드할 때 구현을 중복 지정하지 마세요.

powershell
.\kitaqfc\kitaqfc.exe .\cross.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\cross.nes

ROM에는 쓰기 가능한 CHR RAM 8 KiB와 $6800–$70BF 영역을 포함하는 카트리지 PRG RAM이 필요합니다. 렌더러는 패턴 테이블 두 개, 네임테이블 0, 스크롤, 배경 팔레트를 전용으로 사용하며 제로 페이지 $00–$0A도 예약합니다. 다른 용도로 이 영역을 겹쳐 사용하거나 그리는 동안 매퍼 매핑을 바꾸지 마세요. --nes-chr-ram은 CHR ROM 대신 RAM을 선택합니다. --nes-chr 또는 --chr-rom으로 그래픽 파일을 지정하는 옵션과 함께 사용할 수 없으며, CNROM 구성에서는 허용되지 않습니다.

렌더러는 재진입을 지원하지 않습니다. 초기화 시 NMI를 비활성화하고 설정을 마친 뒤 배경을 켭니다. 그리기와 업로드는 호출한 처리 흐름에서 실행됩니다. 동시에 PPU에 쓰는 다른 처리나 렌더러의 작업용 메모리를 변경하는 인터럽트 핸들러를 실행하지 마세요. 한 번의 업로드가 여러 화면 프레임에 걸칠 수 있습니다. 비동기 게임 루프나 오디오 갱신 스케줄러는 제공하지 않습니다.

함수용도와 호출 조건
Wire3DFC_Init()CHR 테이블 두 개와 CPU 측 그리기 버퍼를 지우고 중앙에 배치할 타일 맵을 구성합니다. 어두운 배경에 청록색을 지정한 뒤 NMI가 비활성화된 상태로 배경을 켭니다. 다른 프레임 작업보다 먼저 호출하세요.
Wire3DFC_BeginFrame()직전 그리기에 사용한 CPU 측 타일을 지웁니다. 표시 중인 이미지는 EndFrame이 버퍼를 전환할 때까지 유지됩니다. 완성할 이미지를 구성하기 전에 한 번 호출하세요.
Wire3DFC_DrawLine2D(ax,ay,bx,by)양 끝점을 포함하는 선분을 CPU 버퍼에 그립니다. 끝점은 −512…511 범위의 부호 있는 좌표입니다. 뷰포트 밖의 픽셀은 잘라내며, 경계 상자 전체가 밖에 있는 선분은 제외합니다. 영역 안의 선은 6502 레지스터를 활용하는 Bresenham 루프로 그립니다.
Wire3DFC_RotatePoint(x,y,z,rx,ry,rz)서로 다른 쓰기 가능한 s16 성분 세 개를 Y, X, Z축 순서로 회전하여 갱신합니다. 각도는 한 바퀴를 32단계로 나타내며 순환합니다. 시작 시 각 성분을 −63…63 범위에 두세요. Q6 축소 시 소수 부분은 0 방향으로 버립니다. 성분 포인터가 하나라도 NULL이면 아무것도 변경하지 않습니다.
Wire3DFC_ProjectPoint(x,y,z,sx,sy)카메라 좌표의 점을 화면 좌표로 변환합니다. X/Y는 −127…127, 깊이는 32…255여야 합니다. 성공하면 1을 반환하고, 화면 영역으로 잘라내기 전의 부호 있는 좌표를 출력합니다. 좌표가 유효하지 않거나 출력 포인터가 NULL이면 출력을 변경하지 않고 0을 반환합니다. X의 양의 방향은 오른쪽, Y의 양의 방향은 위쪽입니다.
Wire3DFC_DrawLine3D(ax,ay,az,bx,by,bz)양 끝점을 투영한 뒤 화면에 선분을 그립니다. 한 끝점이라도 투영 범위를 벗어나면 변 전체를 생략합니다. 변과 근평면·원평면의 교점을 구해 자르는 처리는 하지 않습니다.
Wire3DFC_DrawModel(vertices,count,edges,edge_count,x,y,z,rx,ry,rz)처음 24개까지의 정점을 각각 한 번씩 변환·투영하여 결과를 저장한 뒤, 인덱스로 지정한 변을 그립니다. 정점은 s8 성분 세 개, 변은 u8 정점 인덱스 두 개로 구성됩니다. NULL 배열, 유효하지 않은 인덱스, 투영에서 제외된 끝점은 건너뜁니다. 평행이동 후의 합을 s16 범위에 유지하세요. 유효한 변은 모두 그리며, 이 API는 숨은 면을 제거하지 않습니다.
Wire3DFC_EndFrame()준비한 이미지 또는 비표시 CHR 버퍼의 이전 내용에 사용된 타일을 업로드합니다. 지워야 하는 타일도 포함됩니다. VBlank 한 번에 최대 16타일을 전송하고 스크롤을 복원한 뒤, 새로운 VBlank에서 완성된 이미지를 표시합니다. 완료될 때까지 반환하지 않습니다.

EndFrame 실행 후 wire3d_uploaded_tiles는 업로드한 타일 수를 나타내고, wire3d_transfer_frames는 업로드 배치 수에 마지막 버퍼 전환 대기를 더한 값을 나타냅니다. 후자는 전체 렌더링 소요 시간이 아닙니다. 좌표 변환, 래스터화, CPU 측 상태 관리에도 시간이 걸립니다. 원근 투영에는 양자화된 역수 표를 사용하며, 폭 128픽셀에서 가장 가까운 깊이의 배율 값은 256에서 255로 제한됩니다.

HTML 설명서의 정육면체 예제는 정점 공유, 회전, 투영, 인덱스로 지정한 변 12개, 완성된 이미지의 표시 방법을 보여 줍니다. 검증 이미지는 Y축을 중심으로 회전한 정육면체입니다. 12개의 변이 모두 연결되어 있어야 하며, 이전 이미지의 선이 남아 있으면 안 됩니다.

회전하는 큐브 예제

powershell
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_cube.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\wire_cube.nes
64×48: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.
64×48: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.
96×64: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.
96×64: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.
128×96: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.
128×96: Y축을 중심으로 3/32바퀴 회전한 정육면체입니다. 12개의 변이 연결되어 있고 배경에 이전 선이 남지 않아야 합니다.

KUROSAKI에서 각 크기별로 선, 정육면체, 지우기를 확인하여 총 9개의 이미지 검증을 수행했습니다. 추가로 뷰포트 크기 3종과 컴파일러 설정 4종에서 투영 경계값, NULL 포인터, 각 축의 전체 32단계 회전, 복합 회전, 유효하지 않은 변의 제외를 확인했습니다. 그리기 예제는 NROM을 사용하고, 경계 조건을 모은 더 큰 테스트 ROM은 MMC3를 사용합니다. 이는 에뮬레이터 검증이며 실기기 측정 결과가 아닙니다.

zx0

ZX0 호환 자산을 RAM 또는 VRAM에 디코딩합니다. PC에서 kitaqfc-zx0.exe로 준비한 뒤 실행 중 필요한 데이터만 디코딩하세요. --format=auto는 raw, RLE, ZX0 페이로드 크기를 비교해 가장 작은 것을 선택하며, 같은 크기면 raw, RLE, ZX0 순으로 우선합니다. 9바이트 헤더를 포함한 전체 컨테이너를 65535바이트 이내로 유지하고 실제 CPU 뱅크와 RAM에 맞게 자산을 나누세요.

ZX0 형식과 원래 압축 알고리즘은 Einar Saukas가 설계했습니다. 여기의 압축기와 압축 해제기는 KITAQ의 독립 구현이며, 저작권자는 DAISUKE OBA이고 MIT 라이선스로 배포됩니다.

저장소 루트에서 다음 호스트 명령을 실행하세요. 도구는 tools/zx0/build.ps1로 빌드합니다.

.\kitaqfc-zx0.exe tiles.bin tiles.zx0
.\kitaqfc-zx0.exe tiles.bin tiles.h --header=tiles
.\kitaqfc-zx0.exe map.bin map.kqa --format=auto
.\kitaqfc-zx0.exe tiles.zx0 restored.bin --decompress
zx0_decompress

제한된 RAM 영역에 ZX0 v2 스트림을 디코딩합니다. 일치 복사는 이미 디코딩한 바이트를 참조하므로 대상은 읽기·쓰기가 모두 가능해야 합니다. CPU가 사용 전에 처리할 타일 맵이나 레벨 테이블 등에 쓰세요.

사용 형식

c
u16 zx0_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);

매개변수

dst

쓰기 가능한 대상 RAM의 시작점이며 null이면 안 됩니다. 용량은 배열 요소 수가 아닌 바이트로 지정하세요.

capacity

대상에 쓸 수 있는 바이트 수입니다. 일반 ZX0 스트림에는 최소 1바이트가 필요합니다. 빈 raw KQA1 자산은 용량 0으로도 성공할 수 있습니다.

src

현재 뱅크 매핑으로 읽을 수 있는 압축 데이터의 시작점입니다. null이면 안 됩니다.

packed_size

전체 입력의 바이트 수입니다. ZX0 스트림은 종료 마커를 포함하며 뒤에 추가 바이트가 없어야 합니다. KQA1 컨테이너는 9바이트 헤더를 포함합니다. 최대 65535바이트입니다.

반환값

성공하면 디코딩한 바이트 수, 오류면 0을 반환합니다. 항상 zx0_error도 확인하세요.

사용 조건과 제한

zx0.h를 포함하고 lib/zx0.c를 컴파일하세요. 공유 임시 영역을 사용하므로 인터럽트에서 재진입할 수 없습니다. 입력과 출력은 서로 겹치거나 CPU 주소 공간을 래핑하거나 현재 매핑된 뱅크 창을 넘어서는 안 됩니다. 라이브러리는 자산 뱅크를 전환하지 않습니다.

매 호출은 zx0_error를 갱신합니다. 0=성공, 1=유효하지 않은 인수, 2=입력 잘림, 3=잘못된 스트림, 4=출력 공간 부족, 5=화면 활성입니다. 실패하면 부분 출력이 남을 수 있으므로 사용하지 마세요. 여러 조건이 잘못되었으면 먼저 감지한 조건으로 코드를 정합니다.

순방향 ZX0 v2 스트림을 받습니다. ZX0 v1, 역방향 스트림과 외부 사전은 이 인터페이스에 포함되지 않습니다. 호스트에서 만든 자산의 디코딩 크기를 알고 충분한 공간을 제공하세요.

ZX0 형식 — Einar Saukas

사용 예

c
count=zx0_decompress(decoded_map,16,packed_map,packed_map_SIZE);
    if(count!=16||zx0_error!=ZX0_OK)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

zx0_decompress: NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.
NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm
구현과 소스

kitaqfc/lib/zx0.h:16

선언 주석 원문
Return decoded bytes, or zero on error (possibly after partial writes).
Source and output must not overlap or cross their current CPU bank windows.
Capacity and packed_size are byte counts. No prefix/backward/v1 streams.

kitaqfc/lib/zx0.c:52

c
u16 zx0_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
    u8 phase; u8 low; u8* out; u8* match;
    u16 count; u16 distance; u16 used; u16 upper;
    zx0_error=ZX0_OK;
    if(!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
    kq_zx_input=(const u8*)src; kq_zx_left=packed_size;
    kq_zx_mask=0; kq_zx_borrowed=0;
    out=(u8*)dst; used=0; distance=1; phase=0;
    // Phase 0 is literal, 1 reuses the last distance, 2 carries a new distance.
    while(1) {
        if(phase==2) {
            upper=kq_zx_number(1);
            if(zx0_error) return 0;
            if(upper==256) {
                if(kq_zx_left!=0) { zx0_error=ZX0_BAD_STREAM; return 0; }
                return used;
            }
            if(upper>255) { zx0_error=ZX0_BAD_STREAM; return 0; }
            low=kq_zx_byte();
            distance=(u16)((upper<<7)-(low>>1));
            kq_zx_borrowed=1; kq_zx_borrow_value=(u8)(low&1);
            count=kq_zx_number(0);
            if(count==65535) { zx0_error=ZX0_BAD_STREAM; return 0; }
            count++;
        } else count=kq_zx_number(0);
        if(zx0_error) return 0;
        if(count>capacity-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
        if(phase==0) {
            if(count>kq_zx_left) { zx0_error=ZX0_TRUNCATED; return 0; }
            kq_zx_left-=count; used+=count;
            while(count!=0) { *out++=*kq_zx_input++; count--; }
            phase=kq_zx_bit()?2:1;
        } else {
            if(distance>used) { zx0_error=ZX0_BAD_STREAM; return 0; }
            match=out-distance; used+=count;
            // Forward byte copying deliberately supports overlapping matches.
            while(count!=0) { *out++=*match++; count--; }
            phase=kq_zx_bit()?2:0;
        }
        if(zx0_error) return 0;
    }
}
asset_decompress

--format=auto로 만든 KQA1 헤더에서 코덱과 크기를 읽은 뒤 raw 바이트를 복사하거나 RLE/ZX0를 RAM에 디코딩합니다. 호출자 코드는 코덱마다 별도 분기할 필요가 없습니다.

사용 형식

c
u16 asset_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);

매개변수

dst

쓰기 가능한 대상 RAM의 시작점이며 null이면 안 됩니다. 용량은 배열 요소 수가 아닌 바이트로 지정하세요.

capacity

대상에 쓸 수 있는 바이트 수입니다. 일반 ZX0 스트림에는 최소 1바이트가 필요합니다. 빈 raw KQA1 자산은 용량 0으로도 성공할 수 있습니다.

src

현재 뱅크 매핑으로 읽을 수 있는 압축 데이터의 시작점입니다. null이면 안 됩니다.

packed_size

전체 입력의 바이트 수입니다. ZX0 스트림은 종료 마커를 포함하며 뒤에 추가 바이트가 없어야 합니다. KQA1 컨테이너는 9바이트 헤더를 포함합니다. 최대 65535바이트입니다.

반환값

성공하면 디코딩한 바이트 수, 오류면 0을 반환합니다. 항상 zx0_error도 확인하세요.

사용 조건과 제한

zx0.h를 포함하고 lib/zx0.c를 컴파일하세요. 공유 임시 영역을 사용하므로 인터럽트에서 재진입할 수 없습니다. 입력과 출력은 서로 겹치거나 CPU 주소 공간을 래핑하거나 현재 매핑된 뱅크 창을 넘어서는 안 됩니다. 라이브러리는 자산 뱅크를 전환하지 않습니다.

매 호출은 zx0_error를 갱신합니다. 0=성공, 1=유효하지 않은 인수, 2=입력 잘림, 3=잘못된 스트림, 4=출력 공간 부족, 5=화면 활성입니다. 실패하면 부분 출력이 남을 수 있으므로 사용하지 마세요. 여러 조건이 잘못되었으면 먼저 감지한 조건으로 코드를 정합니다.

KQA1은 KITAQ 자산 컨테이너입니다. 4바이트 매직 값, 1바이트 코덱(0 raw, 1 RLE, 2 ZX0), 디코딩 크기 u16, 페이로드 크기 u16, 페이로드 순서입니다. u16 필드는 리틀 엔디언입니다. RLE는 count/value 쌍을 반복하며 count는 1~255, 종료는 count 0입니다. 빈 raw 자산은 반환값 0, 오류 0으로 성공합니다.

ZX0 형식 — Einar Saukas

사용 예

c
count=asset_decompress(decoded_auto,16,packed_auto,packed_auto_SIZE);
    if(count!=16||zx0_error!=ZX0_OK)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

asset_decompress: NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.
NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm
구현과 소스

kitaqfc/lib/zx0.h:19

선언 주석 원문
Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto.
Empty raw assets return zero with zx0_error==ZX0_OK.

kitaqfc/lib/zx0.c:95

c
u16 asset_decompress(void* dst,u16 capacity,const void* src,u16 packed_size) {
    const u8* in; u8* out; u16 raw; u16 packed; u16 used;
    u8 codec; u8 count; u8 value;
    zx0_error=ZX0_OK;
    if(packed_size<9||!kq_zx_arguments(dst,capacity,src,packed_size)) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
    in=(const u8*)src; out=(u8*)dst;
    if(in[0]!=75||in[1]!=81||in[2]!=65||in[3]!=49) { zx0_error=ZX0_BAD_STREAM; return 0; }
    codec=in[4]; raw=(u16)((u16)in[5]|((u16)in[6]<<8));
    packed=(u16)((u16)in[7]|((u16)in[8]<<8));
    if(packed!=packed_size-9||codec>2) { zx0_error=ZX0_BAD_STREAM; return 0; }
    if(raw>capacity) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
    in+=9;
    if(codec==2) {
        used=zx0_decompress(dst,raw,in,packed);
        if(zx0_error==0&&used!=raw) zx0_error=ZX0_BAD_STREAM;
        return zx0_error?0:used;
    }
    if(codec==0) {
        if(raw!=packed) { zx0_error=ZX0_BAD_STREAM; return 0; }
        used=raw; while(raw!=0) { *out++=*in++; raw--; } return used;
    }
    used=0;
    while(packed!=0) {
        count=*in++; packed--;
        if(count==0) {
            if(packed!=0||used!=raw) { zx0_error=ZX0_BAD_STREAM; return 0; }
            return used;
        }
        if(packed==0) { zx0_error=ZX0_TRUNCATED; return 0; }
        value=*in++; packed--;
        if((u16)count>raw-used) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
        used+=count; while(count!=0) { *out++=value; count--; }
    }
    zx0_error=ZX0_TRUNCATED; return 0;
}
zx0_decompress_vram

ZX0 자산 전체를 호출자가 소유한 RAM에 디코딩한 뒤 최대 128바이트씩 나누어 PPU에 업로드합니다. PPU 메모리는 일반 CPU 포인터로 직접 읽을 수 없으므로 일치 복사 이력은 RAM에 둡니다.

사용 형식

c
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size);

매개변수

ppu_addr

PPU 대상 주소로 0x0000..0x2FFF입니다. 디코딩 범위 전체는 0x3000 전에 끝나야 합니다. 패턴 테이블에 쓰려면 CHR RAM이 있는 카트리지 구성이 필요합니다.

workspace

쓰기 가능한 대상 RAM의 시작점이며 null이면 안 됩니다. 용량은 배열 요소 수가 아닌 바이트로 지정하세요.

capacity

RAM 작업 영역 용량입니다. 디코딩한 자산 전체가 들어가야 합니다. PPU 전송을 128바이트로 나눈다고 필요한 용량이 줄지는 않습니다.

src

현재 뱅크 매핑으로 읽을 수 있는 압축 데이터의 시작점입니다. null이면 안 됩니다.

packed_size

전체 입력의 바이트 수입니다. ZX0 스트림은 종료 마커를 포함하며 뒤에 추가 바이트가 없어야 합니다. KQA1 컨테이너는 9바이트 헤더를 포함합니다. 최대 65535바이트입니다.

반환값

성공하면 디코딩한 바이트 수, 오류면 0을 반환합니다. 항상 zx0_error도 확인하세요.

사용 조건과 제한

zx0.h를 포함하고 lib/zx0.c를 컴파일하세요. 공유 임시 영역을 사용하므로 인터럽트에서 재진입할 수 없습니다. 입력과 출력은 서로 겹치거나 CPU 주소 공간을 래핑하거나 현재 매핑된 뱅크 창을 넘어서는 안 됩니다. 라이브러리는 자산 뱅크를 전환하지 않습니다.

매 호출은 zx0_error를 갱신합니다. 0=성공, 1=유효하지 않은 인수, 2=입력 잘림, 3=잘못된 스트림, 4=출력 공간 부족, 5=화면 활성입니다. 실패하면 부분 출력이 남을 수 있으므로 사용하지 마세요. 여러 조건이 잘못되었으면 먼저 감지한 조건으로 코드를 정합니다.

순방향 ZX0 v2 스트림을 받습니다. ZX0 v1, 역방향 스트림과 외부 사전은 이 인터페이스에 포함되지 않습니다. 호스트에서 만든 자산의 디코딩 크기를 알고 충분한 공간을 제공하세요.

호출 전에 __ppu_mask_set(0)으로 렌더링을 끄세요. 루틴은 PPU 주소 증가량을 일시적으로 1로 선택한 뒤 PPUCTRL을 복원합니다. PPUADDR/PPUDATA 연산은 PPU 주소와 래치 상태를 바꾸므로 렌더링을 다시 켜기 전에 스크롤을 설정하세요. 호출 중 인터럽트 핸들러도 PPU에 접근하면 안 됩니다. CHR ROM이나 팔레트 전송에는 사용하지 마세요. 대상 범위 검사는 RAM 디코딩 뒤에 이루어지므로 범위 오류가 나도 작업 영역은 바뀔 수 있습니다.

ZX0 형식 — Einar Saukas

사용 예

c
count=zx0_decompress_vram(0x0800,tile_workspace,48,packed_tiles,packed_tiles_SIZE);
    if(count!=48||zx0_error!=ZX0_OK)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

zx0_decompress_vram: NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.
NTSC — RAM MAP 행은 정사각형·마름모·체커 타일을 반복합니다. AUTO RLE MAP은 정사각형 타일 16개로 수평 막대를 만듭니다. VRAM TILES는 픽셀을 VRAM에 디코딩한 뒤 같은 세 타일을 반복합니다. CGB와 FC에서 정사각형은 흰색, 마름모는 빨간색, 체커는 파란색이며 DMG는 회색 명암을 사용합니다. CHECKS OK는 세 호출 모두 오류 없이 예상 디코딩 크기를 반환했음을 나타냅니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\zx0_decode.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\zx0_decode.nes --mapper=nrom --nes-chr-ram --no-cache --no-disasm
구현과 소스

kitaqfc/lib/zx0.h:23

선언 주석 원문
Decode into caller-owned RAM, then transfer to CHR RAM or nametable memory.
Rendering must be off. Preserve PPUCTRL; set scroll before enabling rendering.
The workspace must hold the whole output. No palette upload or CHR ROM writes.

kitaqfc/lib/zx0.c:134

c
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size) {
    u16 size; u16 left; const u8* data; u8 chunk; u8 saved_ctrl;
    zx0_error=ZX0_OK;
    if(ppu_addr>=0x3000) { zx0_error=ZX0_BAD_ARGUMENT; return 0; }
    if(__ppu_mask_get()&0x18) { zx0_error=ZX0_DISPLAY_ACTIVE; return 0; }
    size=zx0_decompress(workspace,capacity,src,packed_size);
    if(zx0_error) return 0;
    if(size>0x3000-ppu_addr) { zx0_error=ZX0_OUTPUT_FULL; return 0; }
    saved_ctrl=__ppu_ctrl_get(); __ppu_ctrl_set((u8)(saved_ctrl&0xFB));
    left=size; data=(const u8*)workspace;
    while(left!=0) {
        chunk=left>128?128:(u8)left;
        __vram_write(ppu_addr,data,chunk);
        ppu_addr+=chunk; data+=chunk; left-=chunk;
    }
    __ppu_ctrl_set(saved_ctrl); return size;
}

wire3d — 함수별 레퍼런스

Wire3DFC_Init

CHR RAM, 배경 맵, 준비 버퍼의 픽셀, 변경 타일 이력을 초기화하고 청록색 선으로 배경 표시를 활성화합니다. 기존 화면 내용은 지워집니다.

사용 형식

c
void Wire3DFC_Init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_Init();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_Init: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_Init: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_Init: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:34

선언 주석 원문
Own both CHR-RAM pattern tables and nametable zero. Requires writable 8 KiB
CHR RAM and PRG RAM 0x6800..0x70BF; reserves ZP 0x00..0x0A for drawing/transfer.
Build with --nes-chr-ram and provide cartridge PRG RAM for the stage.
Center the viewport, clear video memory, select cyan-on-dark colors and turn
on background rendering with NMI disabled. Do not run another PPU writer.

kitaqfc/lib/wire3d.c:40

c
void Wire3DFC_Init(void) {
    u16 address;u8 x,y,tile;
    __ppu_mask_set(0);__ppu_ctrl_set(0);
    // Both pattern tables have a zero high bitplane; all subsequent transfers
    // update only low bitplanes in the currently hidden table.
    for(address=0;address<8192;address+=128)__vram_fill(address,0,128);
    for(address=0x2000;address<0x2400;address+=128)__vram_fill(address,255,128);
    __vram_fill(0x23C0,0,64);__palette_bg_load(w3dfc_palette);
    tile=0;
    for(y=0;y<WIRE3D_FC_HEIGHT/8;y++) {
        for(x=0;x<WIRE3D_FC_WIDTH/8;x++) {
            address=(u16)(0x2000+(u16)(y+(30-WIRE3D_FC_HEIGHT/8)/2)*32+x+(32-WIRE3D_FC_WIDTH/8)/2);
            __ppu_addr(address);__ppu_data(tile);tile++;
        }
    }
    __memset(w3dfc_pixels,0,1536);
    __memset(w3dfc_dirty,0,192);
    __memset(w3dfc_bank0_dirty,0,192);
    __memset(w3dfc_bank1_dirty,0,192);
    w3dfc_front=0;w3dfc_batch_count=0;
    wire3d_transfer_frames=0;wire3d_uploaded_tiles=0;
    __scroll_set(0,0);__ppu_ctrl_set(0);__ppu_mask_set(0x0A);
}
Wire3DFC_BeginFrame

이전 그리기에서 사용한 타일의 준비 픽셀을 지워 빈 프레임을 준비합니다. 현재 표시 중인 CHR 테이블과 화면 이미지는 바뀌지 않습니다.

사용 형식

c
void Wire3DFC_BeginFrame(void);

반환값

반환값은 없습니다.

사용 조건과 제한

매번 그리기를 시작할 때 한 번 호출하세요. EndFrame 전에 다시 호출하면 그때까지 준비한 선들이 지워집니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_BeginFrame();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_BeginFrame: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_BeginFrame: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_BeginFrame: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:36

선언 주석 원문
Clear the previous staged drawing. The displayed front image remains intact.

kitaqfc/lib/wire3d.c:63

c
void Wire3DFC_BeginFrame(void) {
    w3dfc_tile_count=WIRE3D_FC_TILES;
    // Clear exactly eight bytes per previously touched tile, using the same
    // zero-page pointer as upload. No nested C indexing runs in the inner loop.
    __asm {
        PHA
        TXA
        PHA
        TYA
        PHA
        LDX #0
w3dfc_clear_next:
        LDA w3dfc_dirty,X
        BEQ w3dfc_clear_skip
        TXA
        ASL A
        ASL A
        ASL A
        STA w3dfc_upload_ptr
        TXA
        LSR A
        LSR A
        LSR A
        LSR A
        LSR A
        CLC
        ADC #104
        STA w3dfc_upload_ptr+1
        LDA #0
        STA w3dfc_dirty,X
        LDY #7
w3dfc_clear_byte:
        STA (w3dfc_upload_ptr),Y
        DEY
        BPL w3dfc_clear_byte
w3dfc_clear_skip:
        INX
        CPX w3dfc_tile_count
        BNE w3dfc_clear_next
        PLA
        TAY
        PLA
        TAX
        PLA
    }
}
Wire3DFC_DrawLine2D

양 끝점을 포함하는 선을 준비 메모리에 래스터화하고 사용한 타일을 전송 대상으로 표시합니다. 카메라 투영은 수행하지 않습니다.

사용 형식

c
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by);

매개변수

ax

시작점의 가로 좌표이며 뷰포트 픽셀 단위로 −512~511입니다. 뷰포트 왼쪽 위는 (0,0)입니다.

ay

시작점의 세로 좌표이며 뷰포트 픽셀 단위로 −512~511입니다. 뷰포트 왼쪽 위는 (0,0)입니다.

bx

끝점의 가로 좌표이며 뷰포트 픽셀 단위로 −512~511입니다. 뷰포트 왼쪽 위는 (0,0)입니다.

by

끝점의 세로 좌표이며 뷰포트 픽셀 단위로 −512~511입니다. 뷰포트 왼쪽 위는 (0,0)입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

양 끝점의 좌표 중 하나라도 −512~511 범위를 벗어나면 선 전체를 버립니다. 그 외에는 뷰포트 밖의 픽셀만 버립니다. 새 픽셀은 준비된 선과 OR 연산으로 합쳐지며 EndFrame을 통해 화면에 나타납니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_DrawLine2D(0,0,WIRE3D_FC_WIDTH-1,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_DrawLine2D: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawLine2D: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawLine2D: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:39

선언 주석 원문
Draw a connected, inclusive segment. Endpoints must be -512..511. Pixels
outside the viewport are clipped; completely outside bounding boxes return.

kitaqfc/lib/wire3d.c:193

c
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by) {
    s16 dx,dy,sx,sy,error,twice;u16 address;u8 tile;
    if(ax < -512 || ax>511 || ay < -512 || ay>511 || bx < -512 || bx>511 || by < -512 || by>511)return;
    if((ax<0 && bx<0)||(ay<0 && by<0)||(ax>=WIRE3D_FC_WIDTH && bx>=WIRE3D_FC_WIDTH)||(ay>=WIRE3D_FC_HEIGHT && by>=WIRE3D_FC_HEIGHT))return;
    dx=bx-ax;if(dx<0)dx=0-dx;
    dy=by-ay;if(dy<0)dy=0-dy;
    if(ax>=0 && ay>=0 && bx>=0 && by>=0 && ax<WIRE3D_FC_WIDTH && bx<WIRE3D_FC_WIDTH && ay<WIRE3D_FC_HEIGHT && by<WIRE3D_FC_HEIGHT) {
        w3dfc_lx=(u8)ax;w3dfc_ly=(u8)ay;
        w3dfc_sx=ax<bx?1:255;w3dfc_sy=ay<by?1:255;
        if(dx>=dy){w3dfc_major=(u8)dx;w3dfc_minor=(u8)dy;w3dfc_y_major=0;}
        else{w3dfc_major=(u8)dy;w3dfc_minor=(u8)dx;w3dfc_y_major=1;}
        w3dfc_error=(u8)(w3dfc_major>>1);w3dfc_remaining=(u8)(w3dfc_major+1);
        w3dfc_line_inside();return;
    }
    sx=ax<bx?1:-1;sy=ay<by?1:-1;error=dx-dy;
    while(1) {
        if(ax>=0 && ax<WIRE3D_FC_WIDTH && ay>=0 && ay<WIRE3D_FC_HEIGHT) {
            tile=(u8)(((u8)ay>>3)*(WIRE3D_FC_WIDTH/8)+((u8)ax>>3));
            address=(u16)tile*8+((u8)ay&7);
            w3dfc_pixels[address]=(u8)(w3dfc_pixels[address]|w3dfc_bits[(u8)ax&7]);
            w3dfc_dirty[tile]=1;
        }
        if(ax==bx && ay==by)return;
        twice=error*2;
        if(twice > (0-dy)){error-=dy;ax+=sx;}
        if(twice < dx){error+=dx;ay+=sy;}
    }
}
Wire3DFC_RotatePoint

모델 공간의 점 하나를 제자리에서 회전합니다. 평행 이동이나 원근 투영은 수행하지 않습니다.

사용 형식

c
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz);

매개변수

x

쓰기 가능한 s16 x 성분의 포인터입니다. 세 성분의 저장 위치는 서로 달라야 하며 각 입력 성분을 −63~63 범위로 유지하세요.

y

쓰기 가능한 s16 y 성분의 포인터입니다. 세 성분의 저장 위치는 서로 달라야 하며 각 입력 성분을 −63~63 범위로 유지하세요.

z

쓰기 가능한 s16 z 성분의 포인터입니다. 세 성분의 저장 위치는 서로 달라야 하며 각 입력 성분을 −63~63 범위로 유지하세요.

rx

X축 회전각이며 한 바퀴는 32단계입니다.

ry

Y축 회전각이며 한 바퀴는 32단계입니다.

rz

Z축 회전각이며 한 바퀴는 32단계입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

각도는 한 바퀴가 32단계입니다. 0은 회전 없음, 8은 90도, 16은 180도입니다. 하위 5비트만 사용합니다. 회전은 Y, X, Z 순서로 적용합니다.

포인터 중 하나라도 null이면 좌표를 바꾸지 않고 반환합니다. Q6 정수 곱은 0 방향으로 절삭합니다. 입력 성분은 자동으로 범위 제한되지 않습니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_RotatePoint(&px,&py,&pz,0,0,8);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.

Wire3DFC_RotatePoint: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.
Wire3DFC_RotatePoint: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.
Wire3DFC_RotatePoint: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 예제에서 Z축으로 90도 회전하면 (24,0,0)이 (0,24,0)으로 바뀌며, 투영된 십자는 정육면체 위쪽에 나타납니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:42

선언 주석 원문
Rotate a small model-space point in Y/X/Z order. Angles wrap in 32 steps.
Start with components in -63..63; supply three distinct writable pointers.

kitaqfc/lib/wire3d.c:227

c
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz) {
    s16 a,b,s,c;
    if(x==0 || y==0 || z==0)return;
    ry=(u8)(ry&31);rx=(u8)(rx&31);rz=(u8)(rz&31);
    if(ry!=0){a=*x;b=*z;s=w3dfc_sin[ry];c=w3dfc_sin[(u8)((ry+8)&31)];*x=w3dfc_q6(a*c+b*s);*z=w3dfc_q6(b*c-a*s);}
    if(rx!=0){a=*y;b=*z;s=w3dfc_sin[rx];c=w3dfc_sin[(u8)((rx+8)&31)];*y=w3dfc_q6(a*c-b*s);*z=w3dfc_q6(a*s+b*c);}
    if(rz!=0){a=*x;b=*y;s=w3dfc_sin[rz];c=w3dfc_sin[(u8)((rz+8)&31)];*x=w3dfc_q6(a*c-b*s);*y=w3dfc_q6(a*s+b*c);}
}
Wire3DFC_ProjectPoint

카메라 공간의 점 하나를 뷰포트의 픽셀 좌표로 투영하며 뷰포트 중심을 투영 중심으로 사용합니다. 그리기나 마스크 변경은 하지 않습니다.

사용 형식

c
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy);

매개변수

x

카메라 공간의 X 좌표입니다. −127~127을 사용하세요. 양의 X는 오른쪽, 양의 Y는 위쪽입니다.

y

카메라 공간의 Y 좌표입니다. −127~127을 사용하세요. 양의 X는 오른쪽, 양의 Y는 위쪽입니다.

z

카메라 공간의 Z 좌표입니다. 깊이는 32~255여야 하며 값이 클수록 멀리 있습니다.

sx

투영된 가로 픽셀 좌표를 저장할 쓰기 가능한 s16 포인터입니다. 출력 저장 위치는 서로 달라야 합니다.

sy

투영된 세로 픽셀 좌표를 저장할 쓰기 가능한 s16 포인터입니다. 출력 저장 위치는 서로 달라야 합니다.

반환값

성공하면 1, 좌표가 범위를 벗어나거나 출력 포인터가 null이면 0을 반환합니다. 실패하면 출력값은 바뀌지 않습니다.

사용 조건과 제한

깊이가 유효해도 화면 안에 투영된다는 보장은 없습니다. 출력 좌표는 뷰포트 범위로 제한하지 않습니다. 정수 역수 근사 때문에 이상적인 부동소수점 투영과 몇 픽셀 차이가 날 수 있습니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
projected=Wire3DFC_ProjectPoint(px,py,pz+96,&sx,&sy);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.

Wire3DFC_ProjectPoint: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.
Wire3DFC_ProjectPoint: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.
Wire3DFC_ProjectPoint: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다. 뷰포트 왼쪽 위를 기준으로 십자 중심은 64×48에서 (32,16), 96×64에서 (48,20), 128×96에서 (64,33)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:46

선언 주석 원문
Project camera-space coordinates using an integer reciprocal table. Require
x/y within -127..127 and z within 32..255. Return zero without touching outputs
on invalid input; success returns one and writes unclipped screen coordinates.

kitaqfc/lib/wire3d.c:235

c
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy) {
    u8 scale;u16 magnitude;
    if(sx==0 || sy==0 || x < -127 || x>127 || y < -127 || y>127 || z<32 || z>255)return 0;
    // Focal length follows half the selected width. Use unsigned magnitude
    // products and shifts so negative coordinates mirror positive ones exactly.
    scale=w3dfc_recip[(u8)z];
    if(x<0){magnitude=__mul16x8((u16)(0-x),scale);*sx=WIRE3D_FC_WIDTH/2-(s16)(magnitude>>7);}
    else{magnitude=__mul16x8((u16)x,scale);*sx=WIRE3D_FC_WIDTH/2+(s16)(magnitude>>7);}
    if(y<0){magnitude=__mul16x8((u16)(0-y),scale);*sy=WIRE3D_FC_HEIGHT/2+(s16)(magnitude>>7);}
    else{magnitude=__mul16x8((u16)y,scale);*sy=WIRE3D_FC_HEIGHT/2-(s16)(magnitude>>7);}
    return 1;
}
Wire3DFC_DrawLine3D

카메라 공간의 두 끝점을 투영하고 둘 다 유효하면 선을 그립니다. 모델 회전이나 평행 이동은 호출 전에 수행하세요.

사용 형식

c
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz);

매개변수

ax

시작점의 카메라 공간 X 좌표입니다. −127~127을 사용하세요.

ay

시작점의 카메라 공간 Y 좌표입니다. −127~127을 사용하세요.

az

시작점의 카메라 공간 Z 좌표입니다. 깊이 32~255를 사용하세요.

bx

끝점의 카메라 공간 X 좌표입니다. −127~127을 사용하세요.

by

끝점의 카메라 공간 Y 좌표입니다. −127~127을 사용하세요.

bz

끝점의 카메라 공간 Z 좌표입니다. 깊이 32~255를 사용하세요.

반환값

반환값은 없습니다.

사용 조건과 제한

어느 한 끝점이라도 투영에 실패하면 선분 전체를 버립니다. 근접 평면과의 교점을 구해 자르는 처리는 없습니다. DrawLine2D는 유효한 선분에서 뷰포트 밖 픽셀을 버립니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_DrawLine3D(-24,-24,96,24,-24,96);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_DrawLine3D: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawLine3D: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawLine3D: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:49

선언 주석 원문
Draw when both endpoints pass the projection range. Near-plane crossings
are rejected, not intersected with the near plane; screen pixels are clipped.

kitaqfc/lib/wire3d.c:247

c
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz) {
    s16 x0,y0,x1,y1;
    if(Wire3DFC_ProjectPoint(ax,ay,az,&x0,&y0)==0)return;
    if(Wire3DFC_ProjectPoint(bx,by,bz,&x1,&y1)==0)return;
    Wire3DFC_DrawLine2D(x0,y0,x1,y1);
}
Wire3DFC_DrawModel

꼭짓점을 한 번씩 회전·평행 이동·투영한 뒤, 인덱스로 지정된 모서리를 그릴 때 계산한 화면 좌표를 재사용합니다.

사용 형식

c
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz);

매개변수

vertices

s8 x/y/z 성분을 가진 Wire3DFC_Vec3 레코드 배열입니다. 성분을 −63~63 범위로 유지하고 지정한 개수만큼 꼭짓점을 제공하세요.

vertex_count

꼭짓점 수입니다. 24보다 크면 처음 24개만 사용합니다.

edges

꼭짓점 인덱스 a/b를 담은 Wire3DFC_Edge 레코드 배열입니다. edge_count개의 원소를 제공하세요.

edge_count

처리할 모서리 수(0~255)입니다. 실제 사용하는 꼭짓점 수를 벗어나는 꼭짓점을 참조하는 모서리는 건너뜁니다.

x

회전 후 더할 X 평행 이동량입니다. 변환된 점은 ProjectPoint의 입력 범위를 만족해야 합니다.

y

회전 후 더할 Y 평행 이동량입니다. 변환된 점은 ProjectPoint의 입력 범위를 만족해야 합니다.

z

회전 후 더할 Z 평행 이동량입니다. 변환된 점은 ProjectPoint의 입력 범위를 만족해야 합니다.

rx

X축 회전각이며 한 바퀴는 32단계입니다.

ry

Y축 회전각이며 한 바퀴는 32단계입니다.

rz

Z축 회전각이며 한 바퀴는 32단계입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

각도는 한 바퀴가 32단계입니다. 0은 회전 없음, 8은 90도, 16은 180도입니다. 하위 5비트만 사용합니다. 회전은 Y, X, Z 순서로 적용합니다.

vertices나 edges가 null이면 아무것도 그리지 않습니다. 테이블은 현재 PRG 매핑에서 계속 읽을 수 있어야 합니다. 이 API는 면 데이터나 깊이 마스크를 사용하지 않고 유효한 모든 모서리를 그리므로 정육면체 뒤쪽 모서리도 보입니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_DrawModel(example_vertices,8,example_edges,12,0,0,96,0,3,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_DrawModel: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawModel: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_DrawModel: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:53

선언 주석 원문
Rotate, translate and project up to 24 vertices, then draw indexed edges.
Invalid edge indices/depth endpoints are skipped. Model data must remain
readable in the current PRG mapping. This draws all edges, without face culling.

kitaqfc/lib/wire3d.c:253

c
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz) {
    u8 i,a,b;s16 px,py,pz;
    if(vertices==0 || edges==0)return;
    if(vertex_count>24)vertex_count=24;
    for(i=0;i<vertex_count;i++) {
        px=vertices[i].x;py=vertices[i].y;pz=vertices[i].z;
        Wire3DFC_RotatePoint(&px,&py,&pz,rx,ry,rz);
        w3dfc_valid[i]=Wire3DFC_ProjectPoint(px+x,py+y,pz+z,&w3dfc_projected_x[i],&w3dfc_projected_y[i]);
    }
    for(i=0;i<edge_count;i++) {
        a=edges[i].a;b=edges[i].b;
        if(a<vertex_count && b<vertex_count && w3dfc_valid[a]!=0 && w3dfc_valid[b]!=0)
            Wire3DFC_DrawLine2D(w3dfc_projected_x[a],w3dfc_projected_y[a],w3dfc_projected_x[b],w3dfc_projected_y[b]);
    }
}
Wire3DFC_EndFrame

숨겨진 CHR 테이블에 그리기에서 사용한 타일과 지워야 할 타일을 전송한 뒤, 완료 후 새 VBlank에서 표시 테이블을 전환합니다.

사용 형식

c
void Wire3DFC_EndFrame(void);

반환값

반환값은 없습니다.

사용 조건과 제한

VBlank 한 번의 전송 묶음은 최대 16타일, 즉 하위 비트플레인 128바이트입니다. 이 동기 호출은 필요한 수만큼 전송 묶음이 끝날 때까지 기다립니다. wire3d_uploaded_tiles는 전송한 타일 수를 나타냅니다. wire3d_transfer_frames는 전송 묶음 수에 교체 대기 한 번을 더한 값이며 게임에서 실제로 경과한 전체 프레임 수는 아닙니다.

숨겨진 테이블의 이력을 이용하여 물체가 이동한 뒤 이전 선을 지울 수 있습니다. 매 프레임 모든 타일 바이트를 비교하는 대신 그리기에서 사용한 타일을 추적합니다.

CHR RAM 8KiB, 카트리지의 쓰기 가능한 PRG RAM $6800–$70BF, 제로 페이지 임시 영역 $00–$0A가 필요합니다. --nes-chr-ram으로 빌드하세요. 작업 공간을 공유하므로 렌더러는 재진입할 수 없습니다.

Init, BeginFrame, 그리기 함수, EndFrame 순서로 호출하세요. 라이브러리는 배경 패턴 테이블 두 개, 네임테이블 0, 배경 팔레트와 스크롤을 관리하며 NMI를 비활성화합니다. 다른 PPU 쓰기 코드와 동시에 실행하지 마세요.

wire3d.c를 포함하기 전에 WIRE3D_FC_WIDTH와 WIRE3D_FC_HEIGHT를 모두 정의하여 64×48(기본값), 96×64, 128×96 중 하나를 선택하세요. 뷰포트는 화면 중앙에 배치됩니다. 작은 크기를 쓰면 그리기와 전송량은 줄지만 예약하는 RAM 용량은 같습니다.

이미지는 세 가지 크기 모두 KUROSAKI에서 캡처했습니다. 검증은 색상을 포함한 256×240 이미지의 모든 픽셀과 RAM의 수치 결과를 비교합니다. 실기 성능을 측정한 결과는 아닙니다.

사용 예

c
Wire3DFC_EndFrame();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

Wire3DFC_EndFrame: 64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
64X48 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_EndFrame: 96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
96X64 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
Wire3DFC_EndFrame: 128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.
128X96 — 완성된 화면에는 검은 배경 위에 청록색 테두리, 중앙의 정육면체, 위쪽 십자, 아래쪽 가로선이 표시됩니다. 테두리는 2D 좌표를, 십자는 점 회전과 투영을, 아래쪽 선은 카메라 공간의 양 끝점을, 정육면체는 꼭짓점과 모서리 배열을 사용합니다. 한 화면에서 이 도형들을 보며 각 API의 역할을 비교할 수 있습니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\wire3d_projection.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\wire3d_projection.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\wire3d_projection.nes --headless --frames 180 --png .\out\wire3d_projection.png
구현과 소스

kitaqfc/lib/wire3d.h:57

선언 주석 원문
Upload changed tiles to the hidden CHR table in bounded VBlank batches and
swap only after completion. Blocks across as many frames as needed. The two
counters report this call's upload batches plus swap, and tile count.

kitaqfc/lib/wire3d.c:336

c
void Wire3DFC_EndFrame(void) {
    u8 tile,back,old;u16 source,destination;
    back=(u8)(w3dfc_front^16);wire3d_transfer_frames=0;wire3d_uploaded_tiles=0;w3dfc_batch_count=0;
    for(tile=0;tile<WIRE3D_FC_TILES;tile++) {
        if(back==0)old=w3dfc_bank0_dirty[tile];else old=w3dfc_bank1_dirty[tile];
        if(old!=0 || w3dfc_dirty[tile]!=0) {
            source=(u16)(0x6800+(u16)tile*8);
            destination=(u16)((u16)back*256+(u16)tile*16);
            w3dfc_src_lo[w3dfc_batch_count]=(u8)source;w3dfc_src_hi[w3dfc_batch_count]=(u8)(source>>8);
            w3dfc_dst_lo[w3dfc_batch_count]=(u8)destination;w3dfc_dst_hi[w3dfc_batch_count]=(u8)(destination>>8);
            w3dfc_batch_count++;wire3d_uploaded_tiles++;
            if(back==0)w3dfc_bank0_dirty[tile]=w3dfc_dirty[tile];else w3dfc_bank1_dirty[tile]=w3dfc_dirty[tile];
            if(w3dfc_batch_count==16)w3dfc_upload_batch();
        }
    }
    if(w3dfc_batch_count!=0)w3dfc_upload_batch();
    // Switch pattern tables only at a fresh VBlank, after every transfer finishes.
    while((w3dfc_status&128)!=0){}
    while((w3dfc_status&128)==0){}
    w3dfc_front=back;__ppu_ctrl_set(back);__scroll_set(0,0);
    wire3d_transfer_frames++;
}

9. 리소스, 매퍼, FDS

bank, asset은 PRG 뱅크와 리소스를 기술합니다. mapper.h의 조작이 실제로 효과를 내는지는 빌드 시 선택한 매퍼에 달려 있습니다. 스크롤 IRQ 설정, 활성화, 승인, 비활성화를 하나의 일관된 절차로 설계하세요.

FDS 기능은 fds_file, fds_overlay, fds_save, fds_sound로 나뉩니다. 오버레이를 읽으면 기존 주소의 코드가 교체되므로 반환 위치와 데이터의 유효 기간에 주의해야 합니다. 일반 카트리지의 far-call과 같다고 가정하지 마세요.

10. 참고 항목과 예제

아래 사전은 공개 헤더를 기준으로 함수, 함수형 매크로, 별칭을 구별합니다. 전체 헤더에서 구조체와 상수도 확인할 수 있습니다. 선언만 있는 API, 저장만 하는 콜백, 특수 장치 인터페이스는 검증된 일반 동작과 별도로 표시합니다. 구현과 정확히 대조할 수 있도록 소스 주석은 원문을 보존합니다.

vram_get_queue_capacity()는 명령 버퍼의 전체 용량인 128바이트를 반환합니다. vram_get_queue_free()는 전체 용량에서 vram_get_queue_used()를 뺀 남은 바이트 수를 반환합니다. 주소 등의 관리 정보를 포함한 명령 크기이며, 실제 VRAM의 빈 공간이 아닙니다. 타일 하나 쓰기는 4바이트, 동일한 값으로 채우기는 5바이트, 포인터를 사용하는 복사는 6바이트가 필요합니다. commit 후에는 NMI가 큐를 비울 수 있으므로 공간 확인은 commit 전에 하세요.

완성된 예제 프로그램

각 프로그램과 공통 헤더, 제공된 ascii.c, 변환 리소스를 함께 실었습니다. m_으로 시작하는 이름은 설명서용 도우미입니다. 빌드 명령은 기대 결과와 검증 기록을 함께 읽으세요. 명령은 저장소들이 나란히 놓인 상위 폴더에서 실행합니다.

일괄 빌드 스크립트 / 결과와 화면

제공된 문자 집합 표시 fc_font.c

목적: 제공된 독자 제작 글꼴에 어떤 문자·숫자·문장부호가 있는지, 그리고 어떻게 그리는지 알아봅니다.

동작 원리: m_init이 표시와 글꼴을 준비합니다. m_text가 각 행을 그리고, m_put은 문자 코드 34로 큰따옴표를 그립니다.

조작 방법과 기대 결과: 대문자 26개, 소문자 26개, 숫자 10개, 문장부호 30개가 있는지 확인하세요. 모든 글자가 읽기 쉬운지 살펴보세요. 입력 조작은 필요하지 않습니다.

제공된 문자 집합 표시: 대문자 26개, 소문자 26개, 숫자 10개, 문장부호 30개가 있는지 확인하세요. 모든 글자가 읽기 쉬운지 살펴보세요. 입력 조작은 필요하지 않습니다.
표시 예: 대문자 26개, 소문자 26개, 숫자 10개, 문장부호 30개가 있는지 확인하세요. 모든 글자가 읽기 쉬운지 살펴보세요. 입력 조작은 필요하지 않습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_font.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_font.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Display the letters, digits and punctuation from the supplied ascii.c font.
// Expected: Uppercase, lowercase, numbers and all 30 supplied punctuation glyphs
#include "fc_common.h"


// Lay out every supplied character category, yielding between rows. The final
// m_put uses character code 34 to display the double quote separately.
void main(void) {
    m_init();
    m_text(2,3,"FONT");
    m_text(2,5,"ABCDEFGHIJKLMNOP"); m_wait();
    m_text(2,6,"QRSTUVWXYZ"); m_wait();
    m_text(2,8,"0123456789"); m_wait();
    m_text(2,10,"abcdefghijklmnop"); m_wait();
    m_text(2,11,"qrstuvwxyz"); m_wait();
    m_text(2,13,"!#$%&'()*+,-./"); m_wait();
    m_text(2,14,":;<=>?@[]^_`{}~"); m_wait();
    m_put(16,13,34); m_wait();
    while (1) { m_wait();  }
}
첫 텍스트 표시 fc_hello.c

목적: ROM을 시작하고 배경에 문자와 숫자를 그리는 기본 순서를 알아봅니다.

동작 원리: m_init 후 m_text가 인사말을 그리고 m_number가 42를 표시 형식으로 바꿉니다. 마지막 루프는 화면을 유지하며 프레임을 기다립니다.

조작 방법과 기대 결과: 화면에 HELLO WORLD와 042가 표시됩니다. 앞의 0은 십진수를 세 자리로 표시하기 위한 것입니다.

첫 텍스트 표시: 화면에 HELLO WORLD와 042가 표시됩니다. 앞의 0은 십진수를 세 자리로 표시하기 위한 것입니다.
표시 예: 화면에 HELLO WORLD와 042가 표시됩니다. 앞의 0은 십진수를 세 자리로 표시하기 위한 것입니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_hello.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_hello.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Display a first message on screen.
// Expected: HELLO WORLD and 042
#include "fc_common.h"


// Initialize the display/font, write HELLO WORLD and a three-digit 042,
// then wait for frames so the result remains visible.
void main(void) {
    m_init();
    m_text(2,3,"HELLO");
    m_text(2,5,"HELLO WORLD");
    m_number(42);
    while (1) { m_wait();  }
}
더 넓은 타입의 중간값으로 연산 fc_arithmetic.c

목적: 8비트로 표현할 수 없는 중간 결과를 보관하는 방법을 알아봅니다.

동작 원리: (u16)200 + 100의 결과는 300입니다. 프로그램은 이를 10으로 나눈 뒤 u8로 변환해 표시합니다. 덧셈 전에 더 넓은 타입으로 캐스팅한다는 점에 주목하세요.

조작 방법과 기대 결과: 030은 계산 결과인 30을 나타냅니다.

더 넓은 타입의 중간값으로 연산: 030은 계산 결과인 30을 나타냅니다.
표시 예: 030은 계산 결과인 30을 나타냅니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_arithmetic.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_arithmetic.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Variables, arithmetic and type conversion.
// Expected: 030
#include "fc_common.h"


// Widen before adding 200 + 100, divide the 16-bit sum, and display 30.
// The cast demonstrates avoiding an 8-bit intermediate result.
void main(void) {
    m_init();
    m_text(2,3,"ARITHMETIC");
    u16 sum;
    sum = (u16)200 + 100;
    m_number((u8)(sum / 10));
    while (1) { m_wait();  }
}
분기와 반복 fc_control.c

목적: 간단한 계산에서 for, continue, while, if/else를 사용해 봅니다.

동작 원리: for 루프는 0부터 7까지 더하면서 3을 건너뛰어 25를 만듭니다. while 루프는 32까지 증가시키고, 이어지는 if는 이 값이 일치하면 42를 대입합니다.

조작 방법과 기대 결과: 042가 최종 결과입니다. 이 프로그램은 if/else 분기를 사용합니다.

분기와 반복문: 최종 결과는 042입니다. 이 프로그램은 if/else 분기를 사용합니다.
표시 예: 042가 최종 결과입니다. 이 프로그램은 if/else 분기를 사용합니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_control.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_control.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Conditional branches and loops.
// Expected: 042
#include "fc_common.h"


// Exercise loop control and a final branch, then display the selected value 42.
void main(void) {
    m_init();
    m_text(2,3,"CONTROL");
    u8 i; u8 sum; sum=0;
    for (i=0;i<8;i++) { if (i==3) continue; sum=(u8)(sum+i); }
    while (sum<32) { sum++; }
    if (sum==32) sum=42; else sum=0;
    m_number(sum);
    while (1) { m_wait();  }
}
함수, 배열, 구조체, 포인터 fc_aggregate.c

목적: 값을 묶고 함수와 포인터로 접근하는 방법을 알아봅니다.

동작 원리: 프로그램은 (10,20)을 담은 Point를 복사한 뒤 포인터로 배열에 1부터 4까지 씁니다. 함수 결과 30에 배열 값 1, 2, 4와 상수 5를 더합니다.

조작 방법과 기대 결과: 합계가 042로 표시됩니다. 구조체 복사, ptr[i]에 쓰는 과정과 함수 반환값을 따라가 보세요.

함수, 배열, 구조체, 포인터: 합계가 042로 표시됩니다. 구조체 복사, ptr[i]에 쓰는 과정과 함수 반환값을 따라가 보세요.
표시 예: 합계가 042로 표시됩니다. 구조체 복사, ptr[i]에 쓰는 과정과 함수 반환값을 따라가 보세요.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_aggregate.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_aggregate.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Functions, arrays, structures and pointers.
// Expected: 042
#include "fc_common.h"

typedef struct { u8 x; u8 y; } Point;
// Add two byte values and explicitly narrow the returned result to u8.
u8 add(u8 a,u8 b) { return (u8)(a+b); }
// Copy a structure, fill an array through a pointer, and combine selected values
// through add() to produce 42. Keep the final screen visible in the idle loop.
void main(void) {
    m_init();
    m_text(2,3,"AGGREGATE");
    Point a; Point b; u8 i; u8 data[4]; u8* ptr;
    a.x=10; a.y=20; b=a; ptr=data;
    for(i=0;i<4;i++) ptr[i]=(u8)(i+1);
    m_number((u8)(add(b.x,b.y)+ptr[0]+ptr[1]+ptr[3]+5));
    while (1) { m_wait();  }
}
비트 플래그와 버퍼 복사 fc_bits_memory.c

목적: 플래그를 한 바이트에 담고 버퍼를 별도 저장 공간에 복사합니다.

동작 원리: __memset으로 4바이트를 지웁니다. 비트 3을 설정하고 비트 1을 반전한 뒤 __memcpy로 4바이트 모두를 복사합니다.

조작 방법과 기대 결과: 복사 대상의 첫 바이트가 010으로 표시됩니다. 비트 3의 값 8과 비트 1의 값 2를 더한 결과입니다.

비트 플래그와 버퍼 복사: 복사 대상의 첫 바이트가 010으로 표시됩니다. 비트 3의 값 8과 비트 1의 값 2를 더한 결과입니다.
표시 예: 복사 대상의 첫 바이트가 010으로 표시됩니다. 비트 3의 값 8과 비트 1의 값 2를 더한 결과입니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_bits_memory.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_bits_memory.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Bit operations and memory copying.
// Expected: 010
#include "fc_common.h"


// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main(void) {
    m_init();
    m_text(2,3,"BITS MEMORY");
    u8 data[4]; u8 copy[4];
    __memset(data,0,4); __bit_set(data,3); __bit_toggle(data,1);
    __memcpy(copy,data,4); m_number(copy[0]);
    while (1) { m_wait();  }
}
새로 누른 버튼 횟수 세기 fc_input.c

목적: 버튼을 새로 누른 경우와 계속 누르고 있는 경우를 구분합니다.

동작 원리: 매 프레임 input_update를 호출합니다. input_pressed(BTN_A)가 참일 때만 카운터가 증가합니다.

조작 방법과 기대 결과: 초기값은 000입니다. A를 누르면 001이 되며, 누르고 있어도 증가하지 않습니다. 버튼을 놓았다가 다시 누르면 002가 됩니다. 바이트 카운터는 255 다음에 000으로 돌아갑니다.

새로 누른 버튼 횟수 세기: 초기값은 000입니다. A를 누르면 001이 되며, 누르고 있어도 증가하지 않습니다. 버튼을 놓았다가 다시 누르면 002가 됩니다. 바이트 카운터는 255 다음에 000으로 돌아갑니다.
표시 예: 초기값은 000입니다. A를 누르면 001이 되며, 누르고 있어도 증가하지 않습니다. 버튼을 놓았다가 다시 누르면 002가 됩니다. 바이트 카운터는 255 다음에 000으로 돌아갑니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/input.c kitaq-docs/samples/fc_input.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_input.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Distinguish a new button press from a held button.
// Expected: 000 initially; A increments once per press
#include "fc_common.h"
#include "input.h"
u8 manual_count;
// Sample buttons once per frame and increment only on a new A press.
// Holding A does not repeatedly increment; the byte-sized counter wraps after 255.
void main(void) {
    m_init();
    m_text(2,3,"INPUT");
    input_init();
    m_number(0);
    while (1) { m_wait(); input_update(); if(input_pressed(BTN_A)) { manual_count++; m_number(manual_count); } }
}
Q8.8 고정소수점 값 곱하기 fc_fixed.c

목적: 정수를 Q8.8로 변환하고 곱한 다음 다시 정수로 바꾸는 방법을 알아봅니다.

동작 원리: fix_from_int가 3과 4를 Q8.8로 변환합니다. fix_mul로 곱하고 fix_to_int로 결과를 다시 정수로 바꿉니다.

조작 방법과 기대 결과: 012는 3 × 4 = 12를 뜻합니다. 256배로 저장된 내부값이 아니라 변환한 정수입니다.

Q8.8 고정소수점 값 곱하기: 012는 3 × 4 = 12를 뜻합니다. 256배로 저장된 내부값이 아니라 변환한 정수입니다.
표시 예: 012는 3 × 4 = 12를 뜻합니다. 256배로 저장된 내부값이 아니라 변환한 정수입니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/fixed.c kitaq-docs/samples/fc_fixed.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_fixed.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Calculate with signed Q8.8 fixed-point values.
// Expected: 012
#include "fc_common.h"
#include "fixed.h"

// Convert 3 and 4 to Q8.8, multiply them, convert the result back to an
// integer, and display 12.
void main(void) {
    m_init();
    m_text(2,3,"FIXED");
    fix8 a; fix8 b; a=fix_from_int(3); b=fix_from_int(4);
    m_number((u8)fix_to_int(fix_mul(a,b)));
    while (1) { m_wait();  }
}
고정 오브젝트 풀에서 할당 fc_entity.c

목적: 동적 할당 없이 고정 슬롯 집합에서 오브젝트를 생성합니다.

동작 원리: entity_init이 풀을 초기화합니다. entity_create가 (20,30)에 타입 1을 만들고, 프로그램은 실패 ID 0xFF가 아닌지 확인한 뒤 vx를 2로 설정합니다.

조작 방법과 기대 결과: 001은 활성 오브젝트 수이며 ID나 좌표가 아닙니다. 이 예제는 오브젝트를 갱신하거나 그리지 않으므로 움직이는 캐릭터는 나타나지 않습니다.

고정 오브젝트 풀에서 할당: 001은 활성 오브젝트 수이며 ID나 좌표가 아닙니다. 이 예제는 오브젝트를 갱신하거나 그리지 않으므로 움직이는 캐릭터는 나타나지 않습니다.
표시 예: 001은 활성 오브젝트 수이며 ID나 좌표가 아닙니다. 이 예제는 오브젝트를 갱신하거나 그리지 않으므로 움직이는 캐릭터는 나타나지 않습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/entity.c kitaq-docs/samples/fc_entity.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_entity.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Allocate an entity from a fixed-capacity object pool.
// Expected: 001
#include "fc_common.h"
#include "entity.h"

// Create one entity and check the 0xFF allocation-failure sentinel before
// accessing its velocity. Display the pool occupancy, not the entity ID.
void main(void) {
    m_init();
    m_text(2,3,"ENTITY");
    u8 id; entity_init(); id=entity_create(1,20,30);
    if(id!=0xFF) entity_get(id)->vx=2; m_number(entity_count_active());
    while (1) { m_wait();  }
}
RAM에 진단 값 기록 fc_debug.c

목적: 화면 표시와 별개로 이름이 붙은 게임 값을 진단 버퍼에 보관합니다.

동작 원리: 디버깅을 초기화하고 프레임 태그를 1로 설정한 뒤 HP라는 이름으로 값 42를 기록합니다.

조작 방법과 기대 결과: 001은 추적 항목 수입니다. HP=42는 진단 버퍼에 저장되며, 화면만으로는 그 내용을 보거나 검증할 수 없습니다.

RAM에 진단 값 기록: 001은 추적 항목 수입니다. HP=42는 진단 버퍼에 저장되며, 화면만으로는 그 내용을 보거나 검증할 수 없습니다.
표시 예: 001은 추적 항목 수입니다. HP=42는 진단 버퍼에 저장되며, 화면만으로는 그 내용을 보거나 검증할 수 없습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/debug.c kitaq-docs/samples/fc_debug.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_debug.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Record diagnostic values in the debug trace.
// Expected: 001; trace log contains HP=42
#include "fc_common.h"
#include "debug.h"

// Record HP = 42 with frame tag 1 and display the number of trace entries.
// The trace buffer contains the value; the screen displays the count, not HP.
void main(void) {
    m_init();
    m_text(2,3,"DEBUG");
    debug_init(); debug_set_frame(1); debug_trace_u8("HP",42);
    m_number(debug_get_trace_count());
    while (1) { m_wait();  }
}
위치 이력 저장 fc_chain.c

목적: 위치 이력을 링 버퍼에 저장하는 방법을 알아봅니다.

동작 원리: chain_init은 점 4개를 담을 수 있는 배열을 연결합니다. 그다음 chain_push_head가 (20,30)을 기록합니다.

조작 방법과 기대 결과: 042는 고정된 완료 표시이며 좌표나 개수가 아닙니다. 저장된 이력은 trail과 points를 살펴보세요. 이 프로그램은 따라오는 몸체를 그리지 않습니다.

위치 이력 저장: 042는 고정된 완료 표시이며 좌표나 개수가 아닙니다. 저장된 이력은 trail과 points를 살펴보세요. 이 프로그램은 따라오는 몸체를 그리지 않습니다.
표시 예: 042는 고정된 완료 표시이며 좌표나 개수가 아닙니다. 저장된 이력은 trail과 points를 살펴보세요. 이 프로그램은 따라오는 몸체를 그리지 않습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/chain.c kitaq-docs/samples/fc_chain.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_chain.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Store a position history in a ring buffer.
// Expected: 042; history contains (20,30)
#include "fc_common.h"
#include "chain.h"
Chain trail; ChainPoint points[4];
// Attach four history slots and record one point. The displayed 42 is a fixed
// completion marker; inspect the chain to verify the stored (20,30) point.
void main(void) {
    m_init();
    m_text(2,3,"CHAIN");
    chain_init(&trail,points,4); chain_push_head(&trail,20,30);
    m_number(42);
    while (1) { m_wait();  }
}
라이브러리 프레임 카운터 읽기 fc_system.c

목적: 프레임 단위 대기가 라이브러리 프레임 카운터에 미치는 영향을 확인합니다.

동작 원리: system_init 후 프로그램은 system_wait_vblank를 두 번 호출하고 system_get_frame8을 표시합니다.

조작 방법과 기대 결과: 결과는 002입니다. 대기 루프는 매뉴얼 보조 함수 m_wait를 사용하므로 표시 값이 계속 증가하지 않습니다.

라이브러리 프레임 카운터 읽기: 결과는 002입니다. 대기 루프는 매뉴얼 보조 함수 m_wait를 사용하므로 표시 값이 계속 증가하지 않습니다.
표시 예: 결과는 002입니다. 대기 루프는 매뉴얼 보조 함수 m_wait를 사용하므로 표시 값이 계속 증가하지 않습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/system.c kitaq-docs/samples/fc_system.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_system.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Count completed frame waits.
// Expected: 002
#include "fc_common.h"
#include "system.h"

// Reset the library frame count, perform two counted waits, and display 2.
// Subsequent m_wait calls leave that library counter unchanged.
void main(void) {
    m_init();
    m_text(2,3,"SYSTEM");
    system_init(); system_wait_vblank(); system_wait_vblank();
    m_number(system_get_frame8());
    while (1) { m_wait();  }
}
현재 장면 ID 선택 fc_scene.c

목적: 타이틀 화면과 플레이 화면 등을 구분하는 ID를 선택하는 방법을 알아봅니다.

동작 원리: 0으로 초기화한 SceneDef 항목 두 개를 등록하고 scene_change로 ID 1을 선택합니다.

조작 방법과 기대 결과: 001은 scene_get_current가 반환한 현재 ID입니다. 이 예제에는 장면 콜백이나 전환 그리기가 등록되어 있지 않습니다.

현재 장면 ID 선택: 001은 scene_get_current가 반환한 현재 ID입니다. 이 예제에는 장면 콜백이나 전환 그리기가 등록되어 있지 않습니다.
표시 예: 001은 scene_get_current가 반환한 현재 ID입니다. 이 예제에는 장면 콜백이나 전환 그리기가 등록되어 있지 않습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/runtime.c kitaqfc/lib/scene.c kitaq-docs/samples/fc_scene.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_scene.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// The scene streaming module shares runtime.c queue symbols, even though this demo only changes IDs.
#define MANUAL_FC_RUNTIME 1
// Switch the current scene ID.
// Expected: 001; no callbacks registered
#include "fc_common.h"
#include "scene.h"
SceneDef manual_scenes[2];
// Register two zero-initialized scene entries, select ID 1 and display it.
// No callbacks are registered in this example.
void main(void) {
    m_init();
    m_text(2,3,"SCENE");
    scene_init(manual_scenes,2); scene_change(1);
    m_number(scene_get_current());
    while (1) { m_wait();  }
}
Q5.3 소수 단위 이동 누적 fc_subpixel.c

목적: 서브픽셀 이동량은 나머지로 보관하고, 정수 픽셀만 위치에 더합니다.

동작 원리: X=40에서 시작해 루프가 2/8픽셀을 여덟 번 더합니다. 오른쪽 시프트로 정수 픽셀을 추출하고 마스크로 나머지를 유지합니다.

조작 방법과 기대 결과: 총 이동량은 2픽셀이므로 화면에 X=42가 042로 표시됩니다. 이 직접 적분 예제는 양의 방향 이동을 처리합니다. 음의 방향에는 알맞은 빌림 처리 경로가 필요합니다.

Q5.3 소수 단위 이동 누적: 총 이동량은 2픽셀이므로 화면에 X=42가 042로 표시됩니다. 이 직접 적분 예제는 양의 방향 이동을 처리합니다. 음의 방향에는 알맞은 빌림 처리 경로가 필요합니다.
표시 예: 총 이동량은 2픽셀이므로 화면에 X=42가 042로 표시됩니다. 이 직접 적분 예제는 양의 방향 이동을 처리합니다. 음의 방향에는 알맞은 빌림 처리 경로가 필요합니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_subpixel.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_subpixel.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Accumulate Q5.3 eighth-pixel motion into an integer coordinate.
// Expected: 042; explicit game-side integration using header constants
#include "fc_common.h"
#include "physics2d.h"

// Integrate eight increments of 2/8 pixel. Carry whole pixels into X and
// retain the fractional remainder with the supplied mask, moving X from 40 to 42.
void main(void) {
    m_init();
    m_text(2,3,"SUBPIXEL");
    KQ2DPosition x; KQ2DFraction frac; KQ2DSpeed speed; u8 i;
    // This example demonstrates positive motion only; signed negative motion needs a matching carry/borrow path.
    x=40; frac=0; speed=2;
    for(i=0;i<8;i++) {
        frac=(u8)(frac+speed);
        x=(u8)(x+(frac>>KQ2D_SUBPIXEL_BITS));
        frac=(u8)(frac & KQ2D_SUBPIXEL_MASK);
    }
    m_number(x);
    while (1) { m_wait();  }
}
NES 배경 스크롤 fc_scroll.c

목적: PPU 스크롤 위치를 갱신해 배경을 이동합니다.

동작 원리: __scroll_set(0,0)으로 시작한 다음 매 프레임 phase를 한 번 증가시켜 가로 위치로 사용합니다.

조작 방법과 기대 결과: SCROLL이라는 글자의 가로 위치가 바뀝니다. phase는 255에서 0으로 돌아갑니다. 여러 네임테이블에 걸친 넓은 맵의 예제는 아닙니다.

NES 배경 스크롤: SCROLL이라는 글자의 가로 위치가 바뀝니다. phase는 255에서 0으로 돌아갑니다. 여러 네임테이블에 걸친 넓은 맵의 예제는 아닙니다.
표시 예: SCROLL이라는 글자의 가로 위치가 바뀝니다. phase는 255에서 0으로 돌아갑니다. 여러 네임테이블에 걸친 넓은 맵의 예제는 아닙니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_scroll.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_scroll.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Scroll the NES background horizontally.
// Expected: title scrolls horizontally
#include "fc_common.h"

u8 phase;
// Advance an 8-bit horizontal scroll position once per frame, wrapping naturally
// after 255. Background motion demonstrates the changing scroll register.
void main(void) {
    m_init();
    m_text(2,3,"SCROLL");
    __scroll_set(0,0);
    while (1) { m_wait(); phase++; __scroll_set(phase,0); }
}
NMI를 통한 타일 갱신 적용 fc_vram_queue.c

목적: 메인 프로그램에서 PPU 갱신을 제출하고 NMI 처리 경로에서 적용합니다.

동작 원리: __vramq_put이 네임테이블 주소 0x2103에 타일 52를 예약하고 __vramq_commit이 제출합니다. 이 주소는 타일 좌표 (3,8)에 해당합니다.

조작 방법과 기대 결과: 배경 좌표 (3,8)에 숫자 4 하나가 나타납니다. 제공된 글꼴에서 타일 52는 숫자 4입니다.

NMI를 통한 타일 갱신 적용: 배경 좌표 (3,8)에 숫자 4 하나가 나타납니다. 제공된 글꼴에서 타일 52는 숫자 4입니다.
표시 예: 배경 좌표 (3,8)에 숫자 4 하나가 나타납니다. 제공된 글꼴에서 타일 52는 숫자 4입니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_vram_queue.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_vram_queue.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Submit a VRAM update for the NMI handler.
// Expected: 4 at (3,8)
#include "fc_common.h"


// Queue tile 52 at nametable address 0x2103, corresponding to (3,8), and
// commit the queue so the NMI update path displays the digit 4.
void main(void) {
    m_init();
    m_text(2,3,"VRAM QUEUE");
    __vramq_put(0x2103,52); __vramq_commit();
    while (1) { m_wait();  }
}
NES OAM 스프라이트 표시 fc_sprite.c

목적: 배경과 독립적으로 이미지를 그릴 수 있도록 스프라이트 색상과 OAM 데이터를 준비합니다.

동작 원리: 스프라이트 팔레트를 불러오고 슬롯 0에 위치 (70,80), 타일 65, 속성 0을 설정합니다. 렌더링을 켠 뒤 OAM DMA를 반복합니다.

조작 방법과 기대 결과: 정지된 문자 A가 스프라이트로 나타납니다. OAM 설정과 전송을 보여 주는 예제이며 애니메이션은 아닙니다.

NES OAM 스프라이트 표시: 정지된 문자 A가 스프라이트로 나타납니다. 이 예제는 애니메이션이 아니라 OAM 설정과 전송을 보여 줍니다.
표시 예: 정지된 문자 A가 스프라이트로 나타납니다. OAM 설정과 전송을 보여 주는 예제이며 애니메이션은 아닙니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe  kitaq-docs/samples/fc_sprite.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_sprite.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Display a sprite using NES OAM.
// Expected: A sprite on screen
#include "fc_common.h"


// Load the sprite palette, assign font tile 65 to OAM slot zero, enable
// background/sprite display, and transfer OAM after each frame wait.
void main(void) {
    m_init();
    m_text(2,3,"SPRITE");
    __palette_sp_load(manual_pal); __sprite_set(0,70,80,65,0);
    __ppu_mask_set(0x1E);
    while (1) { m_wait(); __oam_dma(); }
}
APU 펄스음 재생 fc_sound.c

목적: NES 내장 펄스 채널의 제어·타이머·길이 값을 설정합니다.

동작 원리: nes_apu_init 후 프로그램은 nes_sfx_square1(0xBF,400,20)을 호출합니다. 제어값 0xBF는 고정 음량을 선택하고 길이 카운터를 멈추므로 음이 지속됩니다. 20은 20프레임이 아니라 길이 테이블 인덱스입니다.

조작 방법과 기대 결과: 042가 표시되는 동안 펄스음이 지속됩니다. 멈추려면 nes_apu_silence_all을 호출하세요. 화면의 표시만으로는 소리 출력을 검증할 수 없습니다.

APU 펄스음 재생: 042가 표시되는 동안 펄스음이 지속됩니다. 멈추려면 nes_apu_silence_all을 호출하세요. 화면의 표시만으로는 소리 출력을 검증할 수 없습니다.
표시 예: 042가 표시되는 동안 펄스음이 지속됩니다. 멈추려면 nes_apu_silence_all을 호출하세요. 화면의 표시만으로는 소리 출력을 검증할 수 없습니다.

소스 내려받기 / 검증 상태

powershell
.\kitaqfc\kitaqfc.exe kitaqfc/lib/audio.c kitaq-docs/samples/fc_sound.c -I kitaqfc/lib -I kitaq-docs/samples -o out/fc_sound.nes --mapper=nrom --nes-chr=kitaq-docs/samples/font.chr
c
// Play a pulse-channel effect on the built-in NES APU.
// Expected: 042 and a pulse tone
#include "fc_common.h"
#include "audio.h"

// Initialize the APU, trigger pulse channel 1 with the chosen timer/length
// values, and leave the screen displaying 42 while the hardware plays the tone.
void main(void) {
    m_init();
    m_text(2,3,"SOUND");
    // Control BF selects constant volume and halts the length counter, so this tone sustains.
    // Call nes_apu_silence_all when the game should stop it; 20 is a length-table index, not frames.
    nes_apu_init(); nes_sfx_square1(0xBF,400,20);
    m_number(42);
    while (1) { m_wait();  }
}

라이브러리 API 사전

총 294개 항목입니다. 항목을 열면 문법, 인수, 소스 주석, 사용 예를 볼 수 있습니다. 호출 조각은 주변 코드의 초기화가 필요하며 독립적인 ROM이 아닙니다. 인수 전달 예제는 준비한 값을 API에 넘기는 방법을 보여 줄 뿐, 장치에서의 동작 검증을 뜻하지 않습니다.

actor.h — 게임 액터 배열

NesActor는 월드 위치, 화면 위치, 사각형 크기를 묶습니다. 도우미는 카메라 좌표 변환, 메타스프라이트 OAM 기록, 월드 공간 겹침 검사를 수행합니다. 속도, 화면 밖 제외, OAM DMA는 게임이 관리합니다.

nes_actor_collide

월드 좌표에서 축에 정렬된 액터 사각형의 겹침을 검사합니다.

사용 형식

c
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b);

매개변수

a

첫 번째 액터입니다.

b

두 번째 액터입니다.

반환값

겹치면 1, 분리되어 있거나 가장자리만 닿으면 0을 반환합니다.

사용 조건과 제한

너비·높이는 양수를 쓰고 위치+크기≤65535를 유지하세요. 화면 좌표, attr, flags는 무시합니다.

사용 예

c
result[3]=nes_actor_collide(&a,&b);nes_actor_set_world(&b,315,95);result[4]=nes_actor_collide(&a,&b);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

월드 (300,95)를 카메라 (268,32)로 변환하면 빨강·파랑 8×8 부분이 (32,64), (40,64)에 나타납니다. 원시 OAM Y=63이 화면의 64에 나타나는 것과, 가장자리 접촉일 때 0이던 충돌 결과가 1픽셀 겹치면 1이 되는 것을 보여 줍니다.

nes_actor_collide: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/actor.h:29

선언 주석 원문
Test overlap in world coordinates with exclusive right/bottom edges. Keep
position-plus-size sums within u16; touching edges are not collisions.
Require positive widths and heights; zero extents are not rejected before the edge tests.

kitaqfc/lib/actor.c:49

c
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b)
{
    unsigned short a_right;
    unsigned short b_right;
    unsigned short a_bottom;
    unsigned short b_bottom;

    a_right = (unsigned short)(a->world_x + a->width);
    b_right = (unsigned short)(b->world_x + b->width);
    a_bottom = (unsigned short)(a->world_y + a->height);
    b_bottom = (unsigned short)(b->world_y + b->height);

    if (a_right <= b->world_x)
    {
        return 0;
    }
    if (b_right <= a->world_x)
    {
        return 0;
    }
    if (a_bottom <= b->world_y)
    {
        return 0;
    }
    if (b_bottom <= a->world_y)
    {
        return 0;
    }

    return 1;
}
nes_actor_draw_metasprite

액터의 저장된 화면 좌표로 메타스프라이트를 OAM 복사본에 씁니다.

사용 형식

c
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite);

매개변수

actor

쓰기 가능한 NesActor 포인터입니다.

oam_index

첫 슬롯 0~63입니다. 모든 부분을 담을 공간을 예약하세요.

metasprite

dx,dy,tile,attr의 4바이트 레코드입니다. dx=255이면 끝납니다.

반환값

다음 OAM 슬롯 인덱스를 반환하며, 64개 슬롯의 끝에서 순환합니다.

사용 조건과 제한

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

액터의 attr/flags 필드는 적용하지 않습니다. Y는 원시 OAM 바이트이므로 실제 상단은 1픽셀 아래입니다. OAM 복사본을 화면에 표시하려면 OAM DMA가 필요합니다.

사용 예

c
result[5]=nes_actor_draw_metasprite(&a,0,parts);result[6]=nes_oam_shadow[3];result[7]=nes_oam_shadow[7];nes_oam_dma(2);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

월드 (300,95)를 카메라 (268,32)로 변환하면 빨강·파랑 8×8 부분이 (32,64), (40,64)에 나타납니다. 원시 OAM Y=63이 화면의 64에 나타나는 것과, 가장자리 접촉일 때 0이던 충돌 결과가 1픽셀 겹치면 1이 되는 것을 보여 줍니다.

nes_actor_draw_metasprite: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/actor.h:25

선언 주석 원문
Draw the byte-stream metasprite at cached screen coordinates and return its
next OAM index. Actor attr/flags are not applied by this wrapper.

kitaqfc/lib/actor.c:41

c
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite)
{
    return nes_metasprite_draw(oam_index, actor->screen_x, actor->screen_y, metasprite);
}
nes_actor_set_world

액터의 월드 X/Y를 설정합니다.

사용 형식

c
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y);

매개변수

actor

쓰기 가능한 NesActor 포인터입니다.

x

월드 X, 0~65535픽셀입니다.

y

월드 Y, 0~65535픽셀입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

screen_x/screen_y는 갱신하지 않습니다. 카메라를 통해 화면 좌표로 변환하려면 update_screen을 호출하세요.

사용 예

c
nes_actor_set_world(&a,300,95);nes_actor_set_world(&b,316,95);result[0]=a.world_x;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

월드 (300,95)를 카메라 (268,32)로 변환하면 빨강·파랑 8×8 부분이 (32,64), (40,64)에 나타납니다. 원시 OAM Y=63이 화면의 64에 나타나는 것과, 가장자리 접촉일 때 0이던 충돌 결과가 1픽셀 겹치면 1이 되는 것을 보여 줍니다.

nes_actor_set_world: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/actor.h:19

선언 주석 원문
Set the actor's 16-bit world position without updating cached screen coordinates.

kitaqfc/lib/actor.c:19

c
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y)
{
    actor->world_x = x;
    actor->world_y = y;
}
nes_actor_update_screen

월드 좌표에서 카메라 좌표를 빼고, 하위 바이트를 화면 좌표로 저장합니다.

사용 형식

c
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y);

매개변수

actor

쓰기 가능한 NesActor 포인터입니다.

camera_x

카메라의 월드 X입니다.

camera_y

카메라의 월드 Y입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

음수이거나 256 이상인 결과는 래핑됩니다. 화면 밖 액터를 숨기지 않으므로 그리기 전에 게임에서 제외해야 합니다.

사용 예

c
nes_actor_update_screen(&a,268,32);result[1]=a.screen_x;result[2]=a.screen_y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

월드 (300,95)를 카메라 (268,32)로 변환하면 빨강·파랑 8×8 부분이 (32,64), (40,64)에 나타납니다. 원시 OAM Y=63이 화면의 64에 나타나는 것과, 가장자리 접촉일 때 0이던 충돌 결과가 1픽셀 겹치면 1이 되는 것을 보여 줍니다.

nes_actor_update_screen: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_actor.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_actor.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/actor.h:22

선언 주석 원문
Subtract the camera and retain only the low coordinate bytes. This performs
wrapping projection, not clipping; callers must hide actors outside the viewport.

kitaqfc/lib/actor.c:27

c
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y)
{
    unsigned short dx;
    unsigned short dy;

    dx = (unsigned short)(actor->world_x - camera_x);
    dy = (unsigned short)(actor->world_y - camera_y);

    actor->screen_x = (unsigned char)dx;
    actor->screen_y = (unsigned char)dy;
}

asset.h — 리소스 ID와 데이터 기술자

소재 라이브러리는 ROM 뱅크에 저장한 소재를 8비트 ID로 찾습니다. asset.c를 포함하고, FC에서는 bank.c도 포함합니다. 패딩 없는 6바이트 AssetDesc에는 1바이트 유형, 1바이트 소재 뱅크 번호, 16비트 주소, 바이트 단위 길이가 들어갑니다. 목록과 소재 본체는 서로 다른 뱅크에 둘 수 있습니다.

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

RAW 또는 TILES 소재를 찾아 해당 ROM 뱅크로 전환한 뒤, __vram_write로 최대 128바이트씩 전체 데이터를 전송합니다. 끝나면 bank_get_current()로 저장한 뱅크로 돌아갑니다. 청크 사이에 다음 프레임을 기다리지 않습니다.

패턴 데이터에는 쓰기 가능한 CHR RAM을 사용하고 긴 전송 동안은 렌더링을 끄세요. VBlank에서 전송한다면 전체 작업이 허용 시간 안에 끝나야 하며 128바이트 분할만으로 이를 보장하지 않습니다. 예제는 --mapper=mmc1 --board=surom512로 빌드합니다. 일반 보드에서 --nes-chr를 생략하면 CHR RAM이 아니라 빈 CHR ROM이 만들어집니다.

asset_get

등록된 목록의 ROM 뱅크에서 AssetDesc 하나를 읽어 호출자가 준비한 공간에 복사합니다. 소재 본체가 아니라 소재의 정보를 가져옵니다.

사용 형식

c
u8 asset_get(u8 asset_id, AssetDesc* out_desc);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

out_desc

6바이트 설명자 하나를 저장할 수 있는 쓰기 가능한 영역입니다. 널 포인터이면 목록에 접근하기 전에 실패를 반환합니다.

반환값

설명자를 복사하면 1, 출력 포인터가 널이거나 ID가 범위를 벗어나면 0입니다. 이 실패 경로에서는 출력 공간을 변경하지 않습니다.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

사용 예

c
loaded=asset_get(0,&descriptor);
expect(loaded==1,1);
expect(descriptor.type==ASSET_TYPE_TILES && descriptor.bank==2,2);
expect(descriptor.ptr==asset_shapes && descriptor.len==144,3);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

설명자 0을 읽어 TILES 유형, 뱅크 2, 소재 포인터, 144바이트 길이를 확인합니다. 범위 밖 ID와 널 출력 포인터는 실패해야 하며, 조회 실패 시 기존 출력값이 유지되는지도 확인합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_get: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:25

선언 주석 원문
Copy a descriptor from its ROM bank into caller storage. Null output or an
out-of-range ID returns zero without reading the table.

kitaqfc/lib/asset.c:18

c
u8 asset_get(u8 asset_id, AssetDesc* out_desc)
{
    if (out_desc == 0) return 0;
    if (asset_id >= asset_table_count) return 0;
    __far_memcpy((u8*)out_desc, asset_table_bank, (u16)(asset_table + asset_id), sizeof(AssetDesc));
    return 1;
}
asset_get_bank

설명자에서 소재 본체의 뱅크 번호를 읽습니다. 목록이 들어 있는 뱅크 번호가 아닙니다. 반환 후에도 소재 뱅크가 선택된 상태로 남지 않습니다.

사용 형식

c
u8 asset_get_bank(u8 asset_id);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

반환값

설명자의 8비트 bank를 반환하며 조회 실패 시 0입니다. 유효한 뱅크도 0일 수 있으므로 성공 여부를 구분하려면 asset_get()을 사용하세요.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

각 조회 함수는 호출할 때마다 asset_get으로 설명자를 복사합니다. 뱅크, 포인터, 길이가 모두 필요하다면 asset_get을 한 번 호출하고 필드를 읽으세요.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

사용 예

c
payload_bank=asset_get_bank(0);
expect(payload_bank==2,17); // Payload bank, not descriptor-table bank 3.
expect(asset_get_bank(4)==0,18); // Failure sentinel; a valid descriptor may also use bank 0.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

목록이 뱅크 3에 있어도 소재 본체의 뱅크 2를 반환하는지 확인합니다. 잘못된 ID에서 0을 반환하는지도 검사합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_get_bank: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:33

선언 주석 원문
Return the asset bank or zero on lookup failure; bank zero may also be valid.

kitaqfc/lib/asset.c:74

c
u8 asset_get_bank(u8 asset_id)
{
    AssetDesc desc;
    if (asset_get(asset_id, &desc) == 0) return 0;
    return desc.bank;
}
asset_get_len

설명자에서 소재 길이를 바이트 단위로 읽습니다. 등록된 정보를 반환할 뿐, 소재 본체를 검사하거나 타일 수, 요소 수, 프레임 수로 환산하지 않습니다.

사용 형식

c
u16 asset_get_len(u8 asset_id);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

반환값

바이트 단위의 16비트 len을 반환하며 조회 실패 시 0입니다. 빈 소재도 길이가 0이므로 두 경우를 구분하려면 asset_get()을 사용하세요.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

각 조회 함수는 호출할 때마다 asset_get으로 설명자를 복사합니다. 뱅크, 포인터, 길이가 모두 필요하다면 asset_get을 한 번 호출하고 필드를 읽으세요.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

사용 예

c
expect(asset_get_len(0)==144,21); // Bytes, not tiles.
expect(asset_get_len(3)==0 && asset_get_len(4)==0,22); // Empty asset and invalid ID both yield zero.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

9타일 소재가 타일 수 9가 아닌 바이트 수 144를 반환하는지 확인합니다. 빈 소재와 잘못된 ID는 모두 0이어야 합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_get_len: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:38

선언 주석 원문
Return the descriptor byte length, or zero for an invalid ID.

kitaqfc/lib/asset.c:91

c
u16 asset_get_len(u8 asset_id)
{
    AssetDesc desc;
    if (asset_get(asset_id, &desc) == 0) return 0;
    return desc.len;
}
asset_get_ptr

설명자에 저장된 16비트 소재 주소를 읽습니다. 반환되는 근거리 포인터에는 뱅크 번호가 없습니다. 다른 ROM 뱅크의 소재를 읽을 때는 asset_get_bank()의 결과와 함께 뱅크 간 복사 함수를 사용하세요.

사용 형식

c
const u8* asset_get_ptr(u8 asset_id);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

반환값

저장된 const u8*를 반환하며 조회 실패 시 널입니다. 설명자 안의 소재 포인터가 널인지 검사하지 않으므로 조회 실패를 구분하려면 asset_get()을 사용하세요.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

각 조회 함수는 호출할 때마다 asset_get으로 설명자를 복사합니다. 뱅크, 포인터, 길이가 모두 필요하다면 asset_get을 한 번 호출하고 필드를 읽으세요.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

사용 예

c
payload_pointer=asset_get_ptr(0);
expect(payload_pointer==asset_shapes,19);
expect(*asset_bank_marker==0xA1,20); // Getting the pointer leaves bank 1 selected.
// Use payload_bank together with payload_pointer for a far copy; do not dereference it here.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

반환된 포인터가 ROM 배치 후의 소재 주소와 같고 뱅크 1의 확인용 값도 계속 읽히는지 확인합니다. 이후 그 포인터와 소재 뱅크 2를 뱅크 간 복사 함수에 전달합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_get_ptr: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:36

선언 주석 원문
Return the stored address without switching banks. Pair it with the descriptor
bank before reading banked ROM; a failed lookup returns null.

kitaqfc/lib/asset.c:83

c
const u8* asset_get_ptr(u8 asset_id)
{
    AssetDesc desc;
    if (asset_get(asset_id, &desc) == 0) return 0;
    return desc.ptr;
}
asset_load_raw

소재를 찾아 해당 ROM 뱅크에서 쓰기 가능한 메모리로 min(desc.len, max_len)바이트를 복사합니다. 유형은 검사하지 않으며 타일, 맵, 곡 데이터도 바이트 그대로 복사합니다. 디코딩이나 압축 해제는 하지 않습니다.

사용 형식

c
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

dst

쓰기 대상 메모리입니다. 실제 복사량을 모두 담을 공간을 확보하세요. 0바이트 전송을 제외하면 널 포인터나 잘못된 대상 주소를 검사하지 않습니다.

max_len

복사할 최대 바이트 수이며 범위는 0~65535입니다. 소재 길이보다 작으면 일부만 복사합니다. 0이면 소재 바이트를 복사하지 않습니다.

반환값

조회 실패 시 0, 그 외에는 1입니다. 일부만 복사했거나 한도가 0이거나 빈 소재여도 1입니다. 반환값은 바이트 수가 아니며 전체 소재를 담았다는 보장도 아닙니다.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

원본 전체가 의도한 ROM 매핑에서 읽히고 대상 전체가 쓰기 가능한 메모리 안에 있어야 합니다. 뱅크 창 경계에서 다음 뱅크로 넘어가거나, memmove처럼 겹치는 범위를 처리하거나, VRAM 쓰기 시점을 제어하지 않습니다. VRAM에는 그래픽 전송 API를 사용하세요.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

사용 예

c
loaded=asset_load_raw(0,buffer,7); // Success with truncation: copy seven of 144 bytes.
expect(loaded==1 && buffer[7]==0x55,7);
for(i=0;i<7;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),8);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

144바이트 소재에 한도 7을 지정해 처음 7바이트만 복사하고 다음 바이트의 0x55를 유지하면서도 성공을 반환하는지 확인합니다. 전체 복사, 한도 0, 빈 소재, MAP 소재의 원시 복사도 검사합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_load_raw: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:28

선언 주석 원문
Copy the smaller of the asset length and max_len using a bank-aware transfer.
Return one for a valid ID even when the payload is truncated.

kitaqfc/lib/asset.c:28

c
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len)
{
    AssetDesc desc;
    u16 len;
    if (asset_get(asset_id, &desc) == 0) return 0;
    len = desc.len;
    if (len > max_len) len = max_len;
    __far_memcpy((u8*)dst, desc.bank, (u16)desc.ptr, len);
    return 1;
}
asset_load_tiles

RAW 또는 TILES 소재를 찾아 해당 ROM 뱅크로 전환한 뒤, __vram_write로 최대 128바이트씩 전체 데이터를 전송합니다. 끝나면 bank_get_current()로 저장한 뱅크로 돌아갑니다. 청크 사이에 다음 프레임을 기다리지 않습니다.

사용 형식

c
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);

매개변수

asset_id

0부터 시작하는 소재 번호입니다. 등록된 목록의 항목 수가 count일 때 asset_id < count인 ID가 유효합니다.

vram_dst

CPU 주소나 타일 번호가 아닌 PPU 바이트 주소입니다. 타일 패턴은 $0000~$1FFF의 쓰기 가능한 CHR RAM에 전송하며 타일 하나는 16바이트입니다. 전체 전송이 의도한 PPU 영역 안에 들어가도록 하세요.

반환값

ID가 잘못되었거나 유형이 RAW/TILES가 아니면 0입니다. 허용된 전송을 마치면 1이며 길이 0인 소재도 포함합니다. 성공값이 타일 인코딩이나 VRAM 표시의 정확성을 보장하지는 않습니다.

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

예제의 호출 코드는 고정 뱅크 0에 두어 원본 뱅크를 바꿔도 실행 중인 코드가 사라지지 않도록 합니다. 전송은 동기식입니다. 전송 도중 인터럽트에서 다른 뱅크 전환 복사를 실행하지 마세요. 이 함수만으로 프로그램 전체의 인터럽트 안전성이 확보되지는 않습니다.

전송은 바이트만 쓰며 맵에 타일을 배치하거나 색을 지정하거나 VRAM을 할당하지 않습니다. 타일 맵과 팔레트는 따로 설정하세요. 예제는 렌더링을 끈 상태로 전송하고 이미지가 준비된 뒤 화면을 켭니다.

asset_load_tiles는 매퍼에서 읽은 값이 아니라 bank.c가 기록한 뱅크로 돌아갑니다. bank_switch로 상태를 초기화하고 유지하세요. 매퍼에 직접 쓰거나 __bankswitch만 호출하면 라이브러리 상태는 갱신되지 않습니다. 뱅크 간 복사 내장 함수는 별도로 컴파일러의 뱅크 상태를 복원합니다.

패턴 데이터에는 쓰기 가능한 CHR RAM을 사용하고 긴 전송 동안은 렌더링을 끄세요. VBlank에서 전송한다면 전체 작업이 허용 시간 안에 끝나야 하며 128바이트 분할만으로 이를 보장하지 않습니다. 예제는 --mapper=mmc1 --board=surom512로 빌드합니다. 일반 보드에서 --nes-chr를 생략하면 CHR RAM이 아니라 빈 CHR ROM이 만들어집니다.

사용 예

c
loaded=asset_load_tiles(0,0x0010); // 144 bytes: library uploads chunks of 128 and 16 bytes.
expect(loaded==1 && bank_get_current()==1 && *asset_bank_marker==0xA1,24);
// LCD and NMI are off; CHR RAM is writable. The library selects payload bank 2 and restores bank 1.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

타일 전송 예제는 직접 만든 8×8 패턴 9개, 총 144바이트를 사용합니다. MAP 유형과 잘못된 ID가 거부되는지도 확인합니다. FC에서는 128바이트 청크와 나머지 16바이트를 모두 검증합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_load_tiles: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:31

선언 주석 원문
Switch to the asset bank, upload tile/raw data in chunks of at most 128 bytes,
then restore the bank recorded by this library. The caller supplies safe PPU timing.

kitaqfc/lib/asset.c:41

c
u8 asset_load_tiles(u8 asset_id, u16 vram_dst)
{
    AssetDesc desc;
    const u8* src;
    u16 len;
    u8 chunk;
    u8 old_bank;

    if (asset_get(asset_id, &desc) == 0) return 0;
    if (desc.type != ASSET_TYPE_TILES && desc.type != ASSET_TYPE_RAW) return 0;

    // Restoration uses the library shadow, not a mapper readback; initialize and maintain that shadow.
    old_bank = bank_get_current();
    bank_switch(desc.bank);

    src = desc.ptr;
    len = desc.len;
    // This loop uploads the entire asset synchronously. Chunking does not wait for another frame.
    // Use writable CHR RAM for tile data and keep the source bank visible throughout the transfer.
    while (len != 0) {
        if (len > 128) chunk = 128;
        else chunk = (u8)len;
        __vram_write(vram_dst, src, chunk);
        vram_dst = (u16)(vram_dst + chunk);
        src = src + chunk;
        len = (u16)(len - chunk);
    }

    bank_switch(old_bank);
    return 1;
}
asset_set_table

사용할 소재 목록의 뱅크 번호, 주소, 항목 수를 등록합니다. 활성 목록은 하나뿐입니다. 이 함수는 세 값만 보관하며 목록 복사, 소재 읽기, 메모리 할당은 하지 않습니다.

사용 형식

c
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);

매개변수

bank

소재 본체가 아닌 설명자 목록이 저장된 ROM 뱅크 번호입니다. 8비트 값입니다.

table

AssetDesc 항목 count개가 연속으로 놓인 영역의 근거리 포인터입니다. 등록된 동안 해당 목록 뱅크의 count * 6바이트를 읽을 수 있어야 합니다.

count

항목 수이며 범위는 0~255입니다. 0이면 모든 ID가 무효입니다. asset_set_table(0, 0, 0)으로 빈 목록을 안전하게 등록할 수 있습니다.

반환값

없음(void).

사용 조건과 제한

조회 전에 목록을 등록하세요. 등록 정보는 공유 전역 상태이므로 조회 중에 바꾸면 안 됩니다. 라이브러리는 ID와 asset_get의 출력 포인터를 검사하지만 등록된 목록, 소재 주소, 카트리지 내용의 유효성은 검사하지 않습니다.

사용 예

c
asset_set_table(3,asset_descriptors,4); // Four descriptors in bank 3, payloads in bank 2.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 예제는 뱅크 3의 설명자 4개를 등록하고, 비어 있지 않은 소재는 뱅크 2에서 가져옵니다. 빈 목록도 등록해 주소 0을 읽지 않고 조회가 실패하는지 확인합니다.

예상 화면의 빨간 줄은 픽셀 좌표 (16,32)에서 시작하며 72×8픽셀입니다. 8×8 삼각형, 테두리 두께 1픽셀인 8×8 사각형, 왼쪽 네 열을 채운 타일을 세 번 반복합니다. 삼각형은 왼쪽 위의 1픽셀에서 맨 아래 줄의 8픽셀까지 넓어집니다.

FC에서 (16,64)의 파란 삼각형과 테두리는 __far_memcpy 결과이며, (16,96)의 초록색 쌍은 asset_load_raw 후 CHR로 전송한 결과입니다. 검은 바탕에서 각 쌍은 16×8픽셀입니다. 숫자 상태값뿐 아니라 복사된 이미지 바이트를 직접 확인할 수 있습니다.

예상 상태 표시는 FAILED CHECKS 000과 FIRST FAILURE 00입니다. 검증은 300프레임 동안 실행한 뒤 글자, 도형 마스크, 색을 예상값과 비교합니다. 에뮬레이터 검증이며 실제 하드웨어 시험은 아닙니다.

asset_set_table: SUROM512
SUROM512

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\asset_banks.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\asset_banks.nes --mapper=mmc1 --board=surom512 --no-cache --no-disasm
구현과 소스

kitaqfc/lib/asset.h:22

선언 주석 원문
Retain a bank-qualified descriptor table; the caller keeps its storage valid.

kitaqfc/lib/asset.c:9

c
void asset_set_table(u8 bank, const AssetDesc* table, u8 count)
{
    asset_table_bank = bank;
    asset_table = table;
    asset_table_count = count;
}

attribute.h — 네임테이블 속성

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

사각형으로 팔레트 사분면을 편집하고 겹치는 속성 바이트만 큐에 넣으며, 32×30타일 경계를 검사합니다. palette_map 입력은 타일마다 색상 하나를 지정하는 값이 아니라 완성된 64바이트 속성 테이블입니다.

nes_attr_queue_rect

타일 사각형을 덮는 속성 바이트 행들을 큐에 복사합니다.

사용 형식

c
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

width

타일 단위 너비입니다.

height

타일 단위 높이입니다.

반환값

전부 큐에 들어갔거나 빈 사각형이면 1, 경계가 유효하지 않거나 용량이 부족하면 0을 반환합니다. 경계가 잘못되면 아무것도 큐에 넣지 않습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

속성 1바이트는 4×4타일을 담당합니다. 겹치는 바이트 전체를 전송하므로 이웃 사분면의 올바른 값을 복사본에 유지하세요.

사용 예

c
result[5]=nes_attr_queue_rect(0x2000,2,4,4,4);result[6]=nes_vram_queue_used;result[7]=nes_attr_queue_rect(0x2000,10,4,4,4);result[8]=nes_attr_queue_rect(0x2000,30,28,2,2);nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

직사각형 속성 전송 예제입니다. (16,32)의 빨간 32×32, (80,32)의 파란 32×32, 오른쪽 아래 (240,224)의 초록 16×16 도형을 표시합니다. 인접 사분면을 보존하며 마지막 속성 바이트까지 올바르게 처리합니다.

nes_attr_queue_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/attribute.h:15

선언 주석 원문
Queue the attribute bytes covering a tile rectangle within 32x30 tiles.
Empty rectangles succeed; invalid bounds fail without queuing.
Capacity failure may leave earlier rows queued.

kitaqfc/lib/attribute.c:81

c
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height)
{
    unsigned char qx0;
    unsigned char qy0;
    unsigned char qx1;
    unsigned char qy1;
    unsigned char row;
    unsigned char col;
    // The runtime queue copies these local bytes before return; they are not retained pointers.
    // Caller coordinates must keep each computed row length within eight bytes.
    unsigned char line[8];
    unsigned short ppu_addr;
    unsigned char len;

    if (width == 0 || height == 0) return 1;
    if (tile_x >= 32 || tile_y >= 30) return 0;
    if (width > (unsigned char)(32 - tile_x) || height > (unsigned char)(30 - tile_y)) return 0;

    qx0 = (unsigned char)(tile_x >> 2);
    qy0 = (unsigned char)(tile_y >> 2);
    qx1 = (unsigned char)((tile_x + width - 1) >> 2);
    qy1 = (unsigned char)((tile_y + height - 1) >> 2);

    row = qy0;
    while (1)
    {
        len = (unsigned char)(qx1 - qx0 + 1);
        col = 0;
        while (col != len)
        {
            line[col] = nes_attr_shadow[(unsigned char)(row * 8 + qx0 + col)];
            col = (unsigned char)(col + 1);
        }
        ppu_addr = (unsigned short)(nes_attr_base_from_nt(nt_base) + (unsigned short)(row * 8 + qx0));
        if (nes_vram_queue_try_write(ppu_addr, line, len) == 0)
        {
            return 0;
        }
        if (row == qy1)
        {
            break;
        }
        row = (unsigned char)(row + 1);
    }
    return 1;
}
nes_attr_shadow_build_from_palette_map

이미 비트 단위로 구성된 속성 64바이트를 공유 복사본에 복사합니다.

사용 형식

c
void nes_attr_shadow_build_from_palette_map(unsigned char* src64);

매개변수

src64

읽기 가능한 64바이트 속성 테이블입니다. 타일마다 팔레트 ID 하나를 담은 배열이 아닙니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

사용 예

c
for(i=0;i<64;i++)table[i]=0;table[0]=0xE4;nes_attr_shadow_build_from_palette_map(table);result[0]=nes_attr_shadow[0];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

직사각형 속성 전송 예제입니다. (16,32)의 빨간 32×32, (80,32)의 파란 32×32, 오른쪽 아래 (240,224)의 초록 16×16 도형을 표시합니다. 인접 사분면을 보존하며 마지막 속성 바이트까지 올바르게 처리합니다.

nes_attr_shadow_build_from_palette_map: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/attribute.h:8

선언 주석 원문
KITAQFC phase15 attribute patch helpers.


Copy 64 already-packed attribute bytes into shadow storage. This does not
convert one palette ID per tile into NES attribute bitfields.
The source must expose all 64 packed bytes and stay readable for the duration of this copy.

kitaqfc/lib/attribute.c:26

c
void nes_attr_shadow_build_from_palette_map(unsigned char* src64)
{
    unsigned char i;
    i = 0;
    while (i != 64)
    {
        nes_attr_shadow[i] = (unsigned char)(src64[i] & 0xFF);
        i = (unsigned char)(i + 1);
    }
}
nes_attr_shadow_fill_rect

사각형이 닿는 모든 2×2타일 사분면의 팔레트 필드를 바꿉니다.

사용 형식

c
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index);

매개변수

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

width

타일 단위 너비입니다.

height

타일 단위 높이입니다.

pal_index

팔레트 인덱스입니다. 하위 2비트만 사용합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

사각형 전체를 32×30타일 안에 유지하세요. 비어 있거나 유효하지 않은 사각형은 아무것도 바꾸지 않습니다. 홀수 경계는 사분면 경계까지 확장되므로 사각형 밖의 이웃 타일에도 새 팔레트가 적용될 수 있습니다.

사용 예

c
nes_attr_shadow_fill_rect(2,4,4,4,1);nes_attr_shadow_fill_rect(10,4,4,4,2);nes_attr_shadow_fill_rect(30,28,2,2,3);result[1]=nes_attr_shadow[8];result[2]=nes_attr_shadow[9];result[3]=nes_attr_shadow[15];result[4]=nes_attr_shadow[63];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

직사각형 속성 전송 예제입니다. (16,32)의 빨간 32×32, (80,32)의 파란 32×32, 오른쪽 아래 (240,224)의 초록 16×16 도형을 표시합니다. 인접 사분면을 보존하며 마지막 속성 바이트까지 올바르게 처리합니다.

nes_attr_shadow_fill_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_attributes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_attributes.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/attribute.h:11

선언 주석 원문
Apply a palette to every two-by-two tile quadrant touched by the rectangle.
Empty or out-of-range rectangles leave the shadow unchanged.

kitaqfc/lib/attribute.c:39

c
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index)
{
    unsigned char qx0;
    unsigned char qy0;
    unsigned char qx1;
    unsigned char qy1;
    unsigned char qx;
    unsigned char qy;

    if (width == 0 || height == 0) return;
    if (tile_x >= 32 || tile_y >= 30) return;
    if (width > (unsigned char)(32 - tile_x) || height > (unsigned char)(30 - tile_y)) return;

    qx0 = nes_attr_rect_left(tile_x);
    qy0 = nes_attr_rect_top(tile_y);
    qx1 = nes_attr_rect_left((unsigned char)(tile_x + width - 1));
    qy1 = nes_attr_rect_top((unsigned char)(tile_y + height - 1));

    qy = qy0;
    while (1)
    {
        qx = qx0;
        while (1)
        {
            nes_attr_shadow_set_quad((unsigned char)(qx << 1), (unsigned char)(qy << 1), pal_index);
            if (qx == qx1)
            {
                break;
            }
            qx = (unsigned char)(qx + 1);
        }
        if (qy == qy1)
        {
            break;
        }
        qy = (unsigned char)(qy + 1);
    }
}

audio.h — 음악, 효과음, 음향 제어

표준 APU의 펄스 2개, 삼각파, 노이즈, DMC를 직접 제어합니다. 음악 시퀀서나 자동 믹서가 아니므로 게임이 타이밍과 채널 사용권을 관리합니다.

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

DMC는 $C000..$FFC0의 64바이트 경계에서 ROM을 읽습니다. 길이는 16*n+1바이트로 1..4081입니다. 이 함수들은 정렬·범위를 검사하거나 뱅크를 전환하지 않습니다. 샘플은 독자 데이터를 __aligned(64)로 고정 뱅크 0에 두고 실행 중 배치를 검사합니다.

nes_apu_channel_enable

지정한 마스크로 APU 다섯 채널 전체의 활성 상태를 바꿉니다.

사용 형식

c
void nes_apu_channel_enable(u8 mask);

매개변수

mask

전체 APU_STATUS 활성 마스크입니다. 비트 0..4는 pulse1, pulse2, triangle, noise, DMC를 선택하며 상위 비트는 버립니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_apu_channel_enable: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:17

선언 주석 원문
Replace all five channel-enable bits in APU_STATUS; this is not an additive enable.

kitaqfc/lib/audio.c:43

c
void nes_apu_channel_enable(u8 mask)
{
    APU_STATUS = (u8)(mask & 0x1F);
}
nes_apu_init

프레임 IRQ를 끄고 네 채널을 활성화한 뒤 조용한 제어값을 설정합니다. DMC는 비활성 상태입니다.

사용 형식

c
void nes_apu_init(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_apu_init: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:15

선언 주석 원문
Disable frame IRQs, enable the four tonal/noise channels and install quiet
control defaults. DMC remains disabled.

kitaqfc/lib/audio.c:28

c
void nes_apu_init(void)
{
    APU_FRAME = 0x40;
    APU_STATUS = 0x0F;
    SQ1_VOL = 0x10;
    SQ1_SWEEP = 0x08;
    SQ2_VOL = 0x10;
    SQ2_SWEEP = 0x08;
    TRI_LINEAR = 0x80;
    NOISE_VOL = 0x10;
    DMC_FREQ = 0x00;
    DMC_RAW = 0x00;
}
nes_apu_silence_all

모든 채널을 끄고 펄스·노이즈 음량과 DMC 출력을 지웁니다.

사용 형식

c
void nes_apu_silence_all(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_apu_silence_all: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:19

선언 주석 원문
Disable every channel and clear volume/DMC output settings used by this helper.

kitaqfc/lib/audio.c:49

c
void nes_apu_silence_all(void)
{
    APU_STATUS = 0x00;
    SQ1_VOL = 0x10;
    SQ2_VOL = 0x10;
    TRI_LINEAR = 0x80;
    NOISE_VOL = 0x10;
    DMC_FREQ = 0x00;
    DMC_RAW = 0x00;
}
nes_dmc_config

DMC 제어·출력을 설정하고 샘플 주소와 길이를 레지스터 단위로 변환합니다.

사용 형식

c
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);

매개변수

flags_rate

DMC의 비트 7은 IRQ, 비트 6은 반복, 하위 니블은 재생률 인덱스입니다. 샘플은 IRQ를 활성화하지 않습니다.

output_level

초기 DMC 출력 카운터 0..127입니다. 하위 7비트만 씁니다.

sample_addr

DMC 데이터의 CPU 주소입니다. $C000..$FFC0 안의 64바이트 경계여야 합니다.

sample_len

바이트 길이는 16*n+1 형식의 1..4081입니다. 0이나 표현할 수 없는 길이를 전달하지 마세요.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

DMC는 $C000..$FFC0의 64바이트 경계에서 ROM을 읽습니다. 길이는 16*n+1바이트로 1..4081입니다. 이 함수들은 정렬·범위를 검사하거나 뱅크를 전환하지 않습니다. 샘플은 독자 데이터를 __aligned(64)로 고정 뱅크 0에 두고 실행 중 배치를 검사합니다.

사용 예

c
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
    sound_begin("DMC ORIGINAL DELTAS");
    sound_result[0]=(u8)(((u16)delta&63)==0);
    sound_result[1]=(u8)((u16)delta>=0xC000);
    // Loop at NTSC rate index 10; start the output accumulator at its midpoint.
    nes_dmc_config(0x4A,64,(u16)delta,17);
    nes_dmc_start();
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    nes_dmc_play(0x4C,64,(u16)delta,17);
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    sound_end();
}

예상 결과

독자 델타 패턴을 속도 인덱스 10으로 재생하고 쉬었다가 더 빠른 12로 재생합니다. DMC 정지는 출력 카운터를 유지하므로 간격에 과도음이 남을 수 있습니다. 마지막 연산은 APU 전체를 무음으로 만듭니다.

nes_dmc_config: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:38

선언 주석 원문
Convert a CPU sample address and byte length to DMC register units. Supply
an address in 0xC000-0xFFC0 aligned to 64 bytes and a representable length
16*n+1 in 1..4081; inputs are narrowed without validation or bank management.

kitaqfc/lib/audio.c:115

c
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len)
{
    DMC_FREQ = flags_rate;
    DMC_RAW = (u8)(output_level & 0x7F);
    // Keep the sample bytes mapped at these CPU addresses during playback; this does not select a ROM bank.
    DMC_START = (u8)((sample_addr - 0xC000) >> 6);
    DMC_LEN = (u8)((sample_len - 1) >> 4);
}
nes_dmc_play

DMC 샘플을 설정하고 즉시 재생을 시작합니다.

사용 형식

c
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);

매개변수

flags_rate

DMC의 비트 7은 IRQ, 비트 6은 반복, 하위 니블은 재생률 인덱스입니다. 샘플은 IRQ를 활성화하지 않습니다.

output_level

초기 DMC 출력 카운터 0..127입니다. 하위 7비트만 씁니다.

sample_addr

DMC 데이터의 CPU 주소입니다. $C000..$FFC0 안의 64바이트 경계여야 합니다.

sample_len

바이트 길이는 16*n+1 형식의 1..4081입니다. 0이나 표현할 수 없는 길이를 전달하지 마세요.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

DMC는 $C000..$FFC0의 64바이트 경계에서 ROM을 읽습니다. 길이는 16*n+1바이트로 1..4081입니다. 이 함수들은 정렬·범위를 검사하거나 뱅크를 전환하지 않습니다. 샘플은 독자 데이터를 __aligned(64)로 고정 뱅크 0에 두고 실행 중 배치를 검사합니다.

사용 예

c
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
    sound_begin("DMC ORIGINAL DELTAS");
    sound_result[0]=(u8)(((u16)delta&63)==0);
    sound_result[1]=(u8)((u16)delta>=0xC000);
    // Loop at NTSC rate index 10; start the output accumulator at its midpoint.
    nes_dmc_config(0x4A,64,(u16)delta,17);
    nes_dmc_start();
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    nes_dmc_play(0x4C,64,(u16)delta,17);
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    sound_end();
}

예상 결과

독자 델타 패턴을 속도 인덱스 10으로 재생하고 쉬었다가 더 빠른 12로 재생합니다. DMC 정지는 출력 카운터를 유지하므로 간격에 과도음이 남을 수 있습니다. 마지막 연산은 APU 전체를 무음으로 만듭니다.

nes_dmc_play: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:44

선언 주석 원문
Install sample parameters, then start DMC using the full-channel enable mask.

kitaqfc/lib/audio.c:137

c
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len)
{
    nes_dmc_config(flags_rate, output_level, sample_addr, sample_len);
    nes_dmc_start();
}
nes_dmc_start

APU_STATUS=0x1F를 써서 DMC와 나머지 네 채널을 모두 활성화합니다.

사용 형식

c
void nes_dmc_start(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

DMC는 $C000..$FFC0의 64바이트 경계에서 ROM을 읽습니다. 길이는 16*n+1바이트로 1..4081입니다. 이 함수들은 정렬·범위를 검사하거나 뱅크를 전환하지 않습니다. 샘플은 독자 데이터를 __aligned(64)로 고정 뱅크 0에 두고 실행 중 배치를 검사합니다.

사용 예

c
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
    sound_begin("DMC ORIGINAL DELTAS");
    sound_result[0]=(u8)(((u16)delta&63)==0);
    sound_result[1]=(u8)((u16)delta>=0xC000);
    // Loop at NTSC rate index 10; start the output accumulator at its midpoint.
    nes_dmc_config(0x4A,64,(u16)delta,17);
    nes_dmc_start();
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    nes_dmc_play(0x4C,64,(u16)delta,17);
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    sound_end();
}

예상 결과

독자 델타 패턴을 속도 인덱스 10으로 재생하고 쉬었다가 더 빠른 12로 재생합니다. DMC 정지는 출력 카운터를 유지하므로 간격에 과도음이 남을 수 있습니다. 마지막 연산은 APU 전체를 무음으로 만듭니다.

nes_dmc_start: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:40

선언 주석 원문
Enable DMC together with all four other channels by replacing the status mask.

kitaqfc/lib/audio.c:125

c
void nes_dmc_start(void)
{
    APU_STATUS = 0x1F;
}
nes_dmc_stop

APU_STATUS=0x0F를 써서 DMC를 멈추고 나머지 네 채널 활성 비트를 모두 설정합니다.

사용 형식

c
void nes_dmc_stop(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

DMC는 $C000..$FFC0의 64바이트 경계에서 ROM을 읽습니다. 길이는 16*n+1바이트로 1..4081입니다. 이 함수들은 정렬·범위를 검사하거나 뱅크를 전환하지 않습니다. 샘플은 독자 데이터를 __aligned(64)로 고정 뱅크 0에 두고 실행 중 배치를 검사합니다.

사용 예

c
// A 17-byte original delta pattern, aligned to a 64-byte DMC start boundary.
#include "fc_sound_example.h"
#pragma bank 0
__aligned(64) __prg_rom u8 delta[17]={0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xFF,0xFF,0,0,0xAA};
void main(void) {
    sound_begin("DMC ORIGINAL DELTAS");
    sound_result[0]=(u8)(((u16)delta&63)==0);
    sound_result[1]=(u8)((u16)delta>=0xC000);
    // Loop at NTSC rate index 10; start the output accumulator at its midpoint.
    nes_dmc_config(0x4A,64,(u16)delta,17);
    nes_dmc_start();
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    nes_dmc_play(0x4C,64,(u16)delta,17);
    sound_wait(60);
    nes_dmc_stop(); sound_wait(30);
    sound_end();
}

예상 결과

독자 델타 패턴을 속도 인덱스 10으로 재생하고 쉬었다가 더 빠른 12로 재생합니다. DMC 정지는 출력 카운터를 유지하므로 간격에 과도음이 남을 수 있습니다. 마지막 연산은 APU 전체를 무음으로 만듭니다.

nes_dmc_stop: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_dmc.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-dmc.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:42

선언 주석 원문
Disable DMC while leaving all four other channel-enable bits set.

kitaqfc/lib/audio.c:131

c
void nes_dmc_stop(void)
{
    APU_STATUS = 0x0F;
}
nes_sfx_move_blip

제어값 0x5A, 타이머 0x0280, 길이 인덱스 2로 펄스 1을 트리거해 더 높고 짧은 이동음을 냅니다.

사용 형식

c
void nes_sfx_move_blip(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_move_blip: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:33

선언 주석 원문
Trigger the predefined pulse-1 movement effect.

kitaqfc/lib/audio.c:107

c
void nes_sfx_move_blip(void)
{
    nes_sfx_square1(0x5A, 0x0280, 2);
}
nes_sfx_noise

노이즈만 활성화하고 엔벌로프, 주기/모드, 길이 인덱스를 씁니다.

사용 형식

c
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index);

매개변수

volume

원시 노이즈 제어값: 비트 5는 길이 카운터 정지, 비트 4는 고정 음량, 하위 니블은 음량 또는 엔벌로프 주기입니다.

period_mode

노이즈 레지스터: 비트 7은 짧은 모드, 하위 니블은 주기 테이블 인덱스입니다.

length_index

길이 테이블 인덱스 0..31이며 프레임 수가 아닙니다. 하위 5비트만 레지스터에 전달됩니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_noise: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:29

선언 주석 원문
Enable only noise and load envelope/control, period/mode and length fields.

kitaqfc/lib/audio.c:92

c
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index)
{
    APU_STATUS = 0x08;
    NOISE_VOL = volume;
    NOISE_LO = period_mode;
    NOISE_HI = (u8)(length_index << 3);
}
nes_sfx_square1

펄스 1만 활성화하고 스윕을 끈 뒤 제어값, 타이머, 길이 인덱스를 씁니다.

사용 형식

c
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index);

매개변수

duty_volume

원시 펄스 제어값입니다. 비트 7..6은 듀티, 비트 5는 길이 카운터 정지, 비트 4는 고정 음량, 하위 니블은 음량 또는 엔벌로프 주기입니다.

period

11비트 타이머 0..2047입니다. 펄스 주파수는 CPU 클록/[16*(period+1)], 삼각파는 CPU 클록/[32*(period+1)]입니다.

length_index

길이 테이블 인덱스 0..31이며 프레임 수가 아닙니다. 하위 5비트만 레지스터에 전달됩니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_square1: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:23

선언 주석 원문
Enable only pulse 1, disable its sweep and load control, 11-bit timer and
length-table index. Starting this effect disables the other APU channels.

kitaqfc/lib/audio.c:62

c
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index)
{
    APU_STATUS = 0x01;
    SQ1_SWEEP = 0x08;
    SQ1_VOL = duty_volume;
    SQ1_LO = (u8)period;
    // Only the low five length-index bits and low eleven timer bits reach the registers.
    SQ1_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}
nes_sfx_square2

펄스 2만 활성화하고 스윕을 끈 뒤 제어값, 타이머, 길이 인덱스를 씁니다.

사용 형식

c
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index);

매개변수

duty_volume

원시 펄스 제어값입니다. 비트 7..6은 듀티, 비트 5는 길이 카운터 정지, 비트 4는 고정 음량, 하위 니블은 음량 또는 엔벌로프 주기입니다.

period

11비트 타이머 0..2047입니다. 펄스 주파수는 CPU 클록/[16*(period+1)], 삼각파는 CPU 클록/[32*(period+1)]입니다.

length_index

길이 테이블 인덱스 0..31이며 프레임 수가 아닙니다. 하위 5비트만 레지스터에 전달됩니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_square2: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:25

선언 주석 원문
Enable only pulse 2 and load its control/timer/length fields, disabling other channels.

kitaqfc/lib/audio.c:73

c
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index)
{
    APU_STATUS = 0x02;
    SQ2_SWEEP = 0x08;
    SQ2_VOL = duty_volume;
    SQ2_LO = (u8)period;
    SQ2_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}
nes_sfx_tick_blip

제어값 0x9A, 타이머 0x03F0, 길이 인덱스 4로 펄스 1을 트리거해 짧고 낮은 틱 소리를 냅니다.

사용 형식

c
void nes_sfx_tick_blip(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_tick_blip: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:31

선언 주석 원문
Trigger the predefined pulse-1 tick effect; it inherits the exclusive-channel behavior.

kitaqfc/lib/audio.c:101

c
void nes_sfx_tick_blip(void)
{
    nes_sfx_square1(0x9A, 0x03F0, 4);
}
nes_sfx_triangle

삼각파만 활성화하고 선형 카운터, 타이머, 길이 인덱스를 씁니다.

사용 형식

c
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index);

매개변수

linear

삼각파 선형 카운터: 비트 7은 제어/길이 정지, 하위 7비트는 재로드값입니다.

period

11비트 타이머 0..2047입니다. 펄스 주파수는 CPU 클록/[16*(period+1)], 삼각파는 CPU 클록/[32*(period+1)]입니다.

length_index

길이 테이블 인덱스 0..31이며 프레임 수가 아닙니다. 하위 5비트만 레지스터에 전달됩니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 보조 함수는 단일 채널 APU_STATUS 마스크를 써서 다른 소리를 끕니다. 다중 음원 믹서가 아닙니다. 펄스·삼각파의 period는 타이머값이며 작을수록 음높이가 높습니다. 샘플 음높이와 길이는 NTSC 타이밍으로 확인했습니다.

사용 예

c
// NTSC: pulse A4 (~440 Hz), triangle A3 (~220 Hz), noise, then two short effects.
#include "fc_sound_example.h"
void main(void) {
    sound_begin("APU CHANNELS AND BLIPS");
    // Constant volume 12, length halt and 50% duty. Each helper replaces 4015.
    nes_sfx_square1(0xBC,253,0);
    sound_result[0]=(u8)(APU_STATUS&0x1F);
    sound_wait(30);
    nes_apu_channel_enable(0); sound_wait(15);
    nes_sfx_square2(0x7C,253,0);
    sound_result[1]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_triangle(0xFF,253,0);
    sound_result[2]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_noise(0x3C,5,0);
    sound_result[3]=(u8)(APU_STATUS&0x1F);
    sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_tick_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    nes_sfx_move_blip(); sound_wait(30); nes_apu_silence_all(); sound_wait(15);
    sound_end();
}

예상 결과

pulse1, pulse2, triangle, noise, tick, move 순서로 납니다. NTSC에서 타이머 253은 약 440 Hz 펄스와 220 Hz 삼각파를 만듭니다. tick은 약 111 Hz, move는 약 174 Hz이며 더 빨리 끝납니다. 마지막 구간은 무음입니다.

nes_sfx_triangle: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_channels.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-channels.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio.h:27

선언 주석 원문
Enable only triangle and load linear-counter, timer and length fields.

kitaqfc/lib/audio.c:83

c
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index)
{
    APU_STATUS = 0x04;
    TRI_LINEAR = linear;
    TRI_LO = (u8)period;
    TRI_HI = (u8)((u8)(length_index << 3) | (u8)((period >> 8) & 0x07));
}

audio_vblank.h — NMI로 구동하는 4채널 음악

4채널 음악은 NMI에서 진행됩니다. 초기화한 뒤 곡을 연결하고 큐를 미리 채우세요. 재생을 위해 NMI를 활성화하고 메인 코드에서 계속 보충하세요. NMI는 RAM 큐를 읽으므로, 보충이 제때 이루어지면 적은 RAM으로 긴 스트림을 재생할 수 있습니다. 각 채널에는 독립적인 HOLD/STOP 명령이 있습니다.

레코드는 delay, CH1, CH2, CH3, CH4의 5바이트로 구성됩니다. 지연값 1~255는 새 상태가 지속되는 NMI 틱 수입니다. 0은 재생을 종료하며 나머지 4바이트는 무시합니다. 음정값 0~71은 C2~B7에 해당합니다(24=C4). 노이즈값 0~15는 긴 모드의 주기를, 16~31은 짧은 모드를 선택합니다. 모든 채널에서 255=NES_AUDIO_HOLD는 상태를 유지하고 254=NES_AUDIO_STOP은 소리를 끕니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

지연이 끝났는데 다음 BGM 레코드가 없으면 BGM 음을 끄고, 데이터가 도착한 뒤 NMI에서 재개합니다. 별도로 재생 중인 SFX는 계속됩니다. 부족 횟수는 비어 있는 매 프레임이 아니라 데이터 부족 상태에 진입한 횟수입니다. HOLD 값만 삽입해도 이전 BGM 음이 복원되지는 않습니다. 평소에는 매 프레임 보충하고, 오래 걸리는 작업 전에는 충분한 재생 시간을 큐에 확보하세요.

대기 시간CH1: 펄스 1CH2: 펄스 2CH3: 삼각파CH4: 노이즈
2424 (C4)12 (C3)0 (C2)2

컴파일러가 연결하는 NMI 훅

nes_audio_vblank_init

APU와 큐를 초기화하고 재생, 공급 상태, 데이터 부족 카운터를 리셋합니다.

사용 형식

c
void nes_audio_vblank_init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

APU 프레임 IRQ, DMC, 펄스 스윕을 비활성화합니다. 기본 설정은 펄스 1이 듀티 50%·음량 12, 펄스 2가 듀티 25%·음량 10, 삼각파가 음표 재생 활성화, 노이즈가 음량 8입니다. 초기화만으로 음악이나 NMI가 시작되지는 않습니다.

사용 예

c
nes_audio_vblank_init();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_init: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:19

선언 주석 원문
Reset the queue, feeder and diagnostic counters; silence and initialize the
APU. Owns the four basic channels and disables DMC and APU frame IRQs.

kitaqfc/lib/audio_vblank.c:67

c
void nes_audio_vblank_init(void){
    nes_audio_vblank_stop();nav_underruns=0;nav_frame=0x40;
    nav_sweep1=8;nav_sweep2=8;nav_dmc_flags=0;nav_dmc_level=0;
    nav_timbre[0]=0xBC;nav_timbre[1]=0x7A;nav_timbre[2]=0xFF;nav_timbre[3]=0x38;
}
nes_audio_vblank_enqueue

레코드 하나를 검사하여 RAM 큐에 복사합니다. ROM 공급 기능 없이 게임 코드에서 음악을 생성할 때 사용하세요.

사용 형식

c
u8 nes_audio_vblank_enqueue(const u8* record);

매개변수

record

읽을 수 있는 5바이트의 주소이며 호출이 반환될 때까지 유효해야 합니다. 반환 후에는 원본 배열을 재사용할 수 있습니다. null은 거부됩니다.

반환값

받아들였으면 1을 반환합니다. 큐가 가득 찼거나 null 또는 유효하지 않은 음정·노이즈 값이면 큐를 바꾸지 않고 0을 반환합니다. 종료 레코드의 4개 채널 바이트는 검사하지 않습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

40바이트 RAM 링 버퍼에는 물리적으로 8개 슬롯이 있지만, 가득 찬 상태와 빈 상태를 구분하기 위해 한 슬롯을 비워 둡니다. 사용 가능한 용량은 7레코드입니다. 메인 코드의 단일 생산자만 사용하고 NMI에서 큐 삽입, 보충, 초기화를 하지 마세요. 생산자는 레코드 전체를 복사한 뒤에만 1바이트 쓰기 위치를 공개하므로 NMI가 불완전한 레코드를 읽을 수 없습니다.

레코드는 delay, CH1, CH2, CH3, CH4의 5바이트로 구성됩니다. 지연값 1~255는 새 상태가 지속되는 NMI 틱 수입니다. 0은 재생을 종료하며 나머지 4바이트는 무시합니다. 음정값 0~71은 C2~B7에 해당합니다(24=C4). 노이즈값 0~15는 긴 모드의 주기를, 16~31은 짧은 모드를 선택합니다. 모든 채널에서 255=NES_AUDIO_HOLD는 상태를 유지하고 254=NES_AUDIO_STOP은 소리를 끕니다.

사용 예

c
result[6]=nes_audio_vblank_enqueue(quiet_record); // Copy five bytes into RAM.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_enqueue: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:22

선언 주석 원문
Copy one complete validated record to RAM and publish it atomically last.
Return 1 on success, 0 for null/invalid/full. Call from one foreground producer.

kitaqfc/lib/audio_vblank.c:74

c
u8 nes_audio_vblank_enqueue(const u8* record){
    u8 next;u8 i;u8 value;
    nav_invalid=0;
    if(record==0){nav_invalid=1;return 0;}
    next=nav_head+5;if(next==40)next=0;
    if(next==nav_tail)return 0;
    if(record[0]!=0){
        for(i=1;i<5;i++){
            value=record[i];
            if(value<254){
                if(i<4){if(value>71){nav_invalid=1;return 0;}}
                else{if(value>31){nav_invalid=1;return 0;}}
            }
        }
    }
    for(i=0;i<5;i++)nav_queue[(__safe_index u8)(nav_head+i)]=record[i];
    nav_head=next;return 1;
}
nes_audio_vblank_queued

NMI가 아직 소비하지 않은 레코드 수를 반환합니다.

사용 형식

c
u8 nes_audio_vblank_queued(void);

반환값

큐에 들어 있는 BGM 레코드 수 0~7을 반환합니다. 현재 지연과 모든 SFX 레코드는 제외합니다. NMI가 데이터를 소비하므로 반환값은 조회 순간의 상태입니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

40바이트 RAM 링 버퍼에는 물리적으로 8개 슬롯이 있지만, 가득 찬 상태와 빈 상태를 구분하기 위해 한 슬롯을 비워 둡니다. 사용 가능한 용량은 7레코드입니다. 메인 코드의 단일 생산자만 사용하고 NMI에서 큐 삽입, 보충, 초기화를 하지 마세요. 생산자는 레코드 전체를 복사한 뒤에만 1바이트 쓰기 위치를 공개하므로 NMI가 불완전한 레코드를 읽을 수 없습니다.

사용 예

c
result[1]=nes_audio_vblank_queued(); // Seven records after prefill.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_queued: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:24

선언 주석 원문
Return queued records (0..7), excluding the record whose delay is in progress.

kitaqfc/lib/audio_vblank.c:94

c
u8 nes_audio_vblank_queued(void){
    u8 head;u8 tail;head=nav_head;tail=nav_tail;
    if(head<tail)head=head+40;
    return (head-tail)/5;
}
nes_audio_vblank_free

큐에서 레코드를 넣을 수 있는 빈 슬롯 수를 반환합니다.

사용 형식

c
u8 nes_audio_vblank_free(void);

반환값

BGM용 빈 슬롯 수 0~7을 반환하며, 7−queued()와 같습니다. 바이트 수가 아닌 슬롯 수이고 슬롯당 5바이트입니다. 별도의 35바이트 SFX 버퍼는 이 큐에 포함되지 않습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

40바이트 RAM 링 버퍼에는 물리적으로 8개 슬롯이 있지만, 가득 찬 상태와 빈 상태를 구분하기 위해 한 슬롯을 비워 둡니다. 사용 가능한 용량은 7레코드입니다. 메인 코드의 단일 생산자만 사용하고 NMI에서 큐 삽입, 보충, 초기화를 하지 마세요. 생산자는 레코드 전체를 복사한 뒤에만 1바이트 쓰기 위치를 공개하므로 NMI가 불완전한 레코드를 읽을 수 없습니다.

사용 예

c
result[2]=nes_audio_vblank_free();   // No free slots immediately after prefill.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_free: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:26

선언 주석 원문
Return available record slots (0..7), not bytes.

kitaqfc/lib/audio_vblank.c:99

c
u8 nes_audio_vblank_free(void){return 7-nes_audio_vblank_queued();}
nes_audio_vblank_start

RAM 큐의 소비를 활성화합니다. 다음 NMI에서 첫 레코드를 가져옵니다.

사용 형식

c
void nes_audio_vblank_start(void);

반환값

반환값은 없습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

먼저 초기화하고 레코드를 큐에 넣으세요. 이 호출은 NMI를 활성화하지 않으며 현재 지연이나 읽기 위치도 바꾸지 않습니다. 일시 정지한 드라이버는 pause(0)으로 재개하세요. start만 호출해도 일시 정지가 풀리지는 않습니다.

지연이 끝났는데 다음 BGM 레코드가 없으면 BGM 음을 끄고, 데이터가 도착한 뒤 NMI에서 재개합니다. 별도로 재생 중인 SFX는 계속됩니다. 부족 횟수는 비어 있는 매 프레임이 아니라 데이터 부족 상태에 진입한 횟수입니다. HOLD 값만 삽입해도 이전 BGM 음이 복원되지는 않습니다. 평소에는 매 프레임 보충하고, 오래 걸리는 작업 전에는 충분한 재생 시간을 큐에 확보하세요.

사용 예

c
nes_audio_vblank_start(); // Consume the copied record on the next NMI.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_start: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:28

선언 주석 원문
Enable consumption at the next NMI. Does not enable PPU NMI itself.

kitaqfc/lib/audio_vblank.c:100

c
void nes_audio_vblank_start(void){nav_active=1;}
nes_audio_vblank_stop

재생을 비활성화하고 4개 채널을 무음으로 만든 뒤, 큐와 ROM 공급 상태를 버립니다.

사용 형식

c
void nes_audio_vblank_stop(void);

반환값

반환값은 없습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

BGM과 SFX를 모두 멈추고 큐, 공급 상태, 두 재생 위치, 일시 정지 상태를 지웁니다. 음색과 데이터 부족 카운터는 유지하며 NMI는 비활성화하지 않습니다. 위치를 유지하려면 pause를 사용하세요. set_music과 play_music도 내부적으로 이 정지를 수행하므로 SFX가 취소됩니다.

사용 예

c
nes_audio_vblank_stop(); // Clear the feeder and queued records; silence now.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_stop: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:30

선언 주석 원문
Disable consumption, silence, discard queued records and clear the feeder.

kitaqfc/lib/audio_vblank.c:29

c
void nes_audio_vblank_stop(void){
    u8 i;
    nav_paused=1;nav_active=0;nav_sfx_active=0;nav_status=0;
    nav_delay=0;nav_enabled=0;nav_head=0;nav_tail=0;
    nav_source=0;nav_cursor=0;nav_count=0;nav_left=0;nav_starved=0;
    nav_previous_mask=0;nav_effective_mask=0;nav_refresh=0;
    for(i=0;i<4;i++){nav_notes[(__safe_index u8)i]=254;nav_sfx_notes[(__safe_index u8)i]=254;}
    nav_paused=0;
}
nes_audio_vblank_set_music

재생을 멈추고 스트림을 공급 원본으로 연결합니다. 큐에 복사하는 작업과 재생을 시작하는 작업은 별도입니다.

사용 형식

c
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop);

매개변수

records

읽을 수 있는 ROM 또는 RAM 스트림의 시작 주소입니다. 공급이 끝날 때까지 데이터를 유지하세요. 뱅크 전환은 수행하지 않으므로 refill을 호출하는 동안 적절한 ROM 뱅크를 매핑해야 합니다. NMI는 RAM에 복사된 레코드만 읽습니다.

count

읽을 수 있는 5바이트 레코드의 개수로, 범위는 1~13107입니다. 바이트 수가 아닙니다. count×5바이트 전체가 연속된 유효 CPU 주소 범위에 있어야 합니다.

loop

0은 한 번 재생하고, 0이 아닌 값은 처음부터 반복합니다. 반복 스트림 안에는 지연값이 0인 종료 레코드를 넣지 마세요.

반환값

유효한 포인터와 개수이면 1을, null이거나 개수가 0 또는 13107 초과이면 0을 반환합니다. 실패해도 기존 재생은 멈추고 큐 데이터는 버립니다. 레코드 내용은 보충 시에 검사합니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

레코드는 delay, CH1, CH2, CH3, CH4의 5바이트로 구성됩니다. 지연값 1~255는 새 상태가 지속되는 NMI 틱 수입니다. 0은 재생을 종료하며 나머지 4바이트는 무시합니다. 음정값 0~71은 C2~B7에 해당합니다(24=C4). 노이즈값 0~15는 긴 모드의 주기를, 16~31은 짧은 모드를 선택합니다. 모든 채널에서 255=NES_AUDIO_HOLD는 상태를 유지하고 254=NES_AUDIO_STOP은 소리를 끕니다.

사용 예

c
result[5]=nes_audio_vblank_set_music(song,8,0); // Attach without starting.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_set_music: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:33

선언 주석 원문
Stop and attach a readable five-byte-record stream; return 0 for null, zero
count or a count above 13107. Nonzero loop repeats the stream. Does not start.

kitaqfc/lib/audio_vblank.c:110

c
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop){
    nes_audio_vblank_stop();
    if(records==0||count==0||count>13107)return 0;
    nav_source=records;nav_cursor=records;nav_count=count;nav_left=count;nav_loop=loop;
    return 1;
}
nes_audio_vblank_refill

연결된 스트림에서 최대 7개 레코드를 빈 슬롯에 공급합니다. 메인 코드에서 수행하는 이 작업으로 작은 RAM 큐로도 긴 곡을 재생할 수 있습니다.

사용 형식

c
u8 nes_audio_vblank_refill(void);

반환값

받아들인 레코드 수 0~7을 반환하며, 한 번 재생할 때 자동으로 추가되는 종료 레코드도 포함합니다. 원본이 없거나 큐가 가득 차면 0을 반환합니다. 잘못된 레코드를 만나면 재생을 멈추고 큐와 공급 상태를 버린 뒤 0을 반환합니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

읽을 수 있는 ROM 또는 RAM 스트림의 시작 주소입니다. 공급이 끝날 때까지 데이터를 유지하세요. 뱅크 전환은 수행하지 않으므로 refill을 호출하는 동안 적절한 ROM 뱅크를 매핑해야 합니다. NMI는 RAM에 복사된 레코드만 읽습니다.

0은 한 번 재생하고, 0이 아닌 값은 처음부터 반복합니다. 반복 스트림 안에는 지연값이 0인 종료 레코드를 넣지 마세요.

지연이 끝났는데 다음 BGM 레코드가 없으면 BGM 음을 끄고, 데이터가 도착한 뒤 NMI에서 재개합니다. 별도로 재생 중인 SFX는 계속됩니다. 부족 횟수는 비어 있는 매 프레임이 아니라 데이터 부족 상태에 진입한 횟수입니다. HOLD 값만 삽입해도 이전 BGM 음이 복원되지는 않습니다. 평소에는 매 프레임 보충하고, 오래 걸리는 작업 전에는 충분한 재생 시간을 큐에 확보하세요.

ROM에서 공급하는 데이터와 직접 삽입하는 데이터는 같은 큐를 사용합니다. 순서를 의도적으로 관리할 때만 함께 사용하세요. 보충 자체로 레코드가 재생되거나 소비 측이 시작되지는 않습니다. 다만 잘못된 데이터로 정지하면 APU는 무음이 됩니다.

사용 예

c
nes_audio_vblank_refill();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_refill: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:37

선언 주석 원문
Copy at most seven records from the attached stream. Return copies accepted.
A one-shot feeder appends a delay-zero terminator after the last timed record.
Keep the stream's ROM bank mapped during this foreground call only.

kitaqfc/lib/audio_vblank.c:118

c
u8 nes_audio_vblank_refill(void){
    u8 copied;copied=0;
    while(copied<7&&nav_source!=0){
        if(nav_left==0){
            if(nav_loop!=0){nav_cursor=nav_source;nav_left=nav_count;}
            else {if(nes_audio_vblank_enqueue(nav_end)!=0){copied++;nav_source=0;}return copied;}
        }
        if(nes_audio_vblank_enqueue(nav_cursor)==0){
            // The producer-only flag distinguishes malformed data from full.
            // Re-reading occupancy here would race NMI freeing a full queue.
            if(nav_invalid!=0){nes_audio_vblank_stop();return 0;}
            return copied;
        }
        copied++;nav_cursor=nav_cursor+5;nav_left--;
    }
    return copied;
}
nes_audio_vblank_play_music

한 번의 호출로 곡을 연결하고 큐를 미리 채우며 재생을 활성화합니다. 악구를 시작할 때 일반적으로 사용하는 진입점입니다.

사용 형식

c
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop);

매개변수

records

읽을 수 있는 ROM 또는 RAM 스트림의 시작 주소입니다. 공급이 끝날 때까지 데이터를 유지하세요. 뱅크 전환은 수행하지 않으므로 refill을 호출하는 동안 적절한 ROM 뱅크를 매핑해야 합니다. NMI는 RAM에 복사된 레코드만 읽습니다.

count

읽을 수 있는 5바이트 레코드의 개수로, 범위는 1~13107입니다. 바이트 수가 아닙니다. count×5바이트 전체가 연속된 유효 CPU 주소 범위에 있어야 합니다.

loop

0은 한 번 재생하고, 0이 아닌 값은 처음부터 반복합니다. 반복 스트림 안에는 지연값이 0인 종료 레코드를 넣지 마세요.

반환값

설정과 최초 보충이 성공하면 1을 반환합니다. 설정이 잘못되었거나 최초 보충 중 잘못된 데이터를 만나면 0을 반환합니다. 성공했다고 곡 전체가 검증된 것은 아닙니다. 뒤쪽의 잘못된 레코드는 이후 refill 호출에서 발견됩니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

먼저 init을 한 번 호출하세요. play_music은 음색과 데이터 부족 카운터를 유지합니다. 길거나 반복되는 스트림은 이후에도 주기적으로 refill을 호출해야 합니다.

사용 예

c
result[0]=nes_audio_vblank_play_music(song,8,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_play_music: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:39

선언 주석 원문
Set the stream, prefill the queue and start. Return 0 for invalid setup/data.

kitaqfc/lib/audio_vblank.c:135

c
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop){
    if(nes_audio_vblank_set_music(records,count,loop)==0)return 0;
    if(nes_audio_vblank_refill()==0)return 0;
    nes_audio_vblank_start();return 1;
}
nes_audio_vblank_is_playing

NMI에 의한 큐 소비가 활성화되어 있는지 알려 줍니다.

사용 형식

c
u8 nes_audio_vblank_is_playing(void);

반환값

BGM 소비 측이 활성화되어 있으면 일시 정지나 데이터 부족 중에도 1을 반환하고, 정지 또는 종료 후에는 0을 반환합니다. SFX만 재생 중일 때도 0입니다. 실제로 소리가 나는지나 SFX가 동작 중인지를 나타내지는 않습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

지연이 끝났는데 다음 BGM 레코드가 없으면 BGM 음을 끄고, 데이터가 도착한 뒤 NMI에서 재개합니다. 별도로 재생 중인 SFX는 계속됩니다. 부족 횟수는 비어 있는 매 프레임이 아니라 데이터 부족 상태에 진입한 횟수입니다. HOLD 값만 삽입해도 이전 BGM 음이 복원되지는 않습니다. 평소에는 매 프레임 보충하고, 오래 걸리는 작업 전에는 충분한 재생 시간을 큐에 확보하세요.

사용 예

c
while(nes_audio_vblank_is_playing()!=0){
        // example:nes_audio_vblank_refill:start
        nes_audio_vblank_refill();
        // example:nes_audio_vblank_refill:end
        __nmi_wait();
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_is_playing: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:41

선언 주석 원문
Return 1 while the BGM consumer is enabled, including a recoverable queue underrun.

kitaqfc/lib/audio_vblank.c:101

c
u8 nes_audio_vblank_is_playing(void){return nav_active;}
nes_audio_vblank_underruns

큐 데이터 부족이 발생한 횟수를 반환하여 보충 시간의 여유를 판단하는 데 도움을 줍니다.

사용 형식

c
u8 nes_audio_vblank_underruns(void);

반환값

0~255를 반환하며 255에서 포화됩니다. 대기 중인 매 프레임이 아니라 데이터 부족이 발생한 횟수를 셉니다. init은 값을 지우지만 stop과 곡 변경은 유지합니다. 정상 종료 레코드는 데이터 부족으로 세지 않습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

지연이 끝났는데 다음 BGM 레코드가 없으면 BGM 음을 끄고, 데이터가 도착한 뒤 NMI에서 재개합니다. 별도로 재생 중인 SFX는 계속됩니다. 부족 횟수는 비어 있는 매 프레임이 아니라 데이터 부족 상태에 진입한 횟수입니다. HOLD 값만 삽입해도 이전 BGM 음이 복원되지는 않습니다. 평소에는 매 프레임 보충하고, 오래 걸리는 작업 전에는 충분한 재생 시간을 큐에 확보하세요.

사용 예

c
result[4]=nes_audio_vblank_underruns(); // Zero: queued time covered the busy section.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 독자적으로 만든 4채널 악구를 한 번 재생합니다. 메인 코드가 90프레임 동안 큐를 보충하지 않고 정수 연산을 수행해도 음악은 계속됩니다. “PHRASE COMPLETE / SILENT”와 “UNDERRUNS 0 / MATH OK”는 재생이 끝났고 큐 데이터 부족이 없었으며 연산 결과가 일치했음을 뜻합니다. 녹음된 오디오에서 펄스파 멜로디와 반주, 삼각파 베이스, 노이즈를 들어 보세요. 끝부분은 무음입니다. 이미지는 제어 결과를 보여 주며 소리는 오디오로 확인해야 합니다.

nes_audio_vblank_underruns: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:43

선언 주석 원문
Saturating count of starvation episodes, cleared by init; END is not starvation.

kitaqfc/lib/audio_vblank.c:102

c
u8 nes_audio_vblank_underruns(void){return nav_underruns;}
nes_audio_vblank_set_timbre

현재 음을 다시 시작하지 않고 해당 채널의 다음 음에 적용할 듀티나 레벨을 설정합니다.

사용 형식

c
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control);

매개변수

channel

0=CH1 펄스 1, 1=CH2 펄스 2, 2=CH3 삼각파, 3=CH4 노이즈입니다. 4 이상의 값은 거부됩니다.

control

펄스 채널에서는 비트 7~6이 듀티를 선택하고(0=12.5%, 1=25%, 2=50%, 3=75%), 비트 3~0이 고정 음량 0~15를 설정합니다. 노이즈도 보통 비트 3~0을 고정 음량으로 사용합니다. NES_AUDIO_NOISE_ENVELOPE(128)를 OR하면 이 비트가 감쇠 주기 0~15가 되며, 다음 노이즈 음은 음량 15에서 시작해 한 번 감쇠합니다. 주기가 클수록 느리고 15이면 약 1초가 걸립니다. 노이즈 길이 카운터는 감쇠가 끝날 수 있도록 설정됩니다. 삼각파는 0이면 다음 음을 무음으로, 0이 아니면 소리가 나도록 설정합니다.

반환값

유효한 채널이면 1을 반환합니다. 4 이상의 값은 아무것도 바꾸지 않고 0을 반환합니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

사용 예

c
nes_audio_vblank_set_timbre(3,NES_AUDIO_NOISE_ENVELOPE|15);
    result[3]=nes_audio_vblank_play_sfx(noise,1,8); // Bit 3 owns CH4; no BGM needed.
    // The slow one-shot envelope fades by itself. It does not loop at zero.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

입력은 필요 없습니다. CH1은 약 220Hz의 BGM을 재생하다가 30프레임 동안 약 880Hz의 SFX로 전환한 뒤, 그 시점의 BGM으로 돌아갑니다. 30프레임의 무음 일시 정지 후 BGM을 재개합니다. 두 번째 효과음은 15프레임 뒤 명시적으로 중단합니다. 마지막에는 CH4 노이즈가 천천히 한 번 감쇠합니다. COMPLETE / ALL CHANNELS OFF가 종료를 나타냅니다. 첨부된 녹음에서 음높이 변화, 무음 일시 정지, 노이즈 감쇠를 확인하세요.

nes_audio_vblank_set_timbre: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:50

선언 주석 원문
Configure the next note: pulse bits 7..6 select duty and 3..0 select volume;
noise uses volume 3..0, or NES_AUDIO_NOISE_ENVELOPE | decay_period (0..15)
for a one-shot hardware envelope (larger periods decay more slowly). The
next noise note starts at volume 15; its length permits a full decay.
Triangle uses zero for silent, nonzero for full level.
Return 0 for channel >=4. Hardware has no triangle volume control.

kitaqfc/lib/audio_vblank.c:103

c
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control){
    if(channel>=4)return 0;
    if(channel<2)nav_timbre[(__safe_index u8)channel]=(control&0xCF)|0x30;
    else if(channel==2){if(control==0)nav_timbre[2]=0x80;else nav_timbre[2]=0xFF;}
    else {if((control&128)!=0)nav_timbre[3]=control&15;else nav_timbre[3]=(control&15)|0x30;}
    return 1;
}
nes_audio_vblank_pause

재생 위치를 유지하면서 BGM과 SFX를 함께 일시 정지하거나 재개합니다.

사용 형식

c
void nes_audio_vblank_pause(u8 on);

매개변수

on

0이 아니면 일시 정지하고 0이면 재개합니다. 일시 정지하면 4개 APU 채널이 즉시 비활성화됩니다.

반환값

반환값은 없습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

메인 코드에서 호출하세요. NMI는 복사된 SFX 데이터를 소비합니다. BGM과 SFX 모두 이 라이브러리로 관리하고 다른 APU 드라이버를 동시에 실행하지 마세요.

일시 정지 중에는 BGM의 지연·큐 위치와 SFX의 지연·위치가 모두 진행되지 않습니다. 메인 코드에서는 계속 보충할 수 있습니다. 재개 후 첫 NMI에서 현재 음을 다시 시작하고 정상적인 틱 처리를 진행합니다. 파형 위상과 감쇠 중이던 엔벌로프 레벨은 저장되지 않습니다. is_playing이 보고하는 BGM 활성 플래그는 바뀌지 않습니다. 이미 재생 중이어도 0을 전달하면 음 복원을 요청하므로 매 프레임 호출하지 마세요.

사용 예

c
nes_audio_vblank_pause(1); // Freeze BGM and SFX, and mute immediately.
    phase=3;wait_frames(30);   // Refill may copy data; neither timeline advances.
    nes_audio_vblank_pause(0); // Restore held notes on the next NMI.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

입력은 필요 없습니다. CH1은 약 220Hz의 BGM을 재생하다가 30프레임 동안 약 880Hz의 SFX로 전환한 뒤, 그 시점의 BGM으로 돌아갑니다. 30프레임의 무음 일시 정지 후 BGM을 재개합니다. 두 번째 효과음은 15프레임 뒤 명시적으로 중단합니다. 마지막에는 CH4 노이즈가 천천히 한 번 감쇠합니다. COMPLETE / ALL CHANNELS OFF가 종료를 나타냅니다. 첨부된 녹음에서 음높이 변화, 무음 일시 정지, 노이즈 감쇠를 확인하세요.

nes_audio_vblank_pause: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:54

선언 주석 원문
Any nonzero argument freezes both timelines and mutes immediately. Zero
resumes from the stored delays and restores held notes on the next NMI.
BGM is_playing remains enabled during pause. Refill may still fill the queue.

kitaqfc/lib/audio_vblank.c:40

c
void nes_audio_vblank_pause(u8 on){
    if(on!=0){nav_paused=1;nav_status=0;}
    else{nav_refresh=1;nav_paused=0;}
}
nes_audio_vblank_play_sfx

짧은 효과음을 RAM에 복사하고 다음 NMI부터 선택한 채널의 출력을 일시적으로 사용합니다.

사용 형식

c
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask);

매개변수

records

5바이트 레코드들의 시작 주소입니다. 호출이 반환될 때까지 count×5바이트를 읽을 수 있고 올바르게 매핑된 상태로 유지하세요. 복사 후에는 원본을 재사용할 수 있습니다.

count

1~7레코드, 최대 35바이트입니다. 모든 지연값은 1~255여야 합니다. SFX는 지연값이 0인 종료 레코드를 사용하지 않으며, 지정된 레코드를 재생한 뒤 끝납니다.

mask

하위 4비트로 채널을 선택합니다. 1=CH1, 2=CH2, 4=CH3, 8=CH4입니다. 여러 채널은 비트를 OR하여 선택하세요. 상위 비트는 무시하며 하위 마스크가 0이면 거부됩니다.

반환값

효과음을 복사하고 활성화하면 1을 반환합니다. null, 잘못된 개수, 선택된 채널 없음, 지연값 0, 잘못된 음정·노이즈 값이면 0을 반환합니다. 잘못된 입력은 현재 효과음을 바꾸지 않습니다. 선택하지 않은 채널의 값도 검사합니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

메인 코드에서 호출하세요. NMI는 복사된 SFX 데이터를 소비합니다. BGM과 SFX 모두 이 라이브러리로 관리하고 다른 APU 드라이버를 동시에 실행하지 마세요.

그동안 BGM도 계속 진행하며, 효과음이 끝나면 출력이 그 시점의 BGM 음으로 돌아갑니다. SFX 스트림은 하나이며 새 호출로 교체됩니다. HOLD/STOP 값은 BGM과 같지만 효과음의 유지 상태는 정지된 음으로 시작하므로 첫 HOLD가 BGM 음을 이어받지는 않습니다. BGM 없이 SFX만 재생할 수도 있습니다. 일시 정지 중에 등록한 효과음은 재개를 기다립니다. 음색은 BGM과 공유합니다.

사용 예

c
result[1]=nes_audio_vblank_play_sfx(effect,1,1); // Mask bit 0 owns CH1.
    // The BGM timeline keeps advancing; its current CH1 note returns after 30 ticks.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

입력은 필요 없습니다. CH1은 약 220Hz의 BGM을 재생하다가 30프레임 동안 약 880Hz의 SFX로 전환한 뒤, 그 시점의 BGM으로 돌아갑니다. 30프레임의 무음 일시 정지 후 BGM을 재개합니다. 두 번째 효과음은 15프레임 뒤 명시적으로 중단합니다. 마지막에는 CH4 노이즈가 천천히 한 번 감쇠합니다. COMPLETE / ALL CHANNELS OFF가 종료를 나타냅니다. 첨부된 녹음에서 음높이 변화, 무음 일시 정지, 노이즈 감쇠를 확인하세요.

nes_audio_vblank_play_sfx: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:62

선언 주석 원문
Copy 1..7 timed records. Bits 0..3 select channels temporarily owned by SFX.
BGM continues underneath and its current notes return when the effect ends.
Return 1 after copying; 0 for null, invalid count/data or an empty low mask.
Ignore mask bits 4..7. All fields are validated, even unselected channels.
Delay zero is not accepted for SFX. Invalid input preserves the current SFX.
Replacement restarts the effect; the first HOLD starts from a stopped note.
The source need only stay mapped/readable until this foreground call returns.

kitaqfc/lib/audio_vblank.c:47

c
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask){
    u8 i;u8 ch;u8 value;u8 bytes;
    if(records==0||count==0||count>7||(mask&15)==0)return 0;
    bytes=(u8)(count+count+count+count+count);
    i=0;
    while(i<bytes){
        if(records[i]==0)return 0;
        ch=1;while(ch<5){
            value=records[i+ch];
            if(value<254){if(ch<4){if(value>71)return 0;}else{if(value>31)return 0;}}
            ch++;
        }
        i=i+5;
    }
    nav_sfx_active=0;
    for(i=0;i<bytes;i++)nav_sfx_queue[(__safe_index u8)i]=records[i];
    for(i=0;i<4;i++)nav_sfx_notes[(__safe_index u8)i]=254;
    nav_sfx_mask=mask&15;nav_sfx_cursor=0;nav_sfx_length=bytes;nav_sfx_delay=0;
    nav_sfx_active=1;return 1;
}
nes_audio_vblank_stop_sfx

효과음의 채널 사용을 해제하여 다음 NMI에서 현재 BGM 음을 복원하도록 합니다.

사용 형식

c
void nes_audio_vblank_stop_sfx(void);

반환값

반환값은 없습니다.

사용 조건과 제한

audio_vblank.h를 포함하고 audio_vblank.c를 빌드에 정확히 한 번 추가하세요. 컴파일러는 기본 NMI 핸들러든 사용자 정의 NMI 핸들러든 훅을 한 번 호출하도록 합니다. PPU의 NMI를 활성화하세요. 메인 루프에서는 음악 갱신 루틴을 호출하지 않습니다.

이 4채널 드라이버는 NTSC에 맞춰져 있습니다. CH1=펄스 1, CH2=펄스 2, CH3=삼각파, CH4=노이즈이며 인덱스는 0~3입니다. DMC나 확장 음원은 재생하지 않습니다. audio.c의 효과음처럼 APU에 직접 쓰는 코드와 동시에 실행하지 마세요. PAL용 음정표나 템포 보정은 없습니다.

메인 코드에서 호출하세요. NMI는 복사된 SFX 데이터를 소비합니다. BGM과 SFX 모두 이 라이브러리로 관리하고 다른 APU 드라이버를 동시에 실행하지 마세요.

이 호출은 APU 레지스터에 쓰지 않습니다. BGM 큐, 위치, 활성 상태를 유지합니다. SFX만 재생 중이었다면 다음 NMI에서 채널이 정지된 음으로 돌아갑니다. 일시 정지 중에는 재개할 때 반영됩니다. 즉시 무음으로 만들고 두 재생 위치를 모두 버리려면 stop을 사용하세요.

사용 예

c
nes_audio_vblank_stop_sfx(); // Release effect ownership at the next NMI.
    // BGM continues at its current position; this does not restart the song.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

입력은 필요 없습니다. CH1은 약 220Hz의 BGM을 재생하다가 30프레임 동안 약 880Hz의 SFX로 전환한 뒤, 그 시점의 BGM으로 돌아갑니다. 30프레임의 무음 일시 정지 후 BGM을 재개합니다. 두 번째 효과음은 15프레임 뒤 명시적으로 중단합니다. 마지막에는 CH4 노이즈가 천천히 한 번 감쇠합니다. COMPLETE / ALL CHANNELS OFF가 종료를 나타냅니다. 첨부된 녹음에서 음높이 변화, 무음 일시 정지, 노이즈 감쇠를 확인하세요.

nes_audio_vblank_stop_sfx: NROM
NROM
NROM — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\audio_vblank.c .\kitaq-docs\samples\api-examples\fc\audio_vblank_effects.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr -o .\out\audio_vblank_effects.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/audio_vblank.h:65

선언 주석 원문
Release effect ownership at the next NMI, restoring current BGM notes.
This does not stop or restart the BGM and does not write the APU immediately.

kitaqfc/lib/audio_vblank.c:44

c
void nes_audio_vblank_stop_sfx(void){nav_sfx_active=0;}

bank.h — 뱅크를 고려한 코드·데이터 접근

ROM 뱅크는 카트리지 저장 공간을 CPU 주소 공간에 매핑하는 단위입니다. 서로 다른 뱅크가 같은 CPU 주소에 나타날 수 있으므로 16비트 포인터만으로는 뱅크가 있는 데이터를 식별할 수 없습니다. BankPtr은 1바이트 뱅크 번호와 2바이트 포인터를 담은 패딩 없는 3바이트 설명자입니다. farptr_make로 만들고 farptr_read8, farptr_read16 또는 farptr_read로 데이터에 접근합니다.

FC 예제는 MMC3의 논리 뱅크를 사용합니다. 뱅크 0의 공통 코드는 $C000–$FFFF에 있고, 선택한 16 KiB 뱅크는 $8000–$BFFF에 나타납니다. 이 번호는 컴파일러의 뱅크 번호이며 매퍼의 8 KiB 단위 레지스터 값과 다릅니다. bank_switch로 라이브러리 기록을 초기화하세요. FC의 far_call은 함수 이름으로 호출하는 매크로입니다. 함수의 뱅크를 전달하며, 번호는 __bankof로 구하는 것이 명확합니다.

bank_get_current

bank_switch가 마지막으로 기록한 뱅크 번호를 반환합니다. 카트리지 레지스터가 아니라 소프트웨어의 기록을 읽습니다.

사용 형식

c
u8 bank_get_current(void);

반환값

bank_switch가 마지막으로 저장한 u8 값입니다. 초기화 전에는 0입니다.

사용 조건과 제한

라이브러리 기록은 0으로 시작하며 카트리지의 시작 매핑을 알아내지 않습니다. bank_switch로 초기화하세요. 내장 함수나 매퍼에 대한 직접 쓰기는 이 별도 기록을 갱신하지 않으므로 bank_get_current로 실제 하드웨어 매핑을 검증할 수는 없습니다.

사용 예

c
starting_bank=bank_get_current(); // Last value recorded by bank_switch.
bank_expect(starting_bank==2,1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

처음 bank_switch(2)는 뱅크 2를 기록하고 선택합니다. 이후 직접 전환으로 뱅크 1을 선택해 home1에서 A1을 읽어도 bank_get_current는 여전히 2입니다. 복귀하면 home2에서 다시 B2를 읽습니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

bank_get_current: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:18

선언 주석 원문
Read this wrapper's bank shadow; arbitrary mapper writes do not update it.

kitaqfc/lib/bank.c:14

c
u8 bank_get_current(void)
{
    return kq_bank_current;
}
bank_switch

요청한 뱅크 번호를 라이브러리에 기록한 뒤, 컴파일러의 매퍼별 PRG 뱅크 전환 처리로 선택합니다.

사용 형식

c
void bank_switch(u8 bank);

매개변수

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

반환값

없음(void).

사용 조건과 제한

지속되는 뱅크 변경으로 실행 중인 코드나 반환 주소가 사라져서는 안 됩니다. 예제처럼 호출 위치, 사용하는 bank_switch 본체, 이후 실행할 코드를 공통·고정 뱅크 0에 두세요.

라이브러리 기록은 0으로 시작하며 카트리지의 시작 매핑을 알아내지 않습니다. bank_switch로 초기화하세요. 내장 함수나 매퍼에 대한 직접 쓰기는 이 별도 기록을 갱신하지 않으므로 bank_get_current로 실제 하드웨어 매핑을 검증할 수는 없습니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
bank_switch(2); // Select the payload window and update library bookkeeping.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

처음 bank_switch(2)는 뱅크 2를 기록하고 선택합니다. 이후 직접 전환으로 뱅크 1을 선택해 home1에서 A1을 읽어도 bank_get_current는 여전히 2입니다. 복귀하면 home2에서 다시 B2를 읽습니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

bank_switch: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:16

선언 주석 원문
Record the bank in the library shadow and invoke the mapper-aware compiler intrinsic.

kitaqfc/lib/bank.c:7

c
void bank_switch(u8 bank)
{
    kq_bank_current = bank;
    __bankswitch(bank);
}
far_call

__farcall((bank), func)로 확장되는 매크로입니다. 컴파일러가 함수 이름으로 처리할 수 있도록 대상을 전달하며, 콜백 포인터를 통한 간접 호출은 아닙니다.

사용 형식

c
#define far_call(bank, func) __farcall((bank), func)

매개변수

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

func

선언된 매개변수 없는 함수의 이름입니다. 이 이름 기반 호출 내장 함수에는 함수 주소를 담은 변수를 전달할 수 없습니다. bank 인수는 콜백 매개변수로 전달되지 않습니다.

반환값

반환형은 호출한 함수에 따릅니다. void 콜백은 값을 반환하지 않고 u8·u16 콜백은 해당 값을 반환합니다. 화면 예제는 void 콜백을 사용합니다.

사용 조건과 제한

이 호출은 사용자 인수를 전달하거나 콜백의 인터럽트 재진입성을 보장하지 않습니다. 콜백 자체의 데이터, 스택, 인터럽트 사용을 올바르게 관리하세요. 뱅크 복귀는 중첩 및 반복 호출로 검사했지만 실제 하드웨어와 임의의 인터럽트 타이밍은 이 예제의 검증 범위가 아닙니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
far_call(3,callback); // No arguments; callback reads payload[0] in bank 3.
bank_expect(callback_count==1 && callback_byte==0xD3,15);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램의 이 지점에서 far_call는 callback_count를 1으로 만듭니다. 콜백은 뱅크 3에서 D3을 읽습니다. 이후 home2를 근거리로 읽어 B2인지 검사함으로써 뱅크 2가 매핑된 상태로 복귀했는지 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

far_call: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:36

far_data_read

원거리 메모리 복사 내장 함수를 사용해 지정한 ROM 뱅크에서 호출자가 제공한 RAM으로 len바이트를 복사합니다. 반환 전에 이전 뱅크를 복원합니다.

사용 형식

c
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);

매개변수

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

addr

선택한 ROM 뱅크에서 읽을 수 있는 바이트열을 가리키는 근거리 포인터입니다. 포인터 자체에는 뱅크 번호가 없습니다.

dst

쓰기 대상 메모리입니다. 실제 복사량을 모두 담을 공간을 확보하세요. 0바이트 전송을 제외하면 널 포인터나 잘못된 대상 주소를 검사하지 않습니다.

len

바이트 수이며 범위는 0~65535입니다. 0이면 메모리를 복사하지 않지만 뱅크 선택과 복구 과정은 실행합니다.

반환값

없음(void).

사용 조건과 제한

원본 전체가 의도한 ROM 매핑에서 읽히고 대상 전체가 쓰기 가능한 메모리 안에 있어야 합니다. 뱅크 창 경계에서 다음 뱅크로 넘어가거나, memmove처럼 겹치는 범위를 처리하거나, VRAM 쓰기 시점을 제어하지 않습니다. VRAM에는 그래픽 전송 API를 사용하세요.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
far_data_read(3,payload,buffer,4); // Copy into four writable RAM bytes.
bank_expect(buffer[0]==0xD3 && buffer[1]==0x7A && buffer[2]==0x5C && buffer[3]==0x96,7);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 far_data_read로 4바이트를 복사하고 버퍼를 D3 7A 5C 96와 비교합니다. 호출자의 원본 설명자나 포인터는 유지됩니다. 완성 프로그램은 길이 0 복사와 뱅크 복원도 별도로 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

far_data_read: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:24

선언 주석 원문
Copy len bytes from banked ROM to caller-owned writable storage.

kitaqfc/lib/bank.c:32

c
void far_data_read(u8 bank, const void* addr, void* dst, u16 len)
{
    __far_memcpy((u8*)dst, bank, (u16)addr, len);
}
far_data_read16

__farpeek16으로 연속된 두 바이트를 읽어 리틀 엔디언 워드로 반환합니다. 첫 번째 바이트가 하위 8비트입니다.

사용 형식

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

매개변수

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

addr

선택한 ROM 뱅크에서 읽을 수 있는 바이트열을 가리키는 근거리 포인터입니다. 포인터 자체에는 뱅크 번호가 없습니다.

반환값

읽은 리틀 엔디언 워드를 u16(0~65535)으로 반환합니다.

사용 조건과 제한

선택한 CPU 매핑에서 바이트 또는 워드 전체를 읽을 수 있어야 합니다. 포인터 검사, 주소 잘라내기, 창 경계에서 다음 ROM 뱅크로의 자동 전환은 수행하지 않습니다. 워드 읽기에는 연속된 유효한 두 바이트가 필요합니다.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
word_value=far_data_read16(3,payload+1); // Little-endian 7A 5C -> 0x5C7A.
bank_expect(word_value==0x5C7A,6);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 far_data_read16로 뱅크 3의 payload+1를 읽고 5C7A를 확인합니다. 완성 프로그램은 home2의 근거리 읽기로 뱅크 2 매핑이 복원되었는지도 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

far_data_read16: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:22

선언 주석 원문
Read a 16-bit value through the bank-aware compiler intrinsic.

kitaqfc/lib/bank.c:26

c
u16 far_data_read16(u8 bank, const void* addr)
{
    return __farpeek16(bank, (u16)addr);
}
far_data_read8

지정한 ROM 뱅크와 주소에서 __farpeek8로 한 바이트를 읽고, 이전 뱅크를 복원한 뒤 값을 반환합니다.

사용 형식

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

매개변수

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

addr

선택한 ROM 뱅크에서 읽을 수 있는 바이트열을 가리키는 근거리 포인터입니다. 포인터 자체에는 뱅크 번호가 없습니다.

반환값

읽은 바이트를 u8(0~255)로 반환합니다.

사용 조건과 제한

선택한 CPU 매핑에서 바이트 또는 워드 전체를 읽을 수 있어야 합니다. 포인터 검사, 주소 잘라내기, 창 경계에서 다음 ROM 뱅크로의 자동 전환은 수행하지 않습니다. 워드 읽기에는 연속된 유효한 두 바이트가 필요합니다.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
byte_value=far_data_read8(3,payload); // One byte: 0xD3.
bank_expect(byte_value==0xD3,5);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 far_data_read8로 뱅크 3의 payload를 읽고 D3를 확인합니다. 완성 프로그램은 home2의 근거리 읽기로 뱅크 2 매핑이 복원되었는지도 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

far_data_read8: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:20

선언 주석 원문
Read one byte from an explicit ROM bank and 16-bit CPU address.

kitaqfc/lib/bank.c:20

c
u8 far_data_read8(u8 bank, const void* addr)
{
    return __farpeek8(bank, (u16)addr);
}
farptr_make

뱅크 번호 한 바이트와 CPU 주소 공간의 포인터를 호출자의 BankPtr에 저장합니다. 설명자를 만들 뿐, 뱅크를 전환하거나 가리키는 데이터를 읽지 않습니다.

사용 형식

c
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);

매개변수

out

3바이트 BankPtr 하나를 저장할 수 있는 쓰기 가능한 영역의 포인터입니다. out이 널이면 아무것도 쓰지 않습니다.

bank

부호 없는 8비트 ROM 뱅크 번호입니다. 호출 시에는 함수 배치와 일치해야 하고, 읽기 시에는 원본 매핑을 지정합니다. 대상 카트리지에서 유효한 범위를 사용하세요.

ptr

설명자에 저장할 CPU 주소 공간의 16비트 주소입니다. 그대로 저장하며 선택한 뱅크에서 유효한 주소인지 검사하지 않습니다.

반환값

없음(void).

사용 예

c
farptr_make(&pointer,3,payload+1); // Store a bank and address; do not read ROM.
bank_expect(pointer.bank==3 && pointer.ptr==payload+1,8);
farptr_make(0,1,home1); // A null destination is ignored.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

설명자를 뱅크 3과 payload+1로 설정합니다. 읽기 함수들은 7A, 5C7A, 세 바이트 7A 5C 96을 얻습니다. 출력 포인터가 널인 호출은 상태를 바꾸지 않아야 합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

farptr_make: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:26

선언 주석 원문
Store a bank/address pair without reading memory; null output is ignored.

kitaqfc/lib/bank.c:38

c
void farptr_make(BankPtr* out, u8 bank, const u8* ptr)
{
    if (out == 0) return;
    out->bank = bank;
    out->ptr = ptr;
}
farptr_read

BankPtr이 가리키는 곳에서 쓰기 가능한 RAM으로 len바이트를 복사합니다. 설명자는 값으로 전달되므로 호출자의 포인터는 계속 원본 시작점을 가리킵니다.

사용 형식

c
void farptr_read(BankPtr ptr, void* dst, u16 len);

매개변수

ptr

bank와 ptr을 담은 BankPtr 값입니다. 주소가 아니라 설명자 자체를 전달합니다. 호출자의 설명자는 변경되지 않습니다.

dst

쓰기 대상 메모리입니다. 실제 복사량을 모두 담을 공간을 확보하세요. 0바이트 전송을 제외하면 널 포인터나 잘못된 대상 주소를 검사하지 않습니다.

len

바이트 수이며 범위는 0~65535입니다. 0이면 메모리를 복사하지 않지만 뱅크 선택과 복구 과정은 실행합니다.

반환값

없음(void).

사용 조건과 제한

원본 전체가 의도한 ROM 매핑에서 읽히고 대상 전체가 쓰기 가능한 메모리 안에 있어야 합니다. 뱅크 창 경계에서 다음 뱅크로 넘어가거나, memmove처럼 겹치는 범위를 처리하거나, VRAM 쓰기 시점을 제어하지 않습니다. VRAM에는 그래픽 전송 API를 사용하세요.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
farptr_read(pointer,buffer,3);
bank_expect(buffer[0]==0x7A && buffer[1]==0x5C && buffer[2]==0x96,11);
bank_expect(pointer.bank==3 && pointer.ptr==payload+1,23);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 farptr_read로 3바이트를 복사하고 버퍼를 7A 5C 96와 비교합니다. 호출자의 원본 설명자나 포인터는 유지됩니다. 완성 프로그램은 길이 0 복사와 뱅크 복원도 별도로 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

farptr_read: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:32

선언 주석 원문
Copy len bytes from a far pointer; the caller provides a sufficiently large destination.

kitaqfc/lib/bank.c:58

c
void farptr_read(BankPtr ptr, void* dst, u16 len)
{
    __far_memcpy((u8*)dst, ptr.bank, (u16)ptr.ptr, len);
}
farptr_read16

BankPtr에 저장된 뱅크와 주소에서 리틀 엔디언 워드를 읽습니다. 설명자는 값으로 전달되며 포인터는 증가하지 않습니다.

사용 형식

c
u16 farptr_read16(BankPtr ptr);

매개변수

ptr

bank와 ptr을 담은 BankPtr 값입니다. 주소가 아니라 설명자 자체를 전달합니다. 호출자의 설명자는 변경되지 않습니다.

반환값

읽은 리틀 엔디언 워드를 u16(0~65535)으로 반환합니다.

사용 조건과 제한

선택한 CPU 매핑에서 바이트 또는 워드 전체를 읽을 수 있어야 합니다. 포인터 검사, 주소 잘라내기, 창 경계에서 다음 ROM 뱅크로의 자동 전환은 수행하지 않습니다. 워드 읽기에는 연속된 유효한 두 바이트가 필요합니다.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
bank_expect(farptr_read16(pointer)==0x5C7A,10);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 farptr_read16로 뱅크 3의 pointer.ptr를 읽고 5C7A를 확인합니다. 완성 프로그램은 home2의 근거리 읽기로 뱅크 2 매핑이 복원되었는지도 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

farptr_read16: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:30

선언 주석 원문
Read a 16-bit value using both components of the far pointer.

kitaqfc/lib/bank.c:52

c
u16 farptr_read16(BankPtr ptr)
{
    return __farpeek16(ptr.bank, (u16)ptr.ptr);
}
farptr_read8

값으로 전달된 BankPtr의 두 필드를 사용해 한 바이트를 읽습니다. 일반 근거리 포인터에 없는 뱅크 번호를 설명자에서 가져옵니다.

사용 형식

c
u8 farptr_read8(BankPtr ptr);

매개변수

ptr

bank와 ptr을 담은 BankPtr 값입니다. 주소가 아니라 설명자 자체를 전달합니다. 호출자의 설명자는 변경되지 않습니다.

반환값

읽은 바이트를 u8(0~255)로 반환합니다.

사용 조건과 제한

선택한 CPU 매핑에서 바이트 또는 워드 전체를 읽을 수 있어야 합니다. 포인터 검사, 주소 잘라내기, 창 경계에서 다음 ROM 뱅크로의 자동 전환은 수행하지 않습니다. 워드 읽기에는 연속된 유효한 두 바이트가 필요합니다.

원거리 읽기와 복사는 동기식이며 컴파일러의 공유 작업 공간을 사용합니다. 처리 중 인터럽트에서 다른 뱅크 전송을 실행해 같은 공간을 덮어쓰지 마세요. GB 복사 보조 코드는 복귀 뱅크에도 공유 HRAM 슬롯 하나를 씁니다. 예제는 경쟁하는 전송 인터럽트 없이 고정 코드에서 실행합니다.

FC 전환 번호는 컴파일러의 논리 뱅크 번호입니다. 0은 공통 ROM이며 전환 대상이 아닙니다. 보조 코드는 0을 뱅크 1로 처리합니다. SUROM512에서는 1~30만 유효합니다. 전환 내장 함수에 범위를 벗어난 상수를 직접 전달하면 빌드 오류가 나며, 잘못된 실행 시점 값은 1로 대체되고 매퍼 오류 상태가 설정됩니다. 공통 ROM은 뱅크 0 전환을 요청하지 말고 직접 읽으세요.

사용 예

c
bank_expect(farptr_read8(pointer)==0x7A,9);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 코드 조각은 farptr_read8로 뱅크 3의 pointer.ptr를 읽고 7A를 확인합니다. 완성 프로그램은 home2의 근거리 읽기로 뱅크 2 매핑이 복원되었는지도 확인합니다.

완성 프로그램은 뱅크 번호와 근거리 주소로 데이터를 지정하는 방법, 뱅크를 넘는 콜백, 라이브러리 기록과 실제 매핑의 구별을 보여 줍니다. 모든 표시 숫자는 16진수입니다. BANK IN과 BANK OUT은 02, BYTE HEX는 D3, WORD HEX는 5C7A, FAILED CHECKS와 FIRST FAILURE는 모두 00이어야 합니다. CGB와 FC는 파란 글자, DMG는 검은 글자를 표시합니다.

CALL COUNT는 02입니다. far_call 매크로와 __farcall이 각각 콜백을 한 번 호출합니다.

farptr_read8: MMC3
MMC3

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\banked_data_calls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/bank.h:28

선언 주석 원문
Read one byte using both the bank and address of the supplied far pointer.

kitaqfc/lib/bank.c:46

c
u8 farptr_read8(BankPtr ptr)
{
    return __farpeek8(ptr.bank, (u16)ptr.ptr);
}

chain.h — 위치 이력용 링 버퍼

관절로 이어진 뱀이나 밧줄에는 ChainBody를, 위치 이력 기록에는 Chain을 사용하세요. 몸체의 각 관절은 현재 위치와 16방향 중 하나를 저장하며, 굽힘은 갱신할 때마다 한 관절씩 뒤로 전달됩니다. 몸체 연산은 필드 경계 래핑, 꼬리 복제로 길이 늘리기, 축마다 최대 2픽셀의 보정을 지원합니다.

chain_wrap_delta

양 끝이 연결된 축에서 현재 위치부터 목표까지의 가장 짧은 부호 있는 변위를 계산합니다. 필드 경계를 넘는 추적이나 거리 비교에 사용하세요.

사용 형식

c
s16 chain_wrap_delta(u8 target, u8 current, u16 size);

매개변수

target

목표 좌표이며, 범위는 0~size−1입니다.

current

현재 좌표이며, 범위는 0~size−1입니다.

size

경계가 이어지는 축 길이 1~256입니다. X에는 너비, Y에는 높이를 사용합니다. 256을 표현할 수 있도록 16비트 인수입니다.

반환값

최단 변위를 s16으로 반환합니다. 좌표가 증가하는 방향은 양수, 감소하는 방향은 음수, 같은 위치는 0입니다. 유효한 입력의 결과는 −128~+128입니다. 화면 좌표에서는 오른쪽과 아래 방향이 양수입니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. Chain이나 ChainBody를 초기화할 필요는 없습니다. 이 함수는 변위만 계산하며 좌표 배열, 관절 수, 화면을 변경하지 않습니다.

입력을 검사하거나 정규화하지 않습니다. size=0, size>256 또는 size 이상의 좌표를 전달하지 마세요. 더 큰 좌표계에서 온 좌표는 호출 전에 0~size−1로 래핑하세요.

양쪽 경로 길이가 같으면 직접 계산한 target−current의 부호를 유지합니다. size=160일 때 0→80은 +80, 80→0은 −80입니다. size=1이면 유효한 좌표는 0뿐이며 결과도 0입니다.

사용 예

c
distances[0]=chain_wrap_delta(1,159,160);   // +2: cross the right edge.
    distances[1]=chain_wrap_delta(159,1,160);   // -2: cross the left edge.
    distances[2]=chain_wrap_delta(80,0,160);    // +80: keep the direct sign.
    distances[3]=chain_wrap_delta(0,80,160);    // -80: same-length routes.
    distances[4]=chain_wrap_delta(0,0,1);       // 0: the only valid position.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

경계 래핑 좌표를 보여 주는 예제입니다. 각 화살표는 현재 위치에서 목표로 향합니다. 너비 160의 경계 통과는 +02/−02, 필드 절반은 +80/−80, 너비 1은 +00으로 표시합니다. 앞의 0은 10진수 자리 채움입니다. 이 예제는 오브젝트를 그리거나 움직이지 않습니다.

chain_wrap_delta: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_wrap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_wrap.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:39

선언 주석 원문
Return target minus current along the shortest wrapped route.
Supply size 1..256 and coordinates in 0..size-1; inputs are not normalized.
Exactly half a field retains the direct displacement sign.

kitaqfc/lib/chain.c:103

c
s16 chain_wrap_delta(u8 target, u8 current, u16 size)
{
    s16 delta;
    s16 half;
    delta = (s16)((s16)target - (s16)current);
    half = (s16)(size >> 1);
    // At exactly half a field, retain the sign of the direct displacement.
    if (((u16)delta & 0x8000) != 0) {
        if ((u16)(0 - delta) > (u16)half) delta = (s16)(delta + (s16)size);
    } else if ((u16)delta > (u16)half) delta = (s16)(delta - (s16)size);
    return delta;
}
chain_body_init

메모리를 할당하지 않고 호출자가 소유한 관절 배열 3개를 연결한 뒤 관절 수를 0으로 설정합니다.

사용 형식

c
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height);

매개변수

body

대상 ChainBody입니다. null은 무시합니다. 초기화 함수 외에는 초기화된 상태를 전달하세요.

x

관절 X 좌표 배열입니다.

y

관절 Y 좌표 배열입니다.

heading

관절 방향 배열입니다.

capacity

할당한 배열 길이 1~255입니다.

width

경계가 이어지는 필드 너비 1~256입니다.

height

경계가 이어지는 필드 높이 1~256입니다.

반환값

성공하면 1, 인수가 유효하지 않으면 0을 반환합니다. null이 아닌 몸체에서 실패하면 capacity와 count를 0으로 설정하여 갱신을 비활성화합니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. ChainBody는 각 관절의 현재 위치와 방향을 저장합니다. Chain 위치 이력 링과는 별도의 상태 형식입니다.

각각 capacity바이트인 별도의 쓰기 가능한 배열 x, y, heading을 제공하세요. 몸체를 사용하는 동안 배열을 유지해야 합니다. 인덱스 0은 머리이며 count에 포함됩니다. 초기화 후에는 저장 공간 포인터, 용량, 필드 크기를 바꾸지 마세요.

방향은 시계 방향의 16구간입니다. 0=오른쪽, 4=아래, 8=왼쪽, 12=위이며 입력의 하위 4비트만 사용합니다. 화면 좌표는 오른쪽·아래로 증가하고 각 필드 크기는 1~256픽셀입니다.

사용 예

c
chain_body_init(&body,joint_x,joint_y,joint_heading,8,160,144);
    // Eight caller-owned slots, including the head, on a wrapped 160x144 field.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

chain_body_init: NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.
NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:57

선언 주석 원문
Attach three non-overlapping capacity-byte arrays. Return 1 on success.
Invalid storage, zero capacity or dimensions outside 1..256 disable the body.

kitaqfc/lib/chain.c:127

c
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height)
{
    if (body == 0) return 0;
    body->x = x;
    body->y = y;
    body->heading = heading;
    body->count = 0;
    body->capacity = 0;
    body->width = width;
    body->height = height;
    if (x == 0 || y == 0 || heading == 0 || capacity == 0) return 0;
    if (width == 0 || width > 256 || height == 0 || height > 256) return 0;
    body->capacity = capacity;
    return 1;
}
chain_body_reset

지정한 머리 뒤에 관절을 배치하여 곧은 초기 자세를 만듭니다.

사용 형식

c
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading);

매개변수

body

대상 ChainBody입니다. null은 무시합니다. 초기화 함수 외에는 초기화된 상태를 전달하세요.

count

머리를 포함한 관절 수이며 1~capacity입니다.

head_x

머리 X 좌표이며 필드 너비에 맞게 래핑됩니다.

head_y

머리 Y 좌표이며 필드 높이에 맞게 래핑됩니다.

heading

16구간 체계의 머리 방향입니다.

반환값

성공하면 1을 반환합니다. count가 0이거나 너무 크거나 body가 null이면 자세를 바꾸지 않고 0을 반환합니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. ChainBody는 각 관절의 현재 위치와 방향을 저장합니다. Chain 위치 이력 링과는 별도의 상태 형식입니다.

각각 capacity바이트인 별도의 쓰기 가능한 배열 x, y, heading을 제공하세요. 몸체를 사용하는 동안 배열을 유지해야 합니다. 인덱스 0은 머리이며 count에 포함됩니다. 초기화 후에는 저장 공간 포인터, 용량, 필드 크기를 바꾸지 마세요.

방향은 시계 방향의 16구간입니다. 0=오른쪽, 4=아래, 8=왼쪽, 12=위이며 입력의 하위 4비트만 사용합니다. 화면 좌표는 오른쪽·아래로 증가하고 각 필드 크기는 1~256픽셀입니다.

상하좌우 간격은 4픽셀입니다. 중간 방향은 (4,1), (3,3), (1,4) 같은 정수 벡터를 사용하므로 유클리드 거리가 정확히 4픽셀은 아닙니다. 초기 자세가 필드 경계를 넘을 수 있습니다.

사용 예

c
chain_body_reset(&body,7,48,16,0); // Seven joints, facing right.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

chain_body_reset: NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.
NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:61

선언 주석 원문
Seed count joints in a straight pose behind the head; count includes the head.
Return 0 without changing the pose if count is zero or exceeds capacity.
Coordinates wrap to the playfield and headings are masked to 0..15.

kitaqfc/lib/chain.c:143

c
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading)
{
    // Cache caller storage once; following math and old-heading propagation
    // use these local values. Avoid decoding packed struct pointers per joint.
    u8* coord_x;u8* coord_y;u8* directions;u16 width;u16 height;
    u8 i;
    u8 x;
    u8 y;
    s16 ox;
    s16 oy;
    if (body == 0) return 0;
    if (count == 0 || count > body->capacity) return 0;
    coord_x=body->x;coord_y=body->y;directions=body->heading;
    width=body->width;height=body->height;
    heading = (u8)(heading & 15);
    x = chain_body_wrap((s16)head_x, width);
    y = chain_body_wrap((s16)head_y, height);
    ox = chain_body_offset_x(heading);
    oy = chain_body_offset_y(heading);
    i = 0;
    while (i < count) {
        coord_x[(__safe_index u8)i] = x;
        coord_y[(__safe_index u8)i] = y;
        directions[(__safe_index u8)i] = heading;
        x = chain_body_wrap((s16)((s16)x - ox), width);
        y = chain_body_wrap((s16)((s16)y - oy), height);
        i = (u8)(i + 1);
    }
    body->count = count;
    return 1;
}
chain_body_step

머리 자세를 설정하고 각 관절을 앞 관절 뒤의 연결점으로 조금씩 끌어당깁니다. 방향 변화는 갱신마다 한 관절씩 전파됩니다.

사용 형식

c
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading);

매개변수

body

대상 ChainBody입니다. null은 무시합니다. 초기화 함수 외에는 초기화된 상태를 전달하세요.

head_x

머리 X 좌표이며 필드 너비에 맞게 래핑됩니다.

head_y

머리 Y 좌표이며 필드 높이에 맞게 래핑됩니다.

heading

16구간 체계의 머리 방향입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. ChainBody는 각 관절의 현재 위치와 방향을 저장합니다. Chain 위치 이력 링과는 별도의 상태 형식입니다.

각각 capacity바이트인 별도의 쓰기 가능한 배열 x, y, heading을 제공하세요. 몸체를 사용하는 동안 배열을 유지해야 합니다. 인덱스 0은 머리이며 count에 포함됩니다. 초기화 후에는 저장 공간 포인터, 용량, 필드 크기를 바꾸지 마세요.

방향은 시계 방향의 16구간입니다. 0=오른쪽, 4=아래, 8=왼쪽, 12=위이며 입력의 하위 4비트만 사용합니다. 화면 좌표는 오른쪽·아래로 증가하고 각 필드 크기는 1~256픽셀입니다.

앞 관절의 갱신된 위치로 목표 연결점을 구한 뒤, 각 축에서 경계를 고려한 최단 변위를 계산합니다. 변위가 0이면 이동하지 않고, 크기 1~4는 1픽셀, 5 이상은 2픽셀 이동합니다. 정확히 필드 절반이면 원래 변위의 부호를 유지합니다.

각 관절의 갱신 전 저장된 방향을 다음 관절로 전달하여 굽힘이 몸체를 따라 이동하게 합니다. 빈 몸체는 변경하지 않습니다.

머리 속도, 가속도, 충돌, 그리기, 대기는 호출자가 처리합니다. 보통 머리를 먼저 갱신하고 시뮬레이션 틱마다 chain_body_step을 한 번 호출하세요. 관절 추적 기능이며, 강체나 정확한 길이 제약을 푸는 솔버는 아닙니다.

사용 예

c
chain_body_step(&body,48,20,4); // The head turns down; only joint 1 receives 4.
    show_pose(8,34);
    for(step=1;step<4;step++) {
        chain_body_step(&body,48,(u8)(20+step),4);
    }
    // After four ticks, the new heading has reached joints 1 through 4.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

chain_body_step: NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.
NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:66

선언 주석 원문
Install the caller's head pose, then pull joints from front to rear.
Each joint aims four pixels behind its updated predecessor and receives that
predecessor's OLD heading. Each axis moves 1 pixel, or 2 when error exceeds 4.
Call once per simulation tick; this does not advance head physics or render.

kitaqfc/lib/chain.c:175

c
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading)
{
    // Cache caller storage once; following math and old-heading propagation
    // use these local values. Avoid decoding packed struct pointers per joint.
    u8* coord_x;u8* coord_y;u8* directions;u16 width;u16 height;
    u8 i;
    u8 previous;
    u8 pull;
    u8 old;
    u8 x;
    u8 y;
    u8 tx;
    u8 ty;
    s16 sx;
    s16 sy;
    if (body == 0) return;
    if (body->count == 0) return;
    coord_x=body->x;coord_y=body->y;directions=body->heading;
    width=body->width;height=body->height;
    heading = (u8)(heading & 15);
    coord_x[0] = chain_body_wrap((s16)head_x, width);
    coord_y[0] = chain_body_wrap((s16)head_y, height);
    directions[0] = heading;
    pull = heading;
    i = 1;
    while (i < body->count) {
        previous = (u8)(i - 1);
        old = directions[(__safe_index u8)i];
        x = coord_x[(__safe_index u8)i];
        y = coord_y[(__safe_index u8)i];
        tx = chain_body_wrap((s16)((s16)coord_x[(__safe_index u8)previous] - chain_body_offset_x(pull)), width);
        ty = chain_body_wrap((s16)((s16)coord_y[(__safe_index u8)previous] - chain_body_offset_y(pull)), height);
        sx = chain_body_correction(chain_wrap_delta(tx, x, width));
        sy = chain_body_correction(chain_wrap_delta(ty, y, height));
        directions[(__safe_index u8)i] = pull;
        coord_x[(__safe_index u8)i] = chain_body_wrap((s16)((s16)x + sx), width);
        coord_y[(__safe_index u8)i] = chain_body_wrap((s16)((s16)y + sy), height);
        // Save before overwriting: a bend advances by one joint per update.
        pull = old;
        i = (u8)(i + 1);
    }
}
chain_body_grow

꼬리의 현재 위치와 방향을 다음 빈 슬롯에 복사하고 개수를 1 늘립니다.

사용 형식

c
u8 chain_body_grow(ChainBody* body);

매개변수

body

대상 ChainBody입니다. null은 무시합니다. 초기화 함수 외에는 초기화된 상태를 전달하세요.

반환값

관절을 추가하면 1을 반환합니다. 몸체가 비어 있거나 가득 찼거나 비활성 또는 null이면 0입니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. ChainBody는 각 관절의 현재 위치와 방향을 저장합니다. Chain 위치 이력 링과는 별도의 상태 형식입니다.

각각 capacity바이트인 별도의 쓰기 가능한 배열 x, y, heading을 제공하세요. 몸체를 사용하는 동안 배열을 유지해야 합니다. 인덱스 0은 머리이며 count에 포함됩니다. 초기화 후에는 저장 공간 포인터, 용량, 필드 크기를 바꾸지 마세요.

방향은 시계 방향의 16구간입니다. 0=오른쪽, 4=아래, 8=왼쪽, 12=위이며 입력의 하위 4비트만 사용합니다. 화면 좌표는 오른쪽·아래로 증가하고 각 필드 크기는 1~256픽셀입니다.

새 꼬리는 처음에 이전 꼬리와 겹치고, 이후 chain_body_step 호출로 분리됩니다. 성장 자체는 머리나 다른 관절을 움직이지 않습니다.

사용 예

c
chain_body_grow(&body); // Copy the tail pose into the eighth slot.
    chain_body_step(&body,48,16,0);
    chain_body_step(&body,48,16,0);
    chain_body_step(&body,48,16,0);
    chain_body_step(&body,48,16,0); // Allow the duplicated tail to separate.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

chain_body_grow: NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.
NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:69

선언 주석 원문
Append a copy of the current tail; later steps separate the new joint.
Return 1 on growth, or 0 for an empty, disabled or full body.

kitaqfc/lib/chain.c:218

c
u8 chain_body_grow(ChainBody* body)
{
    u8 tail;
    u8 next;
    if (body == 0) return 0;
    next = body->count;
    if (next == 0 || next >= body->capacity) return 0;
    tail = (u8)(next - 1);
    body->x[(__safe_index u8)next] = body->x[(__safe_index u8)tail];
    body->y[(__safe_index u8)next] = body->y[(__safe_index u8)tail];
    body->heading[(__safe_index u8)next] = body->heading[(__safe_index u8)tail];
    body->count = (u8)(next + 1);
    return 1;
}
chain_body_clear

배열과 필드 설정을 유지하면서 관절 수를 0으로 설정합니다.

사용 형식

c
void chain_body_clear(ChainBody* body);

매개변수

body

대상 ChainBody입니다. null은 무시합니다. 초기화 함수 외에는 초기화된 상태를 전달하세요.

반환값

반환값은 없습니다.

사용 조건과 제한

chain.h를 포함하고 chain.c를 정확히 한 번 컴파일하세요. ChainBody는 각 관절의 현재 위치와 방향을 저장합니다. Chain 위치 이력 링과는 별도의 상태 형식입니다.

각각 capacity바이트인 별도의 쓰기 가능한 배열 x, y, heading을 제공하세요. 몸체를 사용하는 동안 배열을 유지해야 합니다. 인덱스 0은 머리이며 count에 포함됩니다. 초기화 후에는 저장 공간 포인터, 용량, 필드 크기를 바꾸지 마세요.

방향은 시계 방향의 16구간입니다. 0=오른쪽, 4=아래, 8=왼쪽, 12=위이며 입력의 하위 4비트만 사용합니다. 화면 좌표는 오른쪽·아래로 증가하고 각 필드 크기는 1~256픽셀입니다.

배열 바이트는 지우지 않습니다. 다시 시작하려면 chain_body_reset을 사용하세요. 비운 직후 chain_body_grow를 호출해도 관절은 추가되지 않습니다.

사용 예

c
chain_body_clear(&body); // Forget the pose; the three arrays remain owned by us.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

chain_body_clear: NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.
NTSC — 이미지 위쪽은 곧은 몸체, 가운데는 아래로 방향을 바꾼 뒤 한 번 갱신한 모습, 아래쪽은 네 번 갱신한 모습입니다. 좌표는 논리 크기의 두 배로 그립니다. 굽힘은 오른쪽 머리에서 뒤로 전달됩니다. CGB와 FC는 머리를 빨강, 몸통을 파랑, 꼬리를 초록으로 표시하며 DMG는 검은색입니다. 성장 후 네 번 안정화 갱신을 하면 겹친 꼬리가 분리됩니다. 이는 같은 여덟 관절의 세 상태이며, 관절 24개짜리 몸체 하나가 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\chain_body.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\chain_body.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\chain_body.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/chain.h:71

선언 주석 원문
Remove all joints while retaining caller-owned storage and playfield settings.

kitaqfc/lib/chain.c:233

c
void chain_body_clear(ChainBody* body)
{
    if (body == 0) return;
    body->count = 0;
}
chain_clear

기록된 이력을 비워 같은 저장 배열을 다시 사용할 수 있게 합니다.

사용 형식

c
void chain_clear(Chain* chain);

매개변수

chain

초기화된 Chain을 가리키는 포인터입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

저장 배열을 지우거나 해제하지 않고 개수와 head를 0으로 초기화합니다. Chain과 점 배열은 호출 측이 소유하며 사용 중 계속 유효해야 합니다. 읽기 함수의 8비트 인덱스 덧셈이 넘치지 않도록 용량은 128 이하로 지정하세요.

FC 버전은 chain == 0이면 아무 동작도 하지 않습니다.

사용 예

c
chain_clear(&trail);result[11]=chain_get_count(&trail);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 점 네 개의 이력을 비우고 개수가 0이 되는지 확인합니다.

chain_clear: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/chain.h:24

선언 주석 원문
Forget the recorded points without clearing or freeing the backing storage.

kitaqfc/lib/chain.c:16

c
void chain_clear(Chain* chain)
{
    if (chain == 0) return;
    chain->count = 0;
    chain->head = 0;
}
chain_get_count

현재 이력에서 읽을 수 있는 위치의 개수를 반환합니다.

사용 형식

c
u8 chain_get_count(const Chain* chain);

매개변수

chain

초기화된 Chain을 가리키는 포인터입니다.

반환값

0부터 용량까지의 개수를 반환합니다. chain이 NULL이면 0입니다.

사용 조건과 제한

Chain과 점 배열은 호출 측이 소유하며 사용 중 계속 유효해야 합니다. 읽기 함수의 8비트 인덱스 덧셈이 넘치지 않도록 용량은 128 이하로 지정하세요.

사용 예

c
result[4]=chain_get_count(&trail);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

네 슬롯에 다섯 번 추가해도 개수는 4입니다. 비우면 0이 되며, 용량이 0인 이력에서는 계속 0입니다.

chain_get_count: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/chain.h:33

선언 주석 원문
Return the number of stored points, or zero for a null chain.

kitaqfc/lib/chain.c:66

c
u8 chain_get_count(const Chain* chain)
{
    if (chain == 0) return 0;
    return chain->count;
}
chain_get_segment

얼마나 오래된 점인지 지정하여 저장된 위치를 읽습니다.

사용 형식

c
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out);

매개변수

chain

초기화된 Chain을 가리키는 포인터입니다.

index

점의 이력 인덱스입니다. 0이 최신 점이며 count - 1이 남아 있는 가장 오래된 점입니다.

out

결과를 받을 별도의 쓰기 가능한 ChainPoint입니다. NULL이면 실패합니다.

반환값

성공하면 1, 요청이 유효하지 않으면 0을 반환합니다. out이 NULL이 아니면 유효성 검사 전에 (0,0)으로 초기화합니다.

사용 조건과 제한

out이 저장 배열이나 Chain과 같은 메모리를 가리키면 안 됩니다. 출력을 초기화하면서 소스 데이터가 손상되기 때문입니다. Chain과 점 배열은 호출 측이 소유하며 사용 중 계속 유효해야 합니다. 읽기 함수의 8비트 인덱스 덧셈이 넘치지 않도록 용량은 128 이하로 지정하세요.

사용 예

c
result[1]=chain_get_segment(&trail,0,&point);result[2]=point.x;result[3]=point.y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

인덱스 0은 최신 점 (40,24), 인덱스 3은 가장 오래된 점 (16,8)을 읽습니다. 유효하지 않은 인덱스 4는 실패를 반환하고 출력은 (0,0)이 됩니다.

chain_get_segment: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/chain.h:31

선언 주석 원문
Read a point by age, with index zero denoting the newest point. Return zero
for an invalid request and clear a non-null output first. Keep head + index
within the 8-bit range: addition is narrowed before the capacity wrap.

kitaqfc/lib/chain.c:42

c
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out)
{
    u8 pos;
    // Do not alias out with the backing ring: it is cleared before the requested point is read.
    if (out != 0) {
        out->x = 0;
        out->y = 0;
    }

    if (out == 0) return 0;
    if (chain == 0) return 0;
    if (chain->capacity == 0) return 0;
    if (index >= chain->count) return 0;

    pos = (u8)(chain->head + index);
    while (pos >= chain->capacity) {
        pos = (u8)(pos - chain->capacity);
    }
    out->x = chain->points[(__safe_index u8)pos].x;
    out->y = chain->points[(__safe_index u8)pos].y;
    return 1;
}
chain_init

위치 이력용 순환 버퍼에 저장 공간을 연결하고, 기록된 점이 없는 상태로 시작합니다.

사용 형식

c
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity);

매개변수

chain

초기화할 쓰기 가능한 Chain입니다.

storage

ChainPoint 항목을 capacity개 담을 수 있는 배열입니다.

capacity

점 슬롯 수입니다. 이력을 사용하려면 1~128을 지정하세요. 0이면 기록하지 않습니다.

반환값

반환값은 없습니다.

사용 조건과 제한

Chain과 점 배열은 호출 측이 소유하며 사용 중 계속 유효해야 합니다. 읽기 함수의 8비트 인덱스 덧셈이 넘치지 않도록 용량은 128 이하로 지정하세요.

FC 버전은 chain == 0이면 아무 동작도 하지 않습니다.

사용 예

c
chain_init(&trail,storage,4);result[0]=chain_get_count(&trail);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 점 슬롯 네 개를 연결하고 초기 개수가 0인지 검사합니다.

chain_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/chain.h:22

선언 주석 원문
Attach caller-owned ring storage and start with no recorded points.
The storage must remain valid for the lifetime of the chain.

kitaqfc/lib/chain.c:5

c
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity)
{
    if (chain == 0) return;
    // A nonzero capacity requires writable storage for that many complete ChainPoint records.
    chain->points = storage;
    chain->capacity = capacity;
    chain->count = 0;
    chain->head = 0;
}
chain_push_head

이력의 head에 최신 위치를 추가합니다. 가득 찬 경우 가장 오래된 점을 덮어씁니다.

사용 형식

c
void chain_push_head(Chain* chain, s16 x, s16 y);

매개변수

chain

초기화된 Chain을 가리키는 포인터입니다.

x

새 점의 부호 있는 X 좌표입니다.

y

새 점의 부호 있는 Y 좌표입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

chain이 NULL이거나 용량이 0이면 아무 작업도 하지 않습니다. Chain과 점 배열은 호출 측이 소유하며 사용 중 계속 유효해야 합니다. 읽기 함수의 8비트 인덱스 덧셈이 넘치지 않도록 용량은 128 이하로 지정하세요.

사용 예

c
chain_push_head(&trail,8,8);chain_push_head(&trail,16,8);chain_push_head(&trail,24,16);chain_push_head(&trail,32,16);chain_push_head(&trail,40,24);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

네 슬롯에 다섯 번 추가하면 최신 점은 (40,24), 남아 있는 가장 오래된 점은 (16,8)이 됩니다. 원래의 (8,8)은 덮어써집니다.

chain_push_head: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_chain.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_chain.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/chain.h:27

선언 주석 원문
Move the ring head backward and store the newest point. Once full, each push
replaces the oldest point; zero capacity is a no-op.

kitaqfc/lib/chain.c:25

c
void chain_push_head(Chain* chain, s16 x, s16 y)
{
    if (chain == 0) return;
    if (chain->capacity == 0) return;

    if (chain->head == 0) chain->head = (u8)(chain->capacity - 1);
    else chain->head = (u8)(chain->head - 1);

    chain->points[(__safe_index u8)chain->head].x = x;
    chain->points[(__safe_index u8)chain->head].y = y;

    if (chain->count < chain->capacity) chain->count = (u8)(chain->count + 1);
}

collision.h — 사각형 등 도형의 충돌 검사

부호 없는 16비트 위치를 사용하여 FC 월드 좌표에서 사각형끼리 또는 사각형과 점의 겹침을 검사합니다.

nes_box16_contains_point

점이 FC 사각형 안에 있는지 검사합니다. 왼쪽과 위쪽 가장자리는 포함하고, 오른쪽과 아래쪽 가장자리는 제외합니다.

사용 형식

c
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py);

매개변수

box

읽을 수 있는 NesBox16입니다. null을 전달하지 마세요.

px

점의 X 좌표입니다. 부호 없는 16비트 값입니다.

py

점의 Y 좌표입니다. 부호 없는 16비트 값입니다.

반환값

안쪽이면 1, 바깥쪽이면 0을 반환합니다.

사용 조건과 제한

x, y는 부호 없는 16비트 좌표이고, 너비와 높이는 바이트 값입니다. 양수인 크기를 사용하고, 좌표와 크기의 합이 65535를 넘지 않게 하세요. 좌표의 래핑 경계를 가로지르는 사각형은 처리하지 못합니다.

사용 예

c
result[6]=nes_box16_contains_point(&na,9,1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

좌표 1단위는 타일 하나입니다. (1,1)의 사각형 A와 (5,4)의 사각형 B는 모두 8×6 크기입니다. A만 차지하는 칸은 A, B만 차지하는 칸은 B로 표시하고, 4×3 겹침 영역의 12칸은 O로 표시합니다. A의 오른쪽 가장자리 x=9는 포함하지 않습니다. CGB에서는 A가 빨간색, B가 파란색, O가 초록색입니다.

nes_box16_contains_point: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/collision.h:18

선언 주석 원문
Test a half-open box: left/top edges are included and right/bottom edges
excluded. Callers provide valid pointers and non-overflowing edge coordinates.

kitaqfc/lib/collision.c:49

c
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py)
{
    unsigned short right;
    unsigned short bottom;

    right = (unsigned short)(box->x + box->w);
    bottom = (unsigned short)(box->y + box->h);

    if (px < box->x)
    {
        return 0;
    }
    if (py < box->y)
    {
        return 0;
    }
    if (px >= right)
    {
        return 0;
    }
    if (py >= bottom)
    {
        return 0;
    }

    return 1;
}
nes_box16_intersects

포인터로 전달된 두 FC 사각형이 면적을 갖고 겹치는지 검사합니다.

사용 형식

c
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b);

매개변수

a

읽을 수 있는 첫 번째 NesBox16입니다. null을 전달하지 마세요.

b

읽을 수 있는 두 번째 NesBox16입니다.

반환값

겹치면 1, 떨어져 있거나 가장자리만 닿으면 0을 반환합니다.

사용 조건과 제한

x, y는 부호 없는 16비트 좌표이고, 너비와 높이는 바이트 값입니다. 양수인 크기를 사용하고, 좌표와 크기의 합이 65535를 넘지 않게 하세요. 좌표의 래핑 경계를 가로지르는 사각형은 처리하지 못합니다.

사용 예

c
result[5]=nes_box16_intersects(&na,&nb);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

좌표 1단위는 타일 하나입니다. (1,1)의 사각형 A와 (5,4)의 사각형 B는 모두 8×6 크기입니다. A만 차지하는 칸은 A, B만 차지하는 칸은 B로 표시하고, 4×3 겹침 영역의 12칸은 O로 표시합니다. A의 오른쪽 가장자리 x=9는 포함하지 않습니다. CGB에서는 A가 빨간색, B가 파란색, O가 초록색입니다.

nes_box16_intersects: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/collision.h:15

선언 주석 원문
Test two axis-aligned world boxes using exclusive right/bottom edges.
Coordinate-plus-size sums must fit u16; touching edges return false.
Both boxes must have positive dimensions; the overlap test has no separate empty-box guard.

kitaqfc/lib/collision.c:15

c
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b)
{
    unsigned short a_right;
    unsigned short b_right;
    unsigned short a_bottom;
    unsigned short b_bottom;

    a_right = (unsigned short)(a->x + a->w);
    b_right = (unsigned short)(b->x + b->w);
    a_bottom = (unsigned short)(a->y + a->h);
    b_bottom = (unsigned short)(b->y + b->h);

    if (a_right <= b->x)
    {
        return 0;
    }
    if (b_right <= a->x)
    {
        return 0;
    }
    if (a_bottom <= b->y)
    {
        return 0;
    }
    if (b_bottom <= a->y)
    {
        return 0;
    }

    return 1;
}

danmaku — 64개 탄을 관리하는 풀

danmaku는 고정소수점 기반의 64개 탄환 풀입니다. 초기화 후 탄환을 생성하고 step으로 이동·충돌을 처리한 다음, draw로 OAM을 구성하고 VBlank에서 DMA를 수행하세요. 플레이어 입력, 피해, 점수, 오디오는 게임 코드에서 처리합니다. 예제는 독자적으로 만든 마름모 타일을 CHR RAM에 전송하지만 라이브러리 자체는 CHR ROM 스프라이트도 사용할 수 있습니다.

danmaku_reset

모든 탄환을 비활성화하고 피격·스침 이벤트, 최대 및 누적 개수, 할당·그리기 순서 커서를 리셋합니다.

사용 형식

c
void danmaku_reset(void);

반환값

반환값은 없습니다.

사용 조건과 제한

플레이어 위치와 dm_invulnerable은 유지됩니다. OAM과 화면은 지우지 않습니다. 보이는 탄환을 없애려면 빈 풀을 그린 뒤 DMA를 수행하거나 OAM을 따로 숨기세요.

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
danmaku_reset();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_reset: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:26

선언 주석 원문
Reset the pool, counters and rotating OAM priority. Player settings survive.

kitaqfc/lib/danmaku.c:40

c
void danmaku_reset(void) {
    danmaku_clear();
    dm_peak=0;dm_spawned=0;dm_rejected=0;dm_cursor=0;dm_draw_phase=0;
}
danmaku_clear

모든 탄환을 비활성화하고 dm_count, dm_hit, dm_graze를 0으로 만듭니다. 게임 도중 탄환을 제거할 때 사용하세요.

사용 형식

c
void danmaku_clear(void);

반환값

반환값은 없습니다.

사용 조건과 제한

최댓값 dm_peak, 누적값 dm_spawned와 dm_rejected, 할당 및 그리기 순서 커서, 플레이어 설정을 유지합니다. 예제는 탄환 하나를 생성한 뒤 지웁니다. 활성 수는 0이 되지만 누적 생성 수는 1로 남습니다. OAM은 따로 갱신하세요.

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
danmaku_clear();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_clear: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:28

선언 주석 원문
Remove all bullets and step events; preserve lifetime/peak counters.

kitaqfc/lib/danmaku.c:35

c
void danmaku_clear(void) {
    u8 i;
    for(i=0;i<64;i++)dm_active[i]=0;
    dm_count=0;dm_hit=0;dm_graze=0;
}
danmaku_spawn

풀을 순환 탐색하여 빈 슬롯 하나를 찾고 위치, 속도, 아직 스치지 않은 상태를 초기화합니다. 생성만으로 이동하거나 충돌을 검사하지는 않습니다.

사용 형식

c
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);

매개변수

x

중심의 가로 좌표이며 단위는 픽셀입니다. 허용 범위는 8~247입니다.

y

중심의 세로 좌표이며 단위는 픽셀입니다. 허용 범위는 24~231입니다.

vx

부호 있는 Q4.4 형식의 가로 속도입니다. 16이면 갱신당 1픽셀, −16이면 반대 방향으로 1픽셀 이동합니다. −128~127은 −8~+7.9375픽셀을 나타냅니다.

vy

부호 있는 Q4.4 형식의 세로 속도입니다. 16이면 갱신당 1픽셀, −16이면 반대 방향으로 1픽셀 이동합니다. −128~127은 −8~+7.9375픽셀을 나타냅니다.

반환값

성공하면 1, 위치가 잘못되었거나 64개 슬롯이 모두 차 있으면 0을 반환합니다. 성공 시 dm_count와 dm_spawned를 증가시키고 필요하면 dm_peak를 갱신합니다. 실패 시 dm_rejected를 증가시킵니다.

사용 조건과 제한

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
danmaku_spawn(48,64,16,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_spawn: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:31

선언 주석 원문
Allocate a bullet at pixel-center x/y with signed 1/16-pixel velocities.
Return one on success, zero outside the playfield or when all 64 slots are full.

kitaqfc/lib/danmaku.c:44

c
u8 danmaku_spawn(u8 x,u8 y,s8 vx,s8 vy) {
    u8 i,n;
    if(x<8 || x>=248 || y<24 || y>=232){dm_rejected++;return 0;}
    i=dm_cursor;
    for(n=0;n<64;n++) {
        if(dm_active[i]==0) {
            dm_x[i]=x;dm_y[i]=y;dm_fraction_x[i]=0;dm_fraction_y[i]=0;
            // Expand signed Q4.4 to a two-byte two's-complement Q8.8 step.
            dm_vx_integer[i]=(u8)((s16)vx>>4);dm_vx_fraction[i]=(u8)((u8)vx<<4);
            dm_vy_integer[i]=(u8)((s16)vy>>4);dm_vy_fraction[i]=(u8)((u8)vy<<4);
            dm_grazed[i]=0;dm_active[i]=1;dm_cursor=(u8)((i+1)&63);
            dm_count++;if(dm_count>dm_peak)dm_peak=dm_count;
            dm_spawned++;return 1;
        }
        i=(u8)((i+1)&63);
    }
    dm_rejected++;return 0;
}
danmaku_fan

일정한 간격으로 방향을 바꾸며 탄환을 생성합니다. 방향 간격과 개수에 따라 부채꼴이나 완전한 원형 탄막을 만들 수 있습니다.

사용 형식

c
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);

매개변수

x

중심의 가로 좌표이며 단위는 픽셀입니다. 허용 범위는 8~247입니다.

y

중심의 세로 좌표이며 단위는 픽셀입니다. 허용 범위는 24~231입니다.

direction

한 바퀴를 32단계로 나눈 초기 방향입니다. 0=오른쪽, 8=아래, 16=왼쪽, 24=위입니다. 하위 5비트만 사용합니다.

step

탄환 하나를 생성할 때마다 더하는 방향 증분입니다. 32를 법으로 순환합니다. 0이면 모든 탄환이 같은 방향으로 나갑니다.

count

생성 시도 횟수입니다. 64보다 크면 64로 제한하며, 0이면 아무것도 하지 않습니다.

speed

갱신당 1/16픽셀 단위의 속력입니다. 16은 1픽셀, 32는 2픽셀입니다. 64보다 크면 64로 제한합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

속도는 정수 사인 테이블에서 구하고 0 방향으로 절삭하므로 대각선 속력과 반지름에는 양자화 오차가 있습니다. 풀이 가득 차도 남은 시도는 취소하지 않으며 실패할 때마다 dm_rejected를 증가시킵니다.

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
danmaku_fan(160,104,0,2,16,32);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_fan: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:34

선언 주석 원문
Emit up to 64 bullets. Angles wrap in 32 steps: 0 right, 8 down, 16 left,
24 up. Speed is in 1/16 pixel, clamped to 64. Each failed allocation counts.

kitaqfc/lib/danmaku.c:62

c
void danmaku_fan(u8 x,u8 y,u8 direction,u8 step,u8 count,u8 speed) {
    u8 i,angle;s16 vx,vy;
    if(count>64)count=64;if(speed>64)speed=64;
    angle=(u8)(direction&31);
    for(i=0;i<count;i++) {
        vx=(s16)Danmaku_Sin[(u8)((angle+8)&31)]*(s16)speed;
        vy=(s16)Danmaku_Sin[angle]*(s16)speed;
        danmaku_spawn(x,y,(s8)(vx/64),(s8)(vy/64));
        angle=(u8)((angle+step)&31);
    }
}
danmaku_step

활성 탄환을 각각 한 번 이동시키고 화면 밖 탄환을 제거하며 플레이어 피격과 스침을 판정합니다. 탄환의 시간은 실제 경과 시간이 아니라 호출 횟수에 따라 진행됩니다.

사용 형식

c
void danmaku_step(void);

반환값

반환값은 없습니다.

사용 조건과 제한

호출 전에 dm_player_x와 dm_player_y를 플레이어 중심 좌표로 설정하세요. 이동 후 두 축의 거리가 모두 3픽셀 이하면 명중으로 판정합니다. dm_invulnerable이 0이면 dm_hit을 1로 설정하고 탄환을 제거합니다. 무적 상태에서는 탄환을 남겨 둡니다.

피격 영역 바깥에서 두 축의 거리가 모두 11픽셀 이하면 탄환당 한 번 스침으로 판정합니다. dm_hit과 dm_graze는 함수 진입 시 리셋되므로 이번 갱신 결과만 나타냅니다. 플레이어가 무적일 때 피격 영역 안에 있는 탄환은 스침으로 세지 않습니다.

이 함수는 OAM에 쓰지 않으며 피해나 사운드 콜백도 호출하지 않습니다. 게임 코드에서 dm_hit과 dm_graze를 읽어 플레이어 상태나 점수를 갱신하세요.

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
danmaku_step();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_step: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:38

선언 주석 원문
Move all live bullets once. Remove offscreen/hitting bullets. Hit uses a
+/-3-pixel box; graze uses +/-11, once per bullet. Events describe this step.
This updates logic only; it neither writes OAM nor calls a game damage handler.

kitaqfc/lib/danmaku.c:73

c
void danmaku_step(void) {
    // Byte arrays let the 6502 move every slot without multiplying a structure
    // index. Carry propagates fractional motion into the signed integer step.
    __asm {
        LDX #0
        STX dm_count
        STX dm_hit
        STX dm_graze
dm_tick_loop:
        LDA dm_active,X
        BNE dm_tick_live
        JMP dm_tick_next
dm_tick_live:
        CLC
        LDA dm_fraction_x,X
        ADC dm_vx_fraction,X
        STA dm_fraction_x,X
        LDA dm_x,X
        ADC dm_vx_integer,X
        STA dm_x,X
        CMP #8
        BCC dm_tick_kill
        CMP #248
        BCS dm_tick_kill
        CLC
        LDA dm_fraction_y,X
        ADC dm_vy_fraction,X
        STA dm_fraction_y,X
        LDA dm_y,X
        ADC dm_vy_integer,X
        STA dm_y,X
        CMP #24
        BCC dm_tick_kill
        CMP #232
        BCS dm_tick_kill
        // Unsigned absolute distances avoid false collisions across screen wrap.
        LDA dm_x,X
        SEC
        SBC dm_player_x
        BCS dm_tick_dx
        EOR #255
        CLC
        ADC #1
dm_tick_dx:
        STA dm_distance_x
        LDA dm_y,X
        SEC
        SBC dm_player_y
        BCS dm_tick_dy
        EOR #255
        CLC
        ADC #1
dm_tick_dy:
        STA dm_distance_y
        CMP #4
        BCS dm_tick_graze
        LDA dm_distance_x
        CMP #4
        BCS dm_tick_graze
        LDA dm_invulnerable
        BNE dm_tick_count
        LDA #1
        STA dm_hit
dm_tick_kill:
        LDA #0
        STA dm_active,X
        JMP dm_tick_next
dm_tick_graze:
        LDA dm_grazed,X
        BNE dm_tick_count
        LDA dm_distance_x
        CMP #12
        BCS dm_tick_count
        LDA dm_distance_y
        CMP #12
        BCS dm_tick_count
        LDA #1
        STA dm_grazed,X
        INC dm_graze
dm_tick_count:
        INC dm_count
dm_tick_next:
        INX
        CPX #64
        BEQ dm_tick_done
        JMP dm_tick_loop
dm_tick_done:
    }
}
danmaku_draw

탄환 중심에서 8×8 스프라이트를 구성하여 요청한 섀도 OAM 범위에 기록합니다. 탄환을 이동하거나 충돌을 검사하지는 않습니다.

사용 형식

c
u8 danmaku_draw(u8 first_oam, u8 slots, u8 tile, u8 attributes);

매개변수

first_oam

첫 OAM 슬롯 번호(0~63)입니다. 64 이상이면 아무것도 쓰지 않고 0을 반환합니다. 예제는 슬롯 0을 플레이어용으로 예약하기 위해 1부터 시작합니다.

slots

사용 가능한 연속 슬롯 수이며 최대 64−first_oam으로 제한합니다. 0이면 아무것도 쓰지 않지만 그리기 우선순위는 진행됩니다.

tile

모든 탄환이 공유하는 8×8 패턴 타일입니다. 스프라이트 모드를 8×8로 설정하세요. 이 호출은 모든 탄환에 같은 모양을 사용합니다.

attributes

모든 탄환이 공유하는 NES 스프라이트 속성입니다. 비트 0~1은 팔레트, 비트 5는 배경 뒤 표시, 비트 6은 좌우 반전, 비트 7은 상하 반전입니다.

반환값

OAM에 기록한 탄환 수를 반환합니다. 활성 탄환 수와 사용 가능한 슬롯 수 중 작은 값입니다.

사용 조건과 제한

특정 탄환이 항상 우선되지 않도록 호출마다 시작 탄환 슬롯을 64를 법으로 13씩 전진시킵니다. 요청한 범위 안의 사용하지 않는 슬롯은 Y=240으로 숨기고 범위 밖의 OAM은 유지합니다.

중심 X에서 4, 중심 Y에서 5를 뺍니다. 하드웨어가 OAM Y에 1을 더하므로 스프라이트의 중심이 맞춰집니다. VBlank 중에 __oam_dma를 직접 호출하세요. 스캔라인당 스프라이트 8개라는 하드웨어 제한은 그대로 적용됩니다.

64개의 탄환을 위해 카트리지의 쓰기 가능한 PRG RAM $6000–$627F(640바이트)를 예약하세요. 카트리지 매퍼에서 해당 RAM을 활성화해야 합니다. 작업 상태를 공유하므로 이 라이브러리는 재진입할 수 없습니다.

현재 활성 탄환 수는 dm_count, 그 최댓값은 dm_peak입니다. 16비트 카운터 dm_spawned와 dm_rejected는 각각 생성 성공과 실패 횟수를 세며 65535 다음에는 0으로 돌아갑니다. dm_x와 dm_y는 중심 좌표입니다. dm_active가 0이면 이 좌표를 사용하지 마세요.

KUROSAKI에서 4가지 컴파일러 설정에 대해 모든 픽셀, 탄환 수와 좌표, 예약된 OAM, 순환하는 그리기 순서를 확인합니다. 별도의 수치 검증은 피격, 스침, 화면 밖 탄환 제거, 풀이 가득 찬 경우를 다룹니다. 실기에서는 검증하지 않았습니다.

사용 예

c
bullet_result[9]=danmaku_draw(1,24,1,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

danmaku_draw: 기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.
기본 설정 — 왼쪽의 노란 탄환 4개는 (48,64)에서 상하좌우로 각각 16픽셀 이동했습니다. 오른쪽의 노란 탄환 16개는 (160,104)에서 약 32픽셀 떨어진 고리를 이룹니다. (112,192)의 청록색 플레이어는 예약된 OAM 슬롯 0을 사용합니다. 예제는 로직을 16회 갱신한 뒤 화면을 유지합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\danmaku_patterns.c -I .\kitaqfc\lib --mapper=nrom --nes-chr-ram -o .\out\danmaku_patterns.nes --no-cache --no-disasm
.\kurosaki\kurosaki.exe run .\out\danmaku_patterns.nes --headless --frames 180 --png .\out\danmaku_patterns.png
구현과 소스

kitaqfc/lib/danmaku.h:43

선언 주석 원문
Write up to slots consecutive shadow-OAM entries starting at first_oam.
Rotate bullet priority by 13 modulo 64 and hide unused entries in that range.
tile is an 8x8 sprite centered at (4,4). Return the number emitted. DMA is
caller-owned and belongs in VBlank. Hardware's eight-per-scanline limit remains.

kitaqfc/lib/danmaku.c:162

c
u8 danmaku_draw(u8 first_oam,u8 slots,u8 tile,u8 attributes) {
    u8 i,n,drawn;
    if(first_oam>=64)return 0;
    if(slots>(u8)(64-first_oam))slots=(u8)(64-first_oam);
    dm_draw_phase=(u8)((dm_draw_phase+13)&63);i=dm_draw_phase;drawn=0;
    for(n=0;n<64;n++) {
        if(dm_active[i]!=0 && drawn<slots) {
            __sprite_set((u8)(first_oam+drawn),(u8)(dm_x[i]-4),(u8)(dm_y[i]-5),tile,attributes);
            drawn++;
        }
        i=(u8)((i+1)&63);
    }
    for(n=drawn;n<slots;n++)__sprite_hide((u8)(first_oam+n));
    return drawn;
}

debug.h — RAM 기록과 단언

이름, 값, 프레임 태그를 고정 용량의 RAM 로그에 기록합니다. 어설션은 진단 정보를 남기며 실행은 계속됩니다.

debug_assert_fail

어설션 실패 코드를 보관하고 ASSERT 항목 추가를 시도합니다.

사용 형식

c
void debug_assert_fail(u16 code);

매개변수

code

보관할 16비트 오류 식별자입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

실행은 계속됩니다. 로그가 가득 차도 마지막 어설션 코드는 갱신합니다. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_assert_fail(42);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 코드 42를 기록하고 로그를 가득 채운 뒤 코드 999를 기록합니다. 개수는 32로 유지되지만 마지막 코드는 999로 바뀝니다.

debug_assert_fail: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:36

선언 주석 원문
Store the assertion code even if the log is full, then attempt an ASSERT
entry. The helper records failure but does not stop execution.

kitaqfc/lib/debug.c:67

c
void debug_assert_fail(u16 code)
{
    kq_debug_last_assert = code;
    debug_push(debug_assert_label, code);
}
debug_get_last_assert

남은 로그 공간과 관계없이 최근 어설션 코드를 읽습니다.

사용 형식

c
u16 debug_get_last_assert(void);

반환값

최근 16비트 코드를 반환합니다. 초기화 직후의 값은 0입니다.

사용 조건과 제한

0은 초기값이기도 하므로 코드 0으로 기록한 실패와 구분할 수 없습니다. 읽어도 코드는 지워지지 않습니다.

사용 예

c
result[22]=debug_get_last_assert();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 어설션 매크로의 77, 로그가 가득 찬 뒤의 999, 재초기화 후의 0을 확인합니다.

debug_get_last_assert: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:42

선언 주석 원문
Read the latest assertion code, initially zero after debug_init.

kitaqfc/lib/debug.c:86

c
u16 debug_get_last_assert(void)
{
    return kq_debug_last_assert;
}
debug_get_trace_count

추적 배열의 유효한 항목 수를 읽습니다.

사용 형식

c
u8 debug_get_trace_count(void);

반환값

0~DEBUG_TRACE_MAX 범위의 개수를 반환합니다.

사용 조건과 제한

로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
result[1]=debug_get_trace_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 처음 열 개의 항목과, 추가 기록 후의 상한 32를 확인합니다. 가득 차도 개수는 순환하여 0으로 돌아가지 않습니다.

debug_get_trace_count: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:38

선언 주석 원문
Read the number of valid entries in the fixed trace buffer.

kitaqfc/lib/debug.c:74

c
u8 debug_get_trace_count(void)
{
    return kq_debug_trace_count;
}
debug_get_trace_log

기록된 이름, 값, 프레임 태그를 확인할 수 있도록 내부 배열의 참조를 얻습니다.

사용 형식

c
const DebugTraceEntry* debug_get_trace_log(void);

반환값

내부 DebugTraceEntry 배열의 읽기 전용 포인터를 반환합니다.

사용 조건과 제한

복사본이 아닙니다. debug_get_trace_count()가 반환한 개수만큼만 읽으세요. 초기화 후 남아 있는 이전 바이트는 유효한 레코드가 아닙니다. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
entries=debug_get_trace_log();for(i=0;i<10;i++){result[2+i]=entries[i].value;result[12+i]=entries[i].frame;}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 열 개 항목의 값과 프레임 필드를 모두 읽고, BEGIN 이름의 첫 글자가 B인지 확인합니다.

debug_get_trace_log: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:40

선언 주석 원문
Borrow the internal trace array; bound inspection by debug_get_trace_count.

kitaqfc/lib/debug.c:80

c
const DebugTraceEntry* debug_get_trace_log(void)
{
    return kq_debug_trace_log;
}
debug_init

유효한 로그 길이, 프레임 태그, 마지막 어설션 코드를 초기화합니다.

사용 형식

c
void debug_init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

기존 항목의 바이트는 지우지 않습니다. 첫 항목을 기록하기 전에 호출하세요. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_init();result[0]=debug_get_trace_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기 개수가 0인지 확인한 뒤, 데이터가 있는 로그를 초기화합니다. 이전 배열 값은 남아 있지만 개수와 어설션 코드는 0이 되는지 검사합니다.

debug_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:25

선언 주석 원문
Build the RAM-resident ASSERT label and reset log length, frame tag and last
assertion code. Old log storage bytes are not erased.

kitaqfc/lib/debug.c:14

c
void debug_init(void)
{
    debug_assert_label[0] = 'A';
    debug_assert_label[1] = 'S';
    debug_assert_label[2] = 'S';
    debug_assert_label[3] = 'E';
    debug_assert_label[4] = 'R';
    debug_assert_label[5] = 'T';
    debug_assert_label[6] = 0;
    kq_debug_trace_count = 0;
    kq_debug_frame = 0;
    kq_debug_last_assert = 0;
}
debug_mark_frame

처리 흐름의 특정 지점을 식별하도록 이름 있는 프레임 마커를 기록합니다.

사용 형식

c
void debug_mark_frame(const u8* label);

매개변수

label

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_mark_frame("BEGIN");

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

BEGIN 마커를 추가하면 프레임 태그 513과 함께 값 65535(0xFFFF)가 기록됩니다.

debug_mark_frame: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:29

선언 주석 원문
Append a named marker with the reserved marker value 0xFFFF.

kitaqfc/lib/debug.c:48

c
void debug_mark_frame(const u8* label)
{
    debug_push(label, 0xFFFF);
}
debug_set_frame

이후 로그 항목에 복사할 프레임 태그를 설정합니다.

사용 형식

c
void debug_set_frame(u16 frame);

매개변수

frame

이후 항목에 사용할 16비트 프레임 번호입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

프레임을 기다리거나 태그를 자동으로 증가시키지 않습니다. 게임의 프레임 카운터를 명시적으로 전달하세요. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_set_frame(513);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 태그를 513으로 설정하고 항목 열 개를 기록한 뒤, 열 개의 프레임 필드를 모두 다시 읽어 태그를 확인합니다.

debug_set_frame: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:27

선언 주석 원문
Choose the frame tag stored in subsequent debug entries.

kitaqfc/lib/debug.c:29

c
void debug_set_frame(u16 frame)
{
    kq_debug_frame = frame;
}
debug_trace_u16

이름을 붙여 16비트 점수나 카운터를 기록합니다.

사용 형식

c
void debug_trace_u16(const u8* name, u16 value);

매개변수

name

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

value

기록할 0~65535의 값입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_trace_u16("WORD",4660);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

WORD 항목은 4660(0x1234)을 저장하며, 예제에서 다시 읽어 두 바이트를 모두 확인합니다.

debug_trace_u16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:33

선언 주석 원문
Append a 16-bit value with the current frame tag.

kitaqfc/lib/debug.c:60

c
void debug_trace_u16(const u8* name, u16 value)
{
    debug_push(name, value);
}
debug_trace_u8

이름 및 현재 프레임 태그와 함께 8비트 상태값을 기록합니다.

사용 형식

c
void debug_trace_u8(const u8* name, u8 value);

매개변수

name

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

value

0~255의 값이며, 상위 비트를 0으로 채워 로그의 16비트 필드에 저장합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
debug_trace_u8("BYTE",255);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

BYTE 항목에 255를 기록하고, 로그의 16비트 값 필드에도 255가 들어 있는지 확인합니다.

debug_trace_u8: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:31

선언 주석 원문
Widen a byte value into the trace entry's 16-bit value field.

kitaqfc/lib/debug.c:54

c
void debug_trace_u8(const u8* name, u8 value)
{
    debug_push(name, (u16)value);
}
KITAQGB_ASSERT

조건이 거짓이면 어설션 코드 1을 기록하는 문장 형태의 매크로입니다.

사용 형식

c
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)

매개변수

cond

성립해야 하는 조건입니다. 0이면 실패입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

조건은 한 번 평가합니다. 참이면 아무것도 기록하지 않고, 거짓이면 실행을 멈추지 않은 채 실패를 기록합니다. do…while (0) 형태이므로 if…else에서 하나의 문장처럼 사용할 수 있습니다.

사용 예

c
KITAQGB_ASSERT(1);KITAQGB_ASSERT(0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

참인 어설션과 거짓인 어설션을 하나씩 실행하면 코드 1인 항목이 정확히 하나 생깁니다.

KITAQGB_ASSERT: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:48

c
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)
KITAQGB_ASSERT_CODE

조건이 거짓일 때만 지정한 어설션 코드를 기록합니다.

사용 형식

c
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)

매개변수

cond

성립해야 하는 조건입니다.

code

실패했을 때 평가하고 16비트로 변환할 코드 식입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

조건은 한 번 평가하고, 코드 식은 실패했을 때만 평가합니다. 기록 후에도 실행은 계속됩니다.

사용 예

c
KITAQGB_ASSERT_CODE(1,++side_effect);KITAQGB_ASSERT_CODE(0,77);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

조건이 참이면 코드 식 ++side_effect는 평가하지 않으며 변수는 0으로 유지됩니다. 조건이 거짓이면 코드 77을 기록합니다.

KITAQGB_ASSERT_CODE: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:49

c
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)
KITAQGB_MARK_FRAME

이름을 문자열 포인터로 변환하고 값이 0xFFFF인 프레임 마커를 추가합니다.

사용 형식

c
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))

매개변수

label

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
KITAQGB_MARK_FRAME("END");

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

END 마커의 값이 65535, 프레임 태그가 513인지 확인합니다.

KITAQGB_MARK_FRAME: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:47

c
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))
KITAQGB_TRACE

값을 명시적으로 16비트로 변환한 뒤 이름과 함께 기록합니다.

사용 형식

c
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))

매개변수

name

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

value

기록 전에 16비트로 변환할 식입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

매크로는 항상 활성화되어 있으며 빌드 설정으로 자동 비활성화되지 않습니다. 16비트 기록 동작은 KITAQGB_TRACE_U16과 같습니다. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
KITAQGB_TRACE("SCORE",600);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 600을 기록합니다. 검증을 위해 로그에서 값을 다시 읽습니다.

KITAQGB_TRACE: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:46

c
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
KITAQGB_TRACE_U16

값을 명시적으로 16비트로 변환한 뒤 이름과 함께 기록합니다.

사용 형식

c
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))

매개변수

name

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

value

기록 전에 16비트로 변환할 식입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

매크로는 항상 활성화되어 있으며 빌드 설정으로 자동 비활성화되지 않습니다. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
KITAQGB_TRACE_U16("WORD2",500);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 500을 기록합니다. 검증을 위해 로그에서 값을 다시 읽습니다.

KITAQGB_TRACE_U16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:45

c
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
KITAQGB_TRACE_U8

값을 명시적으로 8비트로 변환한 뒤 이름과 함께 기록합니다.

사용 형식

c
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))

매개변수

name

0으로 끝나는 이름 문자열입니다. 해당 메모리는 확인할 때까지 읽을 수 있어야 합니다.

value

기록 전에 8비트로 변환할 식입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

매크로는 항상 활성화되어 있으며 빌드 설정으로 자동 비활성화되지 않습니다. 로그는 기본적으로 32개 항목을 담으며 가득 차면 새 항목을 버립니다. DEBUG_TRACE_MAX를 재정의할 때는 모든 번역 단위에서 동일한 1~255 값을 사용하세요. 이름은 복사하지 않고 포인터로 보관하므로 확인이 끝날 때까지 해당 메모리를 읽을 수 있어야 합니다. 인터럽트와 메인 코드의 쓰기가 겹치지 않도록 호출 측에서 순서를 보장하세요.

사용 예

c
KITAQGB_TRACE_U8("CAST",0x1234);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 52를 기록합니다. 0x1234의 하위 바이트인 0x34만 남습니다.

KITAQGB_TRACE_U8: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_debug.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_debug.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/debug.h:44

c
#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))

entity.h — 고정 배열 기반 오브젝트 풀

entity는 적, 탄환, 아이템 등의 상태를 담는 고정 배열 기반 오브젝트 풀입니다. 빈 슬롯을 할당하고 ID로 참조하다가 오브젝트가 필요 없어지면 슬롯을 재사용합니다. 힙 메모리는 할당하지 않습니다. 좌표와 속도의 단위, 이동, 충돌 처리, 타이머 갱신은 게임에서 정의합니다.

entity.h를 포함하고 entity.c를 정확히 한 번 컴파일하세요. 먼저 entity_init을 호출하고 entity_create의 반환값이 0xFF가 아닌지 확인하세요. ENTITY_MAX의 기본값은 16입니다. ID 체계는 1~255개 슬롯을 지원하지만 실제 할당 가능한 개수는 가용 RAM에 따라 달라집니다. 모든 컴파일 단위에서 같은 용량 정의를 사용하세요.

entity_get이 반환하는 포인터는 재사용 가능한 풀 저장 공간을 가리킵니다. 해제한 뒤에는 같은 ID가 다른 오브젝트를 나타낼 수 있으므로 ID와 포인터 모두 영구적인 식별자가 아닙니다. entity_destroy는 슬롯을 비활성화할 뿐입니다. 스프라이트를 숨기거나 해제하고 다른 자원을 정리하는 일은 호출자가 맡아야 합니다. sprite 필드는 관리용 정보이며 OAM을 자동으로 갱신하지 않습니다.

entity_update_all과 entity_draw_all은 활성 슬롯을 ID 오름차순으로 순회하며 fn(id)를 호출합니다. 실제 갱신이나 그리기 처리는 콜백에 작성하세요. 콜백이 null이면 아무 일도 하지 않습니다. 콜백 안에서 뒤쪽 슬롯을 생성하거나 해제하면 현재 순회에도 영향을 줍니다. 갱신 순회를 먼저 수행하고 그리기 순회를 수행하면 두 작업을 분리할 수 있습니다.

콜백을 공통 PRG 뱅크 0에 두거나, 호출이 끝날 때까지 해당 뱅크가 매핑된 상태를 유지하세요. 함수 포인터는 16비트 CPU 주소만 저장하며 뱅크를 선택하지 않습니다. FC의 함수 매개변수와 지역 변수는 고정된 메모리를 사용하므로 엔티티 순회를 재귀적으로 호출하거나 인터럽트에서 같은 순회에 재진입하면 안 됩니다.

entity_clear_all

모든 엔티티를 해제하고 스프라이트 미할당 표시를 포함한 필드를 초기화합니다.

사용 형식

c
void entity_clear_all(void);

반환값

없음(void).

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

모든 슬롯의 active,type,x,y,vx,vy,state,timer를 0, sprite를 0xFF로 설정합니다. 하드웨어 스프라이트를 숨기거나 해제하지는 않으므로 렌더링 자원은 별도로 정리하세요.

사용 예

c
entity_clear_all();
after_clear = entity_count_active();
if (after_clear != 0 || entity_get(reused) != 0) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_clear_all: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:47

선언 주석 원문
Release every entity and restore deterministic defaults, including the no-sprite sentinel.

kitaqfc/lib/entity.c:14

c
void entity_clear_all(void)
{
    u8 i;
    i = 0;
    while (i < ENTITY_MAX) {
        entity_pool[(__safe_index u8)i].active = 0;
        entity_pool[(__safe_index u8)i].type = 0;
        entity_pool[(__safe_index u8)i].x = 0;
        entity_pool[(__safe_index u8)i].y = 0;
        entity_pool[(__safe_index u8)i].vx = 0;
        entity_pool[(__safe_index u8)i].vy = 0;
        entity_pool[(__safe_index u8)i].state = 0;
        entity_pool[(__safe_index u8)i].timer = 0;
        entity_pool[(__safe_index u8)i].sprite = 0xFF;
        i = (u8)(i + 1);
    }
}
entity_count_active

엔티티 상태를 바꾸지 않고 사용 중인 슬롯 수를 반환합니다.

사용 형식

c
u8 entity_count_active(void);

반환값

active 바이트가 0이 아닌 슬롯 수로, 0~ENTITY_MAX입니다. 사용 중인 수이며 가장 큰 ID나 화면의 스프라이트 수가 아닙니다.

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

사용 예

c
created = entity_count_active();
if (created != 2) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_count_active: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:45

선언 주석 원문
Count occupied slots without changing entity state.

kitaqfc/lib/entity.c:94

c
u8 entity_count_active(void)
{
    u8 i;
    u8 count;
    i = 0;
    count = 0;
    while (i < ENTITY_MAX) {
        if (entity_pool[(__safe_index u8)i].active != 0) count = (u8)(count + 1);
        i = (u8)(i + 1);
    }
    return count;
}
entity_create

지정한 종류와 좌표로 엔티티를 할당하고 재사용 가능한 ID를 반환합니다. 빈 슬롯이 없으면 0xFF입니다.

사용 형식

c
u8 entity_create(u8 type, s16 x, s16 y);

매개변수

type

게임에서 정의하는 0~255의 종류 번호입니다. 풀은 값을 저장할 뿐, 종류에 따라 동작이나 그리기 함수를 선택하지 않습니다.

x, y

부호 있는 16비트 좌표(-32768~32767)입니다. 픽셀이나 Q8.8 등 단위는 게임이 정합니다. 풀은 단위 변환, 좌표 제한, 속도에 따른 자동 이동을 하지 않습니다.

반환값

가장 작은 빈 슬롯 ID(0~ENTITY_MAX-1)를 반환하며, 빈 슬롯이 없으면 0xFF입니다. 성공하면 active=1로 설정하고 종류·좌표를 저장하며 속도·상태·타이머는 0, sprite는 0xFF로 초기화합니다.

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

사용 예

c
first = entity_create(1, 2, 12);
second = entity_create(2, 6, 12);
if (first == 0xFF || second == 0xFF) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_create: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:32

선언 주석 원문
Returns a reusable slot ID, or 0xFF if no slot is free.

kitaqfc/lib/entity.c:34

c
u8 entity_create(u8 type, s16 x, s16 y)
{
    u8 i;
    i = 0;
    while (i < ENTITY_MAX) {
        if (entity_pool[(__safe_index u8)i].active == 0) {
            entity_pool[(__safe_index u8)i].active = 1;
            entity_pool[(__safe_index u8)i].type = type;
            entity_pool[(__safe_index u8)i].x = x;
            entity_pool[(__safe_index u8)i].y = y;
            entity_pool[(__safe_index u8)i].vx = 0;
            entity_pool[(__safe_index u8)i].vy = 0;
            entity_pool[(__safe_index u8)i].state = 0;
            entity_pool[(__safe_index u8)i].timer = 0;
            entity_pool[(__safe_index u8)i].sprite = 0xFF;
            return i;
        }
        i = (u8)(i + 1);
    }
    return 0xFF;
}
entity_destroy

id의 엔티티 슬롯을 해제합니다. 저장된 데이터는 다시 할당할 때 초기화됩니다.

사용 형식

c
void entity_destroy(u8 id);

매개변수

id

0~ENTITY_MAX-1의 재사용 가능한 슬롯 번호입니다. 범위 밖 ID는 유효하지 않습니다. 잘못된 ID를 해제해도 변화가 없고 조회하면 null을 반환합니다.

반환값

없음(void).

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

해제는 active만 0으로 만듭니다. 다른 필드를 지우거나 스프라이트·에셋을 해제하지 않습니다. 이후 같은 ID와 주소가 다른 엔티티에 재사용될 수 있습니다. 관련 자원은 게임에서 정리하세요.

사용 예

c
entity_destroy(first);
after_free = entity_count_active();
if (entity_get(first) != 0 || after_free != 1) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_destroy: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:35

선언 주석 원문
Release a valid slot without clearing its payload. Reallocation resets the payload;
a saved pointer or ID must not be treated as a persistent entity identity.

kitaqfc/lib/entity.c:58

c
void entity_destroy(u8 id)
{
    if (id >= ENTITY_MAX) return;
    entity_pool[(__safe_index u8)id].active = 0;
}
entity_draw_all

활성 엔티티를 순회하며 각 ID를 그리기 콜백에 전달합니다. 순회 방식은 entity_update_all과 같습니다. 콜백이 그리기 데이터를 제출하지 않으면 스프라이트나 픽셀은 그려지지 않습니다.

사용 형식

c
void entity_draw_all(EntityFn fn);

매개변수

fn

형식이 void fn(u8 id)인 함수 포인터입니다. 슬롯 ID를 받으므로 필드에 접근하려면 entity_get(id)를 사용하세요. null도 허용되며 이때는 호출하지 않습니다.

반환값

없음(void).

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

순회가 슬롯에 도달했을 때 활성 상태를 검사합니다. 콜백이 뒤쪽 슬롯을 비활성화하면 건너뛰고, 활성화하면 이번 순회에서 처리할 수 있습니다. 시작 시점의 활성 목록을 고정한 스냅샷이 아닙니다.

콜백을 공통 PRG 뱅크 0에 두거나, 호출이 끝날 때까지 해당 뱅크가 매핑된 상태를 유지하세요. 함수 포인터는 16비트 CPU 주소만 저장하며 뱅크를 선택하지 않습니다. FC의 함수 매개변수와 지역 변수는 고정된 메모리를 사용하므로 엔티티 순회를 재귀적으로 호출하거나 인터럽트에서 같은 순회에 재진입하면 안 됩니다.

사용 예

c
void demo_draw(u8 id) {
    Entity* entity;
    entity = entity_get(id);
    if (entity != 0) {
        m_put((u8)entity->x, (u8)entity->y, (u8)('A' + entity->type - 1));
        draw_calls++;
    }
}

// From main(), after creating two entities:
draw_calls = 0;
entity_draw_all(demo_draw);
automatic_draws = draw_calls;
if (automatic_draws != 2) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_draw_all: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:43

선언 주석 원문
Call the drawing callback for each active slot, using the same traversal rules as update.
The callback submits drawing data; the pool does not allocate sprites.

kitaqfc/lib/entity.c:88

c
void entity_draw_all(EntityFn fn)
{
    entity_update_all(fn);
}
entity_get

사용 중인 유효 ID의 엔티티 포인터를 반환하며, 그 외에는 null입니다. 해당 저장 공간은 재사용됩니다.

사용 형식

c
Entity* entity_get(u8 id);

매개변수

id

0~ENTITY_MAX-1의 재사용 가능한 슬롯 번호입니다. 범위 밖 ID는 유효하지 않습니다. 잘못된 ID를 해제해도 변화가 없고 조회하면 null을 반환합니다.

반환값

사용 중인 슬롯을 수정할 수 있는 포인터를 반환하며, ID가 잘못됐거나 비활성이면 null입니다. 메모리는 풀 소유입니다. 직접 해제하거나 삭제·재할당을 거치는 영구 식별자로 사용하지 마세요.

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

해제는 active만 0으로 만듭니다. 다른 필드를 지우거나 스프라이트·에셋을 해제하지 않습니다. 이후 같은 ID와 주소가 다른 엔티티에 재사용될 수 있습니다. 관련 자원은 게임에서 정리하세요.

사용 예

c
current_entity = entity_get(first);
if (current_entity != 0) { current_entity->vx = 5; }
else { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_get: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:37

선언 주석 원문
Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.

kitaqfc/lib/entity.c:66

c
Entity* entity_get(u8 id)
{
    if (id >= ENTITY_MAX) return 0;
    if (entity_pool[(__safe_index u8)id].active == 0) return 0;
    return &entity_pool[(__safe_index u8)id];
}
entity_init

엔티티 풀의 모든 슬롯을 초기화합니다.

사용 형식

c
void entity_init(void);

반환값

없음(void).

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

모든 슬롯의 active,type,x,y,vx,vy,state,timer를 0, sprite를 0xFF로 설정합니다. 하드웨어 스프라이트를 숨기거나 해제하지는 않으므로 렌더링 자원은 별도로 정리하세요.

사용 예

c
entity_init();
if (entity_count_active() != 0) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:30

선언 주석 원문
Reset every slot before the game starts using the pool.

kitaqfc/lib/entity.c:8

c
void entity_init(void)
{
    entity_clear_all();
}
entity_update_all

ID 오름차순으로 활성 슬롯을 살펴보고 각각 fn(id)를 한 번 호출합니다. 이동·충돌·타이머 로직은 콜백에 작성하며 풀은 이 로직을 제공하지 않습니다. 콜백이 null이면 아무것도 하지 않습니다.

사용 형식

c
void entity_update_all(EntityFn fn);

매개변수

fn

형식이 void fn(u8 id)인 함수 포인터입니다. 슬롯 ID를 받으므로 필드에 접근하려면 entity_get(id)를 사용하세요. null도 허용되며 이때는 호출하지 않습니다.

반환값

없음(void).

사용 조건과 제한

풀은 고정 배열이며 힙을 동적으로 할당하지 않습니다. ENTITY_MAX의 기본값은 16이고 범위는 1~255입니다. 모든 컴파일 단위에서 같은 값으로 정의하세요. ID는 0부터 시작하며 0xFF는 실패를 나타냅니다.

순회가 슬롯에 도달했을 때 활성 상태를 검사합니다. 콜백이 뒤쪽 슬롯을 비활성화하면 건너뛰고, 활성화하면 이번 순회에서 처리할 수 있습니다. 시작 시점의 활성 목록을 고정한 스냅샷이 아닙니다.

콜백을 공통 PRG 뱅크 0에 두거나, 호출이 끝날 때까지 해당 뱅크가 매핑된 상태를 유지하세요. 함수 포인터는 16비트 CPU 주소만 저장하며 뱅크를 선택하지 않습니다. FC의 함수 매개변수와 지역 변수는 고정된 메모리를 사용하므로 엔티티 순회를 재귀적으로 호출하거나 인터럽트에서 같은 순회에 재진입하면 안 됩니다.

사용 예

c
void demo_update(u8 id) {
    Entity* entity;
    entity = entity_get(id);
    if (entity != 0) { entity->x = entity->x + 1; update_calls++; }
}

// From main(), after creating two entities:
update_calls = 0;
entity_update_all(demo_update);
automatic_updates = update_calls;
if (automatic_updates != 2) { failures++; }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전체 프로그램은 재사용되는 풀 슬롯의 수명 주기를 보여 줍니다. 숫자는 라벨과 함께 읽으세요. 두 개를 할당하면 CREATED 002, 첫 번째를 해제하면 AFTER FREE 001, 가장 작은 빈 ID를 재사용하면 REUSED ID 000, 전체를 해제하면 AFTER CLEAR 000입니다. 해제 후 조회가 null인지, 재할당 시 필드가 초기화되는지도 검사합니다. FAILED CHECKS 000은 기대값과 일치했다는 뜻입니다.

FC 순회는 활성 엔티티 두 개에 각각 콜백을 호출하므로 UPDATE CALLS 002, DRAW CALLS 002가 됩니다. 예제의 갱신 콜백은 둘을 오른쪽으로 한 타일 이동합니다. 그리기 콜백은 두 객체를 나타내는 A를 타일 (3,12), B를 (7,12)에 놓습니다. 콜백이 그린 배경 글자 타일이며 자동 할당된 스프라이트가 아닙니다.

entity_update_all: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\entity.c .\kitaq-docs\samples\api-examples\fc\entity_pool.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\entity_pool.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm
구현과 소스

kitaqfc/lib/entity.h:40

선언 주석 원문
Call fn(id) for each active slot in ascending order. A null callback does nothing.
Changes to later slots take effect during the same traversal. Do not reenter this traversal.

kitaqfc/lib/entity.c:75

c
void entity_update_all(EntityFn fn)
{
    u8 i;
    if (fn == 0) return;
    i = 0;
    while (i < ENTITY_MAX) {
        if (entity_pool[(__safe_index u8)i].active != 0) fn(i);
        i = (u8)(i + 1);
    }
}

FDS 파일과 고정 크기 저장 슬롯

fds_file.h는 게임 버퍼로 파일을 불러오고 디스크의 예약된 저장 슬롯에 쓰는 기능을 제공합니다. fds_save.h도 같은 선언을 포함합니다. 오버레이 코드를 호출할 때는 fds_overlay.h를 사용하세요.

__fds_load_file() / __fds_save_file()

fds_file_io.c fds_file_io_manifest.json fds_file_io_payload.bin

fds_sound.h

64단계 파형, 변조 테이블, 음높이, 음량, 엔벌로프용 컴파일러 내장 함수를 선언합니다. 구현은 KITAQFC가 생성합니다.

--mapper=fds로 .fds 이미지를 빌드하세요. 이 예제는 디스크 입출력 없이 사운드를 다룹니다. 녹음은 KUROSAKI의 RAM 직접 부팅과 오디오 모델을 사용합니다. 빈 테스트 펌웨어는 BIOS 루틴을 구현하지 않으며 배포하지 않습니다. 일반 디스크 부팅과 실기에는 적절한 부팅 환경이 필요합니다.

함수 레퍼런스: __fds_sound_enable, __fds_wave_load, __fds_mod_load, __fds_freq_set, __fds_volume_set, __fds_env_set

fixed.h — Q8.8 고정소수점 연산과 사각형

Q8.8 연산은 정수로 소수 좌표와 속도를 표현하며, 사각형 및 점의 충돌 검사도 제공합니다. 각 연산의 정밀도와 범위 제한을 확인하세요.

fix_div

피연산자의 정밀도를 줄인 뒤 Q8.8 나눗셈을 수행합니다.

사용 형식

c
fix8 fix_div(fix8 a, fix8 b);

매개변수

a

Q8.8 형식의 왼쪽 피연산자입니다.

b

Q8.8 형식의 오른쪽 피연산자입니다.

반환값

Q8.8 몫을 반환합니다. b == 0이면 0을 반환합니다.

사용 조건과 제한

계산식은 ((a >> 4) * 256) / (b >> 4)입니다. 0이 아닌 제수는 비트 이동 후에도 0이 아니어야 하므로 양의 원시값 1~15를 전달하면 안 됩니다. 배율을 적용한 분자도 16비트 안에 들어가야 합니다.

사용 예

c
result[3]=fix_div(768,512);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

3.0을 2.0으로 나누면 1.5(원시값 384)가 됩니다. 제수가 정확히 0인 경우의 반환값도 별도로 검사합니다.

fix_div: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:35

선언 주석 원문
Scale the reduced-precision quotient back to 8.8 and return zero for b == 0.
Callers must also ensure (b >> 4) is nonzero; the explicit check only tests b.
The intermediate product must fit the compiler's integer arithmetic.

kitaqfc/lib/fixed.c:26

c
fix8 fix_div(fix8 a, fix8 b)
{
    if (b == 0) return 0;
    return (fix8)(((a >> 4) * 256) / (b >> 4));
}
fix_from_int

정수 좌표나 속도를 Q8.8로 변환하여 이후 계산에서 소수부를 유지할 수 있게 합니다.

사용 형식

c
fix8 fix_from_int(s16 n);

매개변수

n

변환할 정수이며 범위는 −128~127입니다.

반환값

원시값 n × 256을 반환합니다.

사용 조건과 제한

fix8은 부호 있는 Q8.8 형식입니다. 원시값 256이 1.0을 나타냅니다. 중간값과 결과가 16비트 범위를 벗어나지 않게 하세요.

사용 예

c
result[0]=fix_from_int(-3);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이 예제는 −3을 변환합니다. 화면의 −00768은 Q8.8 원시 결과값 −768입니다.

fix_from_int: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:26

선언 주석 원문
Convert an integer to signed 8.8 fixed point by moving it into the high byte.
The caller must keep the result within the representable 16-bit range.

kitaqfc/lib/fixed.c:5

c
fix8 fix_from_int(s16 n)
{
    return (fix8)(n << 8);
}
fix_lerp

8비트 가중치를 사용하여 한 Q8.8 값에서 다른 값 쪽으로 보간합니다.

사용 형식

c
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8);

매개변수

a

Q8.8 형식의 왼쪽 피연산자입니다.

b

Q8.8 형식의 오른쪽 피연산자입니다.

t_q8

보간 가중치이며 범위는 0~255입니다.

반환값

근사 보간 결과를 Q8.8로 반환합니다.

사용 조건과 제한

차이값과 가중치를 각각 오른쪽으로 4비트 이동합니다. 가중치는 16단계가 되므로 255를 주어도 끝값에 도달하지 않을 수 있습니다. b - a와 최종 결과를 16비트 범위 안에 두세요.

사용 예

c
result[4]=fix_lerp(0,1024,128);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

0과 4.0 사이에서 가중치 128을 쓰면 2.0(원시값 512)이 됩니다. 가중치 255에서는 3.75(원시값 960)가 되어 끝값에 도달하지 않음을 보여 줍니다.

fix_lerp: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:38

선언 주석 원문
Interpolate toward b using a quantized Q0.8 weight. Both shifts discard low
bits, so small changes may vanish and a weight of 255 need not reach b.

kitaqfc/lib/fixed.c:34

c
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8)
{
    fix8 d;
    // The endpoint difference must fit s16 before the reduced-precision interpolation.
    d = (fix8)(b - a);
    return (fix8)(a + (fix8)((d >> 4) * (s16)(t_q8 >> 4)));
}
fix_mul

연산 비용이 낮은 근사 방식으로 Q8.8 값 두 개를 곱합니다.

사용 형식

c
fix8 fix_mul(fix8 a, fix8 b);

매개변수

a

Q8.8 형식의 왼쪽 피연산자입니다.

b

Q8.8 형식의 오른쪽 피연산자입니다.

반환값

곱을 Q8.8 형식으로 반환합니다.

사용 조건과 제한

곱셈 전에 각 피연산자를 오른쪽으로 4비트 이동하여 하위 4비트를 버립니다. 따라서 완전한 16×16비트 곱셈보다 정밀도가 낮습니다. fix8은 부호 있는 Q8.8이며 원시값 256이 1.0을 나타냅니다. 중간값과 결과는 16비트 범위 안에 두세요.

사용 예

c
result[2]=fix_mul(384,512);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

1.5와 2.0을 곱하면 3.0(원시값 768)이 됩니다. 원시값 271끼리 곱했을 때 256이 나오는 양자화 사례도 검사합니다.

fix_mul: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:31

선언 주석 원문
Approximate 8.8 multiplication by discarding four low bits from each operand
before multiplying. This trades precision for a smaller intermediate product.

kitaqfc/lib/fixed.c:18

c
fix8 fix_mul(fix8 a, fix8 b)
{
    return (fix8)((a >> 4) * (b >> 4));
}
fix_to_int

Q8.8의 소수 비트를 버려 정수 좌표를 추출합니다. 음수는 음의 무한대 방향으로 내림합니다.

사용 형식

c
s16 fix_to_int(fix8 x);

매개변수

x

정수로 변환할 Q8.8 값입니다.

반환값

부호 있는 정수를 반환합니다.

사용 조건과 제한

fix8은 부호 있는 Q8.8 형식입니다. 원시값 256이 1.0을 나타냅니다. 중간값과 결과가 16비트 범위를 벗어나지 않게 하세요.

사용 예

c
result[1]=fix_to_int(-384);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

원시값 −384(−1.5)를 전달하면 −2가 되어 −00002로 표시됩니다. 경계값 검사에서는 원시값 −1도 −1이 되는지 확인합니다.

fix_to_int: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:28

선언 주석 원문
Discard the eight fractional bits using a signed right shift.

kitaqfc/lib/fixed.c:11

c
s16 fix_to_int(fix8 x)
{
    return (s16)(x >> 8);
}
kq_abs_s16

부호 있는 16비트 값의 절댓값을 구합니다.

사용 형식

c
s16 kq_abs_s16(s16 v);

매개변수

v

절댓값을 구할 부호 있는 16비트 값입니다.

반환값

−32768의 절댓값처럼 표현할 수 없는 경우를 제외하고, 0 이상의 절댓값을 반환합니다.

사용 조건과 제한

양의 값 32768은 s16에 들어가지 않습니다. 0 이상의 결과가 필요하다면 입력에서 −32768을 제외하세요.

사용 예

c
result[0]=kq_abs_s16(-123);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 −123을 123으로 바꿉니다. 좌표 차이의 크기를 구할 때와 같은 사용법입니다.

kq_abs_s16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:41

선언 주석 원문
Return the magnitude in s16. The most-negative s16 has no positive s16
representation and must be excluded by callers needing a nonnegative result.

kitaqfc/lib/fixed.c:44

c
s16 kq_abs_s16(s16 v)
{
    if (v < 0) return (s16)(0 - v);
    return v;
}
kq_clamp_s16

좌표나 속도를 양 끝값을 포함한 구간 안으로 제한합니다.

사용 형식

c
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi);

매개변수

v

제한할 부호 있는 값입니다.

lo

포함되는 하한값입니다.

hi

포함되는 상한값입니다.

반환값

구간보다 작으면 lo, 크면 hi, 구간 안이면 v를 반환합니다.

사용 조건과 제한

lo <= hi를 만족하는 유효한 구간을 지정하세요.

사용 예

c
result[3]=kq_clamp_s16(25,-10,10);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

25를 −10~10으로 제한하면 10이 됩니다. 하한에서 −11→−10, 구간 안에서 3→3인 경우도 검사합니다.

kq_clamp_s16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:47

선언 주석 원문
Clamp to an inclusive interval; callers must provide lo <= hi.

kitaqfc/lib/fixed.c:65

c
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi)
{
    if (v < lo) return lo;
    if (v > hi) return hi;
    return v;
}
kq_max_s16

부호 있는 16비트 값 두 개 중 큰 값을 선택합니다.

사용 형식

c
s16 kq_max_s16(s16 a, s16 b);

매개변수

a

비교할 첫 번째 부호 있는 값입니다.

b

비교할 두 번째 부호 있는 값입니다.

반환값

선택한 피연산자를 반환합니다.

사용 조건과 제한

부호를 고려하여 비교합니다. 두 피연산자가 같으면 그 값을 반환하며, 입력값은 수정하지 않습니다.

사용 예

c
result[2]=kq_max_s16(-7,4);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

−7과 4를 비교하면 4가 나옵니다. 음수 결과에는 마이너스 기호가 표시됩니다.

kq_max_s16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:45

선언 주석 원문
Select the larger signed value.

kitaqfc/lib/fixed.c:58

c
s16 kq_max_s16(s16 a, s16 b)
{
    if (a > b) return a;
    return b;
}
kq_min_s16

부호 있는 16비트 값 두 개 중 작은 값을 선택합니다.

사용 형식

c
s16 kq_min_s16(s16 a, s16 b);

매개변수

a

비교할 첫 번째 부호 있는 값입니다.

b

비교할 두 번째 부호 있는 값입니다.

반환값

선택한 피연산자를 반환합니다.

사용 조건과 제한

부호를 고려하여 비교합니다. 두 피연산자가 같으면 그 값을 반환하며, 입력값은 수정하지 않습니다.

사용 예

c
result[1]=kq_min_s16(-7,4);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

−7과 4를 비교하면 -7이 나옵니다. 음수 결과에는 마이너스 기호가 표시됩니다.

kq_min_s16: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_math_limits.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_math_limits.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:43

선언 주석 원문
Select the smaller signed value.

kitaqfc/lib/fixed.c:51

c
s16 kq_min_s16(s16 a, s16 b)
{
    if (a < b) return a;
    return b;
}
kq_point_in_rect

점이 사각형 안에 있는지 검사하며, 왼쪽과 위쪽 경계는 포함합니다.

사용 형식

c
u8 kq_point_in_rect(s16 x, s16 y, KQRect r);

매개변수

x

사각형과 같은 좌표계에서의 점 X 좌표입니다.

y

점의 Y 좌표입니다.

r

검사할 사각형이며 값으로 전달합니다.

반환값

안에 있으면 1, 밖에 있으면 0을 반환합니다.

사용 조건과 제한

너비와 높이는 양수여야 하며, 좌표와 크기의 합을 s16으로 표현할 수 있어야 합니다. 오른쪽과 아래쪽 경계는 포함하지 않습니다. 면적이 0인 사각형을 명시적으로 거부하지는 않습니다.

사용 예

c
result[1]=kq_point_in_rect(9,1,a);
for(gy=0;gy<11;gy++){for(gx=0;gx<15;gx++){
inside_a=kq_point_in_rect(gx,gy,a);inside_b=kq_point_in_rect(gx,gy,b);
tile=0;if(inside_a){tile=65;result[3]=result[3]+1;}if(inside_b)tile=66;if(inside_a && inside_b){tile=79;result[4]=result[4]+1;}
m_put(1+gx,2+gy,tile);__vramq_commit();__vramq_exec();
}}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

좌표 1단위는 타일 하나입니다. (1,1)의 사각형 A와 (5,4)의 사각형 B는 모두 8×6 크기입니다. A만 차지하는 칸은 A, B만 차지하는 칸은 B로 표시하고, 4×3 겹침 영역의 12칸은 O로 표시합니다. A의 오른쪽 가장자리 x=9는 포함하지 않습니다. CGB에서는 A가 빨간색, B가 파란색, O가 초록색입니다.

kq_point_in_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:54

선언 주석 원문
Test a half-open rectangle: left/top edges are included, right/bottom excluded.
The coordinate-plus-size sums must remain representable as s16.

kitaqfc/lib/fixed.c:86

c
u8 kq_point_in_rect(s16 x, s16 y, KQRect r)
{
    if (x < r.x) return 0;
    if (y < r.y) return 0;
    if (x >= (s16)(r.x + r.w)) return 0;
    if (y >= (s16)(r.y + r.h)) return 0;
    return 1;
}
kq_rect_intersect

축에 정렬된 두 사각형이 면적을 가지며 겹치는지 검사합니다.

사용 형식

c
u8 kq_rect_intersect(KQRect a, KQRect b);

매개변수

a

값으로 전달하는 첫 번째 사각형입니다. x, y는 왼쪽 위 모서리, w, h는 너비와 높이입니다.

b

A와 같은 좌표계를 사용하는 두 번째 사각형입니다.

반환값

겹치면 1, 떨어져 있거나 가장자리만 닿으면 0을 반환합니다.

사용 조건과 제한

너비와 높이는 양수여야 하며, 좌표와 크기의 합을 s16으로 표현할 수 있어야 합니다. 오른쪽과 아래쪽 경계는 포함하지 않습니다. 면적이 0인 사각형을 명시적으로 거부하지는 않습니다.

사용 예

c
result[0]=kq_rect_intersect(a,b);result[2]=kq_rect_intersect(a,touch);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

좌표 1단위는 타일 하나입니다. (1,1)의 사각형 A와 (5,4)의 사각형 B는 모두 8×6 크기입니다. A만 차지하는 칸은 A, B만 차지하는 칸은 B로 표시하고, 4×3 겹침 영역의 12칸은 O로 표시합니다. A의 오른쪽 가장자리 x=9는 포함하지 않습니다. CGB에서는 A가 빨간색, B가 파란색, O가 초록색입니다.

kq_rect_intersect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch_geometry.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_geometry.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/fixed.h:51

선언 주석 원문
Test overlap using exclusive right/bottom edges; touching edges do not collide.
Require positive dimensions and edge sums that fit s16; zero-sized rectangles
are not explicitly rejected and may be reported as overlapping.

kitaqfc/lib/fixed.c:75

c
u8 kq_rect_intersect(KQRect a, KQRect b)
{
    if ((s16)(a.x + a.w) <= b.x) return 0;
    if ((s16)(b.x + b.w) <= a.x) return 0;
    if ((s16)(a.y + a.h) <= b.y) return 0;
    if ((s16)(b.y + b.h) <= a.y) return 0;
    return 1;
}

input.h — 버튼 상태, 누름, 놓음, 반복 입력

연속 이동에는 input_down, 한 번 실행할 동작에는 input_pressed, 버튼을 뗄 때의 정지에는 input_released, 메뉴 반복에는 input_repeat를 사용합니다. 한 번 초기화하고 입력 틱마다 한 번 갱신한 뒤 필요한 만큼 조회하세요. btn_down, btn_pressed, btn_released, btn_repeat는 네 조회 함수의 별칭입니다.

__pad_read1_safe로 컨트롤러 1을 읽고 NES 비트 순서를 공통 BTN_* 형식으로 변환합니다. 이전 마스크를 저장하고 누름·뗌·반복 마스크를 계산합니다. 컨트롤러 2는 포함하지 않습니다. 입력 틱마다 한 번 호출하세요.

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

반복 시간은 밀리초가 아니라 갱신 횟수입니다. 새로 누르면 즉시 출력하며 기본값 INPUT_REPEAT_DELAY=18, INPUT_REPEAT_RATE=5에서는 19번 뒤, 이후 6번마다 펄스가 나옵니다. 버튼별 카운터는 독립적입니다. 값을 바꾸려면 input.c를 컴파일할 때도 같은 매크로 값을 적용해야 합니다. 호출자 헤더만 바꿔서는 라이브러리 설정이 바뀌지 않습니다.

btn_down

btn_down은 input_down의 별칭입니다. 선택한 버튼 중 하나라도 현재 눌려 있는지 확인합니다.

mask에 지정한 버튼 중 현재 누르고 있는 버튼이 있는지 반환합니다.

사용 형식

c
u8 btn_down(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

input_down

사용 예

c
if (btn_down(BTN_A)!=0) held_ticks++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

btn_down: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:53

c
#define btn_down input_down

kitaqfc/lib/input.c:85

c
u8 input_down(u8 mask) { return (u8)((kq_input_keys & mask) != 0); }
btn_pressed

btn_pressed는 input_pressed의 별칭입니다. 최근 입력 갱신에서 선택한 버튼 중 하나라도 새로 눌렸는지 확인합니다.

최근 입력 갱신에서 마스크에 해당하는 버튼이 눌리지 않은 상태에서 눌린 상태로 바뀌었는지 반환합니다.

사용 형식

c
u8 btn_pressed(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

input_pressed

사용 예

c
if (btn_pressed(BTN_A)!=0) {
        press_count++; seen_press=1;
        first_down=btn_down(BTN_A); first_press=btn_pressed(BTN_A);
        first_repeat=btn_repeat(BTN_A);
        first_current=input_current(); // Only A held: BTN_A, or decimal 16.
        first_previous=input_previous(); // No buttons in the preceding sample: 0.
        if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
        if (first_current!=BTN_A || first_previous!=0) failures++;
        if (btn_down(BTN_A|BTN_B)!=1 || btn_down(0)!=0) failures++;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

btn_pressed: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:54

c
#define btn_pressed input_pressed

kitaqfc/lib/input.c:87

c
u8 input_pressed(u8 mask) { return (u8)((kq_input_press_keys & mask) != 0); }
btn_released

btn_released는 input_released의 별칭입니다. 최근 입력 갱신에서 선택한 버튼 중 하나라도 놓였는지 확인합니다.

최근 입력 갱신에서 마스크에 해당하는 버튼이 눌린 상태에서 놓인 상태로 바뀌었는지 반환합니다.

사용 형식

c
u8 btn_released(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

input_released

사용 예

c
if (btn_released(BTN_A)!=0) {
        release_result=btn_released(BTN_A);
        last_current=input_current(); last_previous=input_previous();
        if (btn_down(BTN_A)!=0 || btn_pressed(BTN_A)!=0 || btn_repeat(BTN_A)!=0) failures++;
        break;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

btn_released: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:55

c
#define btn_released input_released

kitaqfc/lib/input.c:89

c
u8 input_released(u8 mask) { return (u8)((kq_input_release_keys & mask) != 0); }
btn_repeat

btn_repeat는 input_repeat의 별칭입니다. 최초 누름이나 누르고 있는 동안 정해진 시점에 발생하는 반복 신호를 확인합니다.

마스크에 해당하는 버튼의 첫 누름 또는 반복 입력 발생 여부를 반환합니다.

사용 형식

c
u8 btn_repeat(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

반복 시간은 밀리초가 아니라 갱신 횟수입니다. 새로 누르면 즉시 출력하며 기본값 INPUT_REPEAT_DELAY=18, INPUT_REPEAT_RATE=5에서는 19번 뒤, 이후 6번마다 펄스가 나옵니다. 버튼별 카운터는 독립적입니다. 값을 바꾸려면 input.c를 컴파일할 때도 같은 매크로 값을 적용해야 합니다. 호출자 헤더만 바꿔서는 라이브러리 설정이 바뀌지 않습니다.

input_repeat

사용 예

c
if (btn_repeat(BTN_A)!=0) {
        // The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
        if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
        if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
        previous_pulse=held_ticks; repeat_count++;
        if (btn_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

btn_repeat: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_aliases.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:56

c
#define btn_repeat input_repeat

kitaqfc/lib/input.c:91

c
u8 input_repeat(u8 mask) { return (u8)((kq_input_repeat_keys & mask) != 0); }
input_current

최근 입력 갱신에서 얻은 전체 버튼 유지 마스크를 반환합니다.

사용 형식

c
u8 input_current(void);

반환값

공통 BTN_* 형식의 8비트 마스크입니다. 여러 버튼 비트가 동시에 설정될 수 있으며 0은 해당 버튼이 없음을 뜻합니다.

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

사용 예

c
first_current=input_current(); // Only A held: BTN_A, or decimal 16.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_current: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:49

선언 주석 원문
Read the complete translated held-button mask.

kitaqfc/lib/input.c:93

c
u8 input_current(void) { return kq_input_keys; }
input_down

mask에 지정한 버튼 중 현재 누르고 있는 버튼이 있는지 반환합니다.

사용 형식

c
u8 input_down(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

사용 예

c
if (input_down(BTN_A)!=0) held_ticks++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_down: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:41

선언 주석 원문
Return true when any masked logical button is held.

kitaqfc/lib/input.c:85

c
u8 input_down(u8 mask) { return (u8)((kq_input_keys & mask) != 0); }
input_init

현재·이전·누름·뗌·반복 마스크를 모두 0으로 만들고 8개 반복 타이머를 INPUT_REPEAT_DELAY로 초기화합니다. 컨트롤러를 읽지는 않습니다. 다음 첫 갱신에서 눌려 있는 버튼은 새로 누른 것으로 처리합니다.

사용 형식

c
void input_init(void);

반환값

없음(void).

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

반복 시간은 밀리초가 아니라 갱신 횟수입니다. 새로 누르면 즉시 출력하며 기본값 INPUT_REPEAT_DELAY=18, INPUT_REPEAT_RATE=5에서는 19번 뒤, 이후 6번마다 펄스가 나옵니다. 버튼별 카운터는 독립적입니다. 값을 바꾸려면 input.c를 컴파일할 때도 같은 매크로 값을 적용해야 합니다. 호출자 헤더만 바꿔서는 라이브러리 설정이 바뀌지 않습니다.

사용 예

c
input_init();
if (input_current()!=0 || input_previous()!=0) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:35

선언 주석 원문
Clear held/edge/repeat masks and reset each independent repeat countdown.

kitaqfc/lib/input.c:34

c
void input_init(void)
{
    u8 i;
    kq_input_prev_keys = 0;
    kq_input_keys = 0;
    kq_input_press_keys = 0;
    kq_input_release_keys = 0;
    kq_input_repeat_keys = 0;
    i = 0;
    while (i < 8) {
        input_repeat_timer[i] = INPUT_REPEAT_DELAY;
        i = (u8)(i + 1);
    }
}
input_pressed

최근 입력 갱신에서 마스크에 해당하는 버튼이 눌리지 않은 상태에서 눌린 상태로 바뀌었는지 반환합니다.

사용 형식

c
u8 input_pressed(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

사용 예

c
if (input_pressed(BTN_A)!=0) {
        press_count++; seen_press=1;
        first_down=input_down(BTN_A); first_press=input_pressed(BTN_A);
        first_repeat=input_repeat(BTN_A);
        first_current=input_current(); // Only A held: BTN_A, or decimal 16.
        first_previous=input_previous(); // No buttons in the preceding sample: 0.
        if (first_down!=1 || first_press!=1 || first_repeat!=1) failures++;
        if (first_current!=BTN_A || first_previous!=0) failures++;
        if (input_down(BTN_A|BTN_B)!=1 || input_down(0)!=0) failures++;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_pressed: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:43

선언 주석 원문
Return true when any masked button became pressed in the latest input update.

kitaqfc/lib/input.c:87

c
u8 input_pressed(u8 mask) { return (u8)((kq_input_press_keys & mask) != 0); }
input_previous

최근 입력 갱신 직전에 저장된 버튼 유지 마스크를 반환합니다.

사용 형식

c
u8 input_previous(void);

반환값

공통 BTN_* 형식의 8비트 마스크입니다. 여러 버튼 비트가 동시에 설정될 수 있으며 0은 해당 버튼이 없음을 뜻합니다.

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

사용 예

c
first_previous=input_previous(); // No buttons in the preceding sample: 0.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_previous: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:51

선언 주석 원문
Read the translated held-button mask from before the latest update.

kitaqfc/lib/input.c:95

c
u8 input_previous(void) { return kq_input_prev_keys; }
input_released

최근 입력 갱신에서 마스크에 해당하는 버튼이 눌린 상태에서 놓인 상태로 바뀌었는지 반환합니다.

사용 형식

c
u8 input_released(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

사용 예

c
if (input_released(BTN_A)!=0) {
        release_result=input_released(BTN_A);
        last_current=input_current(); last_previous=input_previous();
        if (input_down(BTN_A)!=0 || input_pressed(BTN_A)!=0 || input_repeat(BTN_A)!=0) failures++;
        break;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_released: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:45

선언 주석 원문
Return true when any masked button became released in the latest update.

kitaqfc/lib/input.c:89

c
u8 input_released(u8 mask) { return (u8)((kq_input_release_keys & mask) != 0); }
input_repeat

마스크에 해당하는 버튼의 첫 누름 또는 반복 입력 발생 여부를 반환합니다.

사용 형식

c
u8 input_repeat(u8 mask);

매개변수

mask

BTN_* 하나 또는 여러 값을 |로 결합한 마스크입니다. 선택한 버튼 중 하나라도 해당하면 참이므로 BTN_A | BTN_B가 두 버튼의 동시 누름을 요구하지는 않습니다. 마스크 0은 항상 0을 반환합니다. 모두 눌렸는지는 (input_current() & mask) == mask로 확인하세요.

반환값

선택한 버튼 중 하나라도 조회 조건에 맞으면 정확히 1, 아니면 0입니다. 반환값은 버튼 비트 마스크가 아닙니다。

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

조회 함수는 마지막 input_update()가 저장한 값을 읽습니다. 다시 폴링하거나 에지를 소비하거나 반복 타이머를 진행하지 않습니다. 같은 틱에서 여러 번 조회해도 결과는 같습니다. 버튼을 누른 채 input_update()를 두 번 호출하면 두 번째 호출에서는 새 누름 에지가 사라집니다.

반복 시간은 밀리초가 아니라 갱신 횟수입니다. 새로 누르면 즉시 출력하며 기본값 INPUT_REPEAT_DELAY=18, INPUT_REPEAT_RATE=5에서는 19번 뒤, 이후 6번마다 펄스가 나옵니다. 버튼별 카운터는 독립적입니다. 값을 바꾸려면 input.c를 컴파일할 때도 같은 매크로 값을 적용해야 합니다. 호출자 헤더만 바꿔서는 라이브러리 설정이 바뀌지 않습니다.

사용 예

c
if (input_repeat(BTN_A)!=0) {
        // The initial press is pulse 1. Defaults then wait 19 updates, followed by 6.
        if (repeat_count==1 && held_ticks-previous_pulse!=19) failures++;
        if (repeat_count>1 && held_ticks-previous_pulse!=6) failures++;
        previous_pulse=held_ticks; repeat_count++;
        if (input_repeat(BTN_A)!=1) failures++; // Reading again does not consume it.
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_repeat: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:47

선언 주석 원문
Return true for a masked initial press or scheduled repeat pulse.

kitaqfc/lib/input.c:91

c
u8 input_repeat(u8 mask) { return (u8)((kq_input_repeat_keys & mask) != 0); }
input_update

__pad_read1_safe로 컨트롤러 1을 읽고 NES 비트 순서를 공통 BTN_* 형식으로 변환합니다. 이전 마스크를 저장하고 누름·뗌·반복 마스크를 계산합니다. 컨트롤러 2는 포함하지 않습니다. 입력 틱마다 한 번 호출하세요.

사용 형식

c
void input_update(void);

반환값

없음(void).

사용 조건과 제한

공통 마스크는 BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80입니다. 1인 비트가 눌림을 뜻합니다. GB와 FC의 input_*는 이 형식을 쓰지만 FC의 원시 패드 함수는 비트 순서가 다릅니다.

반복 시간은 밀리초가 아니라 갱신 횟수입니다. 새로 누르면 즉시 출력하며 기본값 INPUT_REPEAT_DELAY=18, INPUT_REPEAT_RATE=5에서는 19번 뒤, 이후 6번마다 펄스가 나옵니다. 버튼별 카운터는 독립적입니다. 값을 바꾸려면 input.c를 컴파일할 때도 같은 매크로 값을 적용해야 합니다. 호출자 헤더만 바꿔서는 라이브러리 설정이 바뀌지 않습니다.

safe 읽기는 전체 버튼을 두 번씩 읽어 두 마스크가 같아질 때까지 반복합니다. 매번 $4016에 스트로브를 보내 두 컨트롤러의 직렬 읽기 위치를 처음으로 돌립니다. 재시도 횟수 제한은 없습니다. 이 일치 확인만으로 모든 DMC·주변기기 조합의 호환성이 입증되지는 않습니다.

사용 예

c
input_update(); // One update per input tick, followed by any number of queries.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

버튼을 누르고 있는 상태, 처음 누른 순간, 뗀 순간, 자동 반복의 차이를 보여 주는 프로그램입니다. A를 40회 갱신 동안 누른 뒤 놓습니다. FIRST DOWN, FIRST PRESS, FIRST REPEAT는 1이고 FIRST CURRENT는 16입니다. HELD TICKS는 40, PRESS COUNT는 1, REPEAT COUNT는 5입니다. 버튼을 놓으면 RELEASE는 1, LAST CURRENT는 0, LAST PREVIOUS는 16이 됩니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 횟수도 달라지며, 프로그램은 실제로 관측한 시간을 기준으로 반복 간격을 검사합니다.

input_update: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\input.c .\kitaq-docs\samples\api-examples\fc\input_edges.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_edges.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input.h:39

선언 주석 원문
Read controller 1 through the safe intrinsic, translate its bit order and
derive press/release edges. Repeat delays count calls to this function, so
call it once per intended input tick.

kitaqfc/lib/input.c:52

c
void input_update(void)
{
    u8 i;
    u8 raw;

    kq_input_prev_keys = kq_input_keys;
    raw = __pad_read1_safe();
    kq_input_keys = input_translate_pad(raw);
    kq_input_press_keys = (u8)(kq_input_keys & (u8)(kq_input_keys ^ kq_input_prev_keys));
    kq_input_release_keys = (u8)(kq_input_prev_keys & (u8)(kq_input_keys ^ kq_input_prev_keys));
    kq_input_repeat_keys = kq_input_press_keys;

    i = 0;
    while (i < 8) {
        u8 mask;
        mask = input_mask_for_index(i);
        if ((kq_input_keys & mask) != 0) {
            if ((kq_input_press_keys & mask) != 0) {
                input_repeat_timer[i] = INPUT_REPEAT_DELAY;
            } else if (input_repeat_timer[i] != 0) {
                input_repeat_timer[i] = (u8)(input_repeat_timer[i] - 1);
            } else {
                kq_input_repeat_keys = (u8)(kq_input_repeat_keys | mask);
                input_repeat_timer[i] = INPUT_REPEAT_RATE;
            }
        } else {
            input_repeat_timer[i] = INPUT_REPEAT_DELAY;
        }
        i = (u8)(i + 1);
    }
}

input_repeat.h — 길게 누를 때의 반복 입력

pad.c와 함께 사용하여 컨트롤러 1의 버튼마다 독립적인 반복 카운터를 추가합니다. 실행 중 대기 시간과 간격을 설정하고 패드를 폴링한 뒤 틱마다 반복 상태를 한 번 진행하세요. 누름·뗌·눌림 조회는 패드 저장값을 사용하며 반복 조회만 nes_pad_repeat_step() 결과에 의존합니다.

첫 카운트다운 값으로 0~255입니다. 새 누름의 즉시 펄스 다음 펄스는 delay + 1번의 반복 갱신 뒤에 나옵니다. 단위는 nes_pad_repeat_step() 호출 횟수이며, 프레임마다 정확히 한 번 호출할 때만 프레임 수와 같습니다.

이후 펄스 사이의 카운트다운 값으로 0~255입니다. 실제 간격은 interval + 1번의 반복 갱신입니다. 0이면 첫 대기 이후 누르고 있는 매 단계에서 출력합니다. 여덟 버튼의 카운터는 독립적입니다.

nes_pad_held

컨트롤러 1의 마지막 눌림 마스크를 추립니다. 선택한 버튼을 누르는 동안 계속 수행할 동작에 씁니다. 저장된 상태만 읽으며 하드웨어를 다시 폴링하지 않습니다.

사용 형식

c
unsigned char nes_pad_held(unsigned char mask);

매개변수

mask

NES 원시 비트 순서로 남길 버튼을 지정하며 필요하면 |로 결합합니다. 0은 아무것도 선택하지 않습니다. 컨트롤러 포트 번호가 아니라 비트 필터입니다.

반환값

저장된 마스크에서 해당하는 비트를 원래 위치 그대로 반환합니다. 1 이외의 값일 수 있으며 0은 해당 버튼이 없음을 뜻합니다. 읽어도 이벤트는 소비되지 않습니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
if (nes_pad_held(0x02)!=0) held_ticks++; // B is bit 1 in NES raw format.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_held: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:28

선언 주석 원문
Return the requested currently-held bits in the NES pad layout.

kitaqfc/lib/input_repeat.c:103

c
unsigned char nes_pad_held(unsigned char mask)
{
    return (unsigned char)(nes_pad1_cur & mask);
}
nes_pad_release

컨트롤러 1의 마지막 뗌 마스크를 추립니다. 이전 폴링에서는 눌렸지만 이번에는 떼어진 선택 버튼을 식별합니다. 계속 떼고 있어도 다음 폴링에서 새 뗌 에지가 생기지는 않습니다.

사용 형식

c
unsigned char nes_pad_release(unsigned char mask);

매개변수

mask

NES 원시 비트 순서로 남길 버튼을 지정하며 필요하면 |로 결합합니다. 0은 아무것도 선택하지 않습니다. 컨트롤러 포트 번호가 아니라 비트 필터입니다.

반환값

저장된 마스크에서 해당하는 비트를 원래 위치 그대로 반환합니다. 1 이외의 값일 수 있으며 0은 해당 버튼이 없음을 뜻합니다. 읽어도 이벤트는 소비되지 않습니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
if (nes_pad_release(0x02)!=0) {
        release_bits=nes_pad_release(0x02); // Returns the released B bit, 2.
        if (nes_pad_held(0x02)!=0 || nes_pad_trigger(0x02)!=0 || nes_pad_repeat(0x02)!=0) failures++;
        break;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_release: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:26

선언 주석 원문
Return the requested newly-released bits from the latest pad poll.

kitaqfc/lib/input_repeat.c:97

c
unsigned char nes_pad_release(unsigned char mask)
{
    return (unsigned char)(nes_pad1_released & mask);
}
nes_pad_repeat

마지막 nes_pad_repeat_step()이 만든 반복 펄스를 추립니다. 새 누름과 누르고 있는 동안의 예약된 반복을 모두 포함합니다. 이 조회 함수는 일정을 진행하지 않습니다.

사용 형식

c
unsigned char nes_pad_repeat(unsigned char mask);

매개변수

mask

NES 원시 비트 순서로 남길 버튼을 지정하며 필요하면 |로 결합합니다. 0은 아무것도 선택하지 않습니다. 컨트롤러 포트 번호가 아니라 비트 필터입니다.

반환값

저장된 마스크에서 해당하는 비트를 원래 위치 그대로 반환합니다. 1 이외의 값일 수 있으며 0은 해당 버튼이 없음을 뜻합니다. 읽어도 이벤트는 소비되지 않습니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
if (nes_pad_repeat(0x02)!=0) {
        if (pulse_count==1 && held_ticks-previous_pulse!=4) failures++;
        if (pulse_count>1 && held_ticks-previous_pulse!=2) failures++;
        previous_pulse=held_ticks; pulse_count++;
        if (nes_pad_repeat(0x02)!=2) failures++; // Querying again preserves the pulse.
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_repeat: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:24

선언 주석 원문
Return the requested repeat-pulse bits from the latest repeat step.

kitaqfc/lib/input_repeat.c:91

c
unsigned char nes_pad_repeat(unsigned char mask)
{
    return (unsigned char)(nes_pad1_repeat & mask);
}
nes_pad_repeat_config

실행 중 첫 대기 시간과 반복 간격을 설정하고 nes_pad1_repeat를 0으로 만든 뒤 8개 버튼별 카운터를 delay로 초기화합니다. 컨트롤러를 폴링하거나 눌림·누름·뗌 마스크를 지우지는 않습니다.

사용 형식

c
void nes_pad_repeat_config(unsigned char delay, unsigned char interval);

매개변수

delay

첫 카운트다운 값으로 0~255입니다. 새 누름의 즉시 펄스 다음 펄스는 delay + 1번의 반복 갱신 뒤에 나옵니다. 단위는 nes_pad_repeat_step() 호출 횟수이며, 프레임마다 정확히 한 번 호출할 때만 프레임 수와 같습니다.

interval

이후 펄스 사이의 카운트다운 값으로 0~255입니다. 실제 간격은 interval + 1번의 반복 갱신입니다. 0이면 첫 대기 이후 누르고 있는 매 단계에서 출력합니다. 여덟 버튼의 카운터는 독립적입니다.

반환값

없음(void).

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

사용 예

c
nes_pad_repeat_config(3,1); // Next pulse after 4 held updates, then every 2.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_repeat_config: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:16

선언 주석 원문
Set initial/repeat countdowns and clear repeat output. Zero countdowns are
allowed and cause a repeat on the next held-button step.
A fresh press emits immediately. Held repeats follow after delay+1 and then interval+1 steps.
These are call counts, not milliseconds; use one fresh pad poll before each step.

kitaqfc/lib/input_repeat.c:21

c
void nes_pad_repeat_config(unsigned char delay, unsigned char interval)
{
    unsigned char i;

    nes_pad_repeat_delay = delay;
    nes_pad_repeat_interval = interval;
    nes_pad1_repeat = 0;

    i = 0;
    while (i != 8)
    {
        nes_pad1_repeat_counter[i] = delay;
        i = i + 1;
    }
}
nes_pad_repeat_step

마지막 폴링의 눌림·누름 마스크로 nes_pad1_repeat를 다시 만들고 버튼별 카운트다운을 한 번 진행합니다. 먼저 nes_pad_poll()을 호출한 다음 한 번 호출하세요. 오래된 누름 마스크를 재사용하면 첫 대기가 계속 초기화되고 새 누름 펄스도 반복됩니다.

사용 형식

c
void nes_pad_repeat_step(void);

반환값

없음(void).

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
nes_pad_repeat_step(); // Once after each fresh poll; getters do not advance time.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_repeat_step: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:20

선언 주석 원문
Generate per-button repeat bits from the most recently polled pad state.
Poll first, then call once per tick; a countdown reaching zero emits on the
following step, so an interval N leaves N decrement steps between pulses.

kitaqfc/lib/input_repeat.c:40

c
void nes_pad_repeat_step(void)
{
    unsigned char bit_index;
    unsigned char mask;
    unsigned char counter;

    nes_pad1_repeat = 0;

    bit_index = 0;
    mask = 1;
    while (mask != 0)
    {
        if ((nes_pad1_cur & mask) == 0)
        {
            nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_delay;
        }
        else
        {
            if ((nes_pad1_pressed & mask) != 0)
            {
                nes_pad1_repeat = nes_pad1_repeat | mask;
                nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_delay;
            }
            else
            {
                counter = nes_pad1_repeat_counter[bit_index];
                if (counter == 0)
                {
                    nes_pad1_repeat = nes_pad1_repeat | mask;
                    nes_pad1_repeat_counter[bit_index] = nes_pad_repeat_interval;
                }
                else
                {
                    nes_pad1_repeat_counter[bit_index] = counter - 1;
                }
            }
        }

        bit_index = bit_index + 1;
        // Byte overflow terminates the scan after the eighth button; each button has its own countdown.
        mask = mask + mask;
    }
}
nes_pad_trigger

컨트롤러 1의 마지막 새 누름 마스크에서 mask에 해당하는 비트를 추립니다. 선택한 버튼이 떼어진 상태에서 눌림으로 바뀔 때 한 번 동작시키는 데 씁니다.

사용 형식

c
unsigned char nes_pad_trigger(unsigned char mask);

매개변수

mask

NES 원시 비트 순서로 남길 버튼을 지정하며 필요하면 |로 결합합니다. 0은 아무것도 선택하지 않습니다. 컨트롤러 포트 번호가 아니라 비트 필터입니다.

반환값

저장된 마스크에서 해당하는 비트를 원래 위치 그대로 반환합니다. 1 이외의 값일 수 있으며 0은 해당 버튼이 없음을 뜻합니다. 읽어도 이벤트는 소비되지 않습니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
if (nes_pad_trigger(0x02)!=0) {
        first_trigger=nes_pad_trigger(0x02); // Returns 2, not Boolean 1.
        first_held=nes_pad_held(0x02); first_repeat=nes_pad_repeat(0x02);
        if (first_trigger!=2 || first_held!=2 || first_repeat!=2) failures++;
    }

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_trigger: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/input_repeat.h:22

선언 주석 원문
Return the requested newly-pressed bits, not a normalized Boolean.

kitaqfc/lib/input_repeat.c:85

c
unsigned char nes_pad_trigger(unsigned char mask)
{
    return (unsigned char)(nes_pad1_pressed & mask);
}

metasprite.h — 여러 스프라이트로 구성한 이미지

metasprite.c의 C 함수와 __metasprite_draw로 확장되는 nes_game.h의 매크로가 제공됩니다. 두 형식은 같은 종료 표시가 있는 데이터열을 읽고 동일한 4바이트 OAM 항목을 씁니다. 예제는 각 형식을 별도로 컴파일합니다.

C 메타스프라이트 함수에는 nes_oam_shadow[256]이 필요합니다. runtime.c는 이를 0x0200에 정의하며, VRAM 큐용으로 0x0300–0x03BF를 예약하고 __nes_nmi도 제공합니다. 각 구현은 한 번만 포함하고 인터럽트 처리를 프로그램 전체와 조율하세요.

nes_metasprite_draw

metasprite.c의 C 함수와 __metasprite_draw로 확장되는 nes_game.h의 매크로가 제공됩니다. 두 형식은 같은 종료 표시가 있는 데이터열을 읽고 동일한 4바이트 OAM 항목을 씁니다. 예제는 각 형식을 별도로 컴파일합니다.

종료 표시가 있는 스프라이트 부품 데이터열을 페이지 02 섀도 버퍼의 연속된 항목으로 펼칩니다. 각 부품은 바이트 크기의 X/Y 오프셋을 기준 위치에 더하고 자체 타일과 속성을 지정합니다. 다음 OAM 번호를 반환합니다.

사용 형식

c
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite);

// Macro from nes_game.h:
#define nes_metasprite_draw(i,x,y,data)    __metasprite_draw((i),(x),(y),(data))

매개변수

oam_index / i

스프라이트 항목 번호 0–63입니다. 바이트 오프셋이 아닙니다. 내부에서 8비트 커서로 4를 곱하므로 범위를 벗어나면 64를 주기로 순환해 다른 항목을 덮어쓸 수 있습니다. 할당 여부는 검사하지 않습니다.

base_x / x

픽셀 단위의 가로 위치를 부호 없는 8비트 값으로 전달합니다. 타일 좌표가 아닙니다.

base_y / y

OAM에 저장할 Y 값입니다. 화면 1번 줄 이후에 윗부분을 놓으려면 표시할 윗좌표에서 1을 뺍니다. 239~255는 240줄 표시 영역 밖입니다.

metasprite / data

dx, dy, tile, attr의 4바이트 레코드로 이루어진 읽기 가능한 데이터열의 포인터입니다. 다음 dx 위치의 0xFF가 종료를 나타내며 그 뒤의 바이트는 필요 없습니다. 오프셋은 256을 주기로 더합니다. 0xFE는 −2, dy의 0xFF는 −1로 사용할 수 있지만 dx의 0xFF는 종료 전용입니다. 포인터는 현재 매핑된 메모리에서 유효해야 합니다.

반환값

다음 OAM 항목 번호인 (oam_index + 기록한 부품 수) modulo 64를 반환합니다. 그린 부품 수가 아니라 다음 기록 위치입니다. 빈 데이터열에서는 oam_index modulo 64입니다.

사용 조건과 제한

헤더 구성을 명확히 선택하세요. nes_game.h와 통합 헤더 fc.h는 매크로를 정의합니다. C 함수를 쓰려면 runtime.h 또는 metasprite.h와 해당 구현 파일을 사용하되 같은 이름의 매크로를 포함하지 않거나 먼저 #undef로 해제하세요. 인수가 없는 DMA 매크로에는 C 함수의 page 인수를 전달할 수 없습니다.

C 메타스프라이트 함수에는 nes_oam_shadow[256]이 필요합니다. runtime.c는 이를 0x0200에 정의하며, VRAM 큐용으로 0x0300–0x03BF를 예약하고 __nes_nmi도 제공합니다. 각 구현은 한 번만 포함하고 인터럽트 처리를 프로그램 전체와 조율하세요.

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

부품 수, 화면 잘라내기, 용량을 검사하지 않습니다. 종료 표시를 반드시 넣고 부품 수를 64−oam_index 이하로 제한하세요. 좌표는 256을 주기로 순환하며 63번 다음에는 0번에 기록합니다. 빈 데이터열은 아무 항목도 변경하지 않습니다. 이 형식은 스프라이트 라이브러리의 metasprite_draw가 사용하는 개수 지정 MetaSpritePart 배열과 다릅니다.

사용 예

c
demo_next=nes_metasprite_draw(4,144,31,demo_parts); // C function: returns 6.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

C-RUNTIME은 C 함수를 함께 사용하는 예제입니다. PAIR에는 (144,32)와 (152,32)에 8×8 화살표 두 개가 나타나며 두 번째는 좌우 반전됩니다. y=64의 다른 두 화살표는 숨기므로 HIDE RANGE는 비어 있어야 합니다. 두 번의 그리기는 슬롯 4–5와 6–7을 사용하여 NEXT 08을 표시합니다. 페이지 02를 페이지 04로 복사한 뒤 슬롯 8의 타일만 화살표 128에서 사각형 129로 바꿉니다. nes_oam_dma(4)는 PAGE 04 옆 (144,96)에 초록 사각형을 표시하고 페이지 02에는 타일 128을 남겨야 합니다.

화살표는 빨간 테두리, 초록 몸통, 끝부분의 파란 세부 무늬로 구성되며 사각형 타일 129는 초록색입니다. 검증에서는 이미지마다 8,704픽셀의 색상과 전송된 OAM 256바이트 전체, 표시된 레이블을 비교합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_metasprite_draw: C 런타임
C 런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm

사용 예

c
fc_oam_next=nes_metasprite_draw(5,136,95,fc_oam_pair); // Returns 7 after two entries.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_metasprite_draw: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/metasprite.h:12

선언 주석 원문
Optional declarations for the phase 10 NES metasprite helper.

Metasprite data layout:
  [dx, dy, tile, attr] repeated, terminated by dx=0xFF


Read four-byte records [dx,dy,tile,attr] until dx=0xFF, writing them to
shadow OAM. Coordinates and destination offsets wrap as bytes. The caller
must supply a terminator and enough unused slots; there is no capacity guard.
Return the next slot index derived from the final wrapped byte offset.

kitaqfc/lib/metasprite.c:18

c
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite)
{
    unsigned char dst;
    unsigned char dx;

    dst = oam_index + oam_index;
    dst = dst + dst;

    while (1)
    {
        dx = metasprite[0];
        if (dx == 0xFF)
        {
            break;
        }

        // These are raw OAM Y bytes, not a corrected screen-top coordinate; no allocation bookkeeping is updated.
        nes_oam_shadow[dst] = (unsigned char)(base_y + metasprite[1]);
        nes_oam_shadow[(unsigned char)(dst + 1)] = metasprite[2];
        nes_oam_shadow[(unsigned char)(dst + 2)] = metasprite[3];
        nes_oam_shadow[(unsigned char)(dst + 3)] = (unsigned char)(base_x + dx);

        dst = (unsigned char)(dst + 4);
        metasprite = metasprite + 4;
    }

    return (unsigned char)(dst >> 2);
}
nes_metasprite_hide_from

연속된 OAM 섀도 항목의 Y 바이트에 0xF8을 써서 숨깁니다. 타일, 속성, X 좌표는 유지됩니다. 이 C 함수는 OAM 전송이나 스프라이트 라이브러리의 할당 해제를 수행하지 않습니다.

사용 형식

c
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count);

매개변수

oam_index

스프라이트 항목 번호 0–63입니다. 바이트 오프셋이 아닙니다. 내부에서 8비트 커서로 4를 곱하므로 범위를 벗어나면 64를 주기로 순환해 다른 항목을 덮어쓸 수 있습니다. 할당 여부는 검사하지 않습니다.

sprite_count

숨길 항목 수로, 범위는 0부터 64−oam_index까지입니다. 0이면 아무것도 변경하지 않습니다. 범위를 넘으면 바이트 커서가 순환하여 OAM 앞부분의 항목까지 숨길 수 있습니다. 함수는 범위를 검사하지 않습니다.

반환값

없음(void).

사용 조건과 제한

C 메타스프라이트 함수에는 nes_oam_shadow[256]이 필요합니다. runtime.c는 이를 0x0200에 정의하며, VRAM 큐용으로 0x0300–0x03BF를 예약하고 __nes_nmi도 제공합니다. 각 구현은 한 번만 포함하고 인터럽트 처리를 프로그램 전체와 조율하세요.

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

사용 예

c
nes_metasprite_hide_from(6,2); // Hide slots 6 and 7; the pair at slots 4 and 5 remains.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

C-RUNTIME은 C 함수를 함께 사용하는 예제입니다. PAIR에는 (144,32)와 (152,32)에 8×8 화살표 두 개가 나타나며 두 번째는 좌우 반전됩니다. y=64의 다른 두 화살표는 숨기므로 HIDE RANGE는 비어 있어야 합니다. 두 번의 그리기는 슬롯 4–5와 6–7을 사용하여 NEXT 08을 표시합니다. 페이지 02를 페이지 04로 복사한 뒤 슬롯 8의 타일만 화살표 128에서 사각형 129로 바꿉니다. nes_oam_dma(4)는 PAGE 04 옆 (144,96)에 초록 사각형을 표시하고 페이지 02에는 타일 128을 남겨야 합니다.

화살표는 빨간 테두리, 초록 몸통, 끝부분의 파란 세부 무늬로 구성되며 사각형 타일 129는 초록색입니다. 검증에서는 이미지마다 8,704픽셀의 색상과 전송된 OAM 256바이트 전체, 표시된 레이블을 비교합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_metasprite_hide_from: C 런타임
C 런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/metasprite.h:17

선언 주석 원문
Hide consecutive shadow slots by setting Y to 0xF8. Keep the requested
range within 64 slots to avoid wrapping and hiding earlier entries.

kitaqfc/lib/metasprite.c:49

c
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count)
{
    unsigned char dst;

    dst = oam_index + oam_index;
    dst = dst + dst;

    while (sprite_count != 0)
    {
        nes_oam_shadow[dst] = 0xF8;
        dst = (unsigned char)(dst + 4);
        sprite_count = sprite_count - 1;
    }
}

nametable_asset.h — 네임테이블 리소스와 배치

32타일 행, 행 간격이 있는 사각형, 채우기, 960+64바이트 화면 전체를 전송하며, 속성 복사본의 편집과 전송도 제공합니다. 상황에 맞게 즉시 전송 또는 나중의 큐 처리를 선택하세요.

직접 전송할 때는 PPUCTRL의 증가량을 1로 설정하고, 렌더링을 끈 초기화 구간이나 전송에 충분히 긴 VBlank를 사용하세요. 이 도우미들은 렌더링을 끄거나 대기하지 않습니다.

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

nes_attr_apply_now

복사본의 64바이트 전체를 네임테이블의 속성 테이블로 즉시 전송합니다.

사용 형식

c
void nes_attr_apply_now(unsigned short nt_base);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

직접 전송할 때는 PPUCTRL의 증가량을 1로 설정하고, 렌더링을 끈 초기화 구간이나 전송에 충분히 긴 VBlank를 사용하세요. 이 도우미들은 렌더링을 끄거나 대기하지 않습니다.

사용 예

c
nes_attr_apply_now(0x2000);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_apply_now: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:20

선언 주석 원문
Upload all attribute shadow bytes immediately; the caller provides safe PPU timing.

kitaqfc/lib/nametable_asset.c:90

c
void nes_attr_apply_now(unsigned short nt_base)
{
    nes_ppu_stream_write(nes_attr_base_from_nt(nt_base), nes_attr_shadow, 64);
}
nes_attr_base_from_nt

네임테이블 시작 주소에 $3C0을 더해 속성 테이블 위치를 구합니다.

사용 형식

c
unsigned short nes_attr_base_from_nt(unsigned short nt_base);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

반환값

입력값을 검사하지 않고 nt_base+$3C0을 반환합니다.

사용 예

c
result[1]=nes_attr_base_from_nt(0x2400);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_base_from_nt: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:11

선언 주석 원문
Locate the 64-byte attribute table at offset 0x03C0 from a nametable base.

kitaqfc/lib/nametable_asset.c:30

c
unsigned short nes_attr_base_from_nt(unsigned short nt_base)
{
    return (unsigned short)(nt_base + 0x03C0);
}
nes_attr_queue_all

속성 복사본 전체를 큐에 복사합니다.

사용 형식

c
unsigned char nes_attr_queue_all(unsigned short nt_base);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

반환값

수락하면 1, 가득 차면 0을 반환합니다. 레코드 하나에 큐 67바이트를 사용합니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

사용 예

c
result[5]=nes_attr_queue_all(0x2000);result[6]=nes_vram_queue_used;nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_queue_all: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:22

선언 주석 원문
Copy all shadow attribute bytes into the runtime queue and return its admission result.

kitaqfc/lib/nametable_asset.c:97

c
unsigned char nes_attr_queue_all(unsigned short nt_base)
{
    return nes_vram_queue_try_write(nes_attr_base_from_nt(nt_base), nes_attr_shadow, 64);
}
nes_attr_shadow_clear

복사본의 64바이트 전체를 같은 비트 구성의 속성값으로 채웁니다.

사용 형식

c
void nes_attr_shadow_clear(unsigned char value);

매개변수

value

비트 단위로 구성한 속성 바이트입니다. 예를 들어 0x55는 모든 사분면에서 팔레트 1을, 0xAA는 팔레트 2를 선택합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

사용 예

c
nes_attr_shadow_clear(0x55);result[2]=nes_attr_shadow[63];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_shadow_clear: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:13

선언 주석 원문
Fill all 64 shadow bytes with an already-packed attribute value.

kitaqfc/lib/nametable_asset.c:36

c
void nes_attr_shadow_clear(unsigned char value)
{
    __memset(nes_attr_shadow, value, 64);
}
nes_attr_shadow_copy

편집의 시작점으로 속성 테이블 전체를 RAM에 복사합니다.

사용 형식

c
void nes_attr_shadow_copy(unsigned char* src64);

매개변수

src64

읽기 가능한 64바이트 속성 테이블입니다. 타일마다 팔레트 ID 하나를 담은 배열이 아닙니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

사용 예

c
for(i=0;i<64;i++)table[i]=0x55;nes_attr_shadow_copy(table);result[3]=nes_attr_shadow[0];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_shadow_copy: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:15

선언 주석 원문
Copy an entire packed attribute table into shadow RAM without writing the PPU.

kitaqfc/lib/nametable_asset.c:42

c
void nes_attr_shadow_copy(unsigned char* src64)
{
    __memcpy(nes_attr_shadow, src64, 64);
}
nes_attr_shadow_set_quad

같은 바이트의 다른 필드는 유지하면서 2×2타일 사분면 하나의 팔레트를 바꿉니다.

사용 형식

c
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index);

매개변수

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

pal_index

팔레트 인덱스의 하위 2비트입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

공유 속성 복사본 64바이트는 CPU RAM $03E0~$041F를 사용하므로 이 범위를 예약하세요. 속성 1바이트는 4×4타일을 담당하며, 각 2비트 필드는 2×2타일 사분면의 팔레트를 선택합니다.

좌표는 32 미만으로 유지하세요. 홀수 좌표는 그 좌표를 포함하는 2×2 사분면을 선택합니다. 가시 행은 0~29입니다.

사용 예

c
nes_attr_shadow_set_quad(4,8,2);result[4]=nes_attr_shadow[17];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_attr_shadow_set_quad: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:18

선언 주석 원문
Replace only the chosen quadrant's palette bits, preserving its neighbors.
Palette IDs are masked to two bits; tile coordinates must fit the shadow table.

kitaqfc/lib/nametable_asset.c:72

c
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index)
{
    unsigned char idx;
    unsigned char shift;
    unsigned char mask;
    unsigned char value;

    idx = nes_attr_index(tile_x, tile_y);
    shift = nes_attr_shift(tile_x, tile_y);
    pal_index = (unsigned char)(pal_index & 3);
    value = nes_attr_shadow[idx];
    mask = (unsigned char)(3 << shift);
    value = (unsigned char)(value & (unsigned char)(~mask));
    value = (unsigned char)(value | (unsigned char)(pal_index << shift));
    nes_attr_shadow[idx] = value;
}
nes_nametable_apply_now

타일 ID 960개를 전송한 다음 속성 테이블 64바이트를 전송합니다.

사용 형식

c
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

nam960

행 우선으로 배치한 32×30타일 배열입니다.

attr64

비트 단위로 구성한 속성 64바이트입니다. 공유 복사본일 필요는 없습니다.

반환값

반환값은 없습니다.

사용 조건과 제한

직접 전송할 때는 PPUCTRL의 증가량을 1로 설정하고, 렌더링을 끈 초기화 구간이나 전송에 충분히 긴 VBlank를 사용하세요. 이 도우미들은 렌더링을 끄거나 대기하지 않습니다.

1024바이트 동기 전송은 보통 렌더링을 끈 초기화 단계에서 수행합니다. 이 도우미는 전송을 여러 프레임으로 나누지 않습니다.

사용 예

c
nes_nametable_apply_now(0x2000,empty,table);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nametable_apply_now: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:41

선언 주석 원문
Upload 960 tile bytes followed by 64 attribute bytes. This bulk operation
does not disable rendering or wait for VBlank on its own.

kitaqfc/lib/nametable_asset.c:178

c
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64)
{
    nes_ppu_stream_write(nt_base, nam960, 960);
    nes_ppu_stream_write(nes_attr_base_from_nt(nt_base), attr64, 64);
}
nes_nt_base_from_index

인덱스의 하위 2비트로 논리 네임테이블 시작 주소를 계산합니다.

사용 형식

c
unsigned short nes_nt_base_from_index(unsigned char index);

매개변수

index

인덱스입니다. 0~3은 각 테이블을 선택하고, 더 큰 값은 하위 2비트로 순환합니다.

반환값

$2000 + (index & 3)*$400을 반환합니다.

사용 예

c
result[0]=nes_nt_base_from_index(5);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_base_from_index: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:9

선언 주석 원문
Mask the index to the four logical nametables and return its PPU base.
Physical storage still depends on the cartridge's mirroring configuration.

kitaqfc/lib/nametable_asset.c:22

c
unsigned short nes_nt_base_from_index(unsigned char index)
{
    index = (unsigned char)(index & 3);
    return (unsigned short)(0x2000 + ((unsigned short)index << 10));
}
nes_nt_queue_fill_rect

사각형의 각 행을 타일 ID 하나로 채우는 작업을 큐에 넣습니다.

사용 형식

c
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

width

타일 단위 너비입니다.

height

타일 단위 높이입니다.

value

채울 타일 ID 0~255입니다.

반환값

모든 행을 넣었거나 높이가 0이면 1, 수락에 실패하면 0을 반환합니다.

사용 조건과 제한

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

좌표와 크기를 가시 네임테이블 안에 유지하세요. 보통 pitch≥width여야 합니다. 클리핑하지 않습니다. 큐에 넣는 행 길이는 최대 127이며, 일반적인 네임테이블 사각형의 너비는 최대 32타일입니다.

사용 예

c
result[10]=nes_nt_queue_fill_rect(0x2000,16,8,4,3,1);result[11]=nes_vram_queue_used;nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_queue_fill_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:38

선언 주석 원문
Queue one fill record per row, stopping on failure while retaining previously
queued rows. The runtime fill-length and rectangle bounds requirements apply.

kitaqfc/lib/nametable_asset.c:157

c
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value)
{
    unsigned char row;
    unsigned short ppu_addr;
    row = 0;
    while (row != height)
    {
        ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
        if (nes_vram_queue_try_fill(ppu_addr, value, width) == 0)
        {
            return 0;
        }
        row = (unsigned char)(row + 1);
    }
    return 1;
}
nes_nt_queue_rect

사각형의 각 행에 대해 원본을 큐에 복사합니다.

사용 형식

c
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

width

타일 단위 너비입니다.

height

타일 단위 높이입니다.

pitch

각 원본 행 시작점 사이의 바이트 간격입니다. 뒤쪽 패딩을 건너뛸 수 있습니다.

src

최소 (height−1)*pitch+width바이트에 걸친 읽기 가능한 원본입니다.

반환값

모든 행을 넣었거나 높이가 0이면 1, 수락에 실패하면 0을 반환합니다.

사용 조건과 제한

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

좌표와 크기를 가시 네임테이블 안에 유지하세요. 보통 pitch≥width여야 합니다. 클리핑하지 않습니다. 큐에 넣는 행 길이는 최대 127이며, 일반적인 네임테이블 사각형의 너비는 최대 32타일입니다.

사용 예

c
result[8]=nes_nt_queue_rect(0x2000,10,8,2,3,4,patch);result[9]=nes_vram_queue_used;nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_queue_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:35

선언 주석 원문
Copy rows into the runtime queue until complete or full. Failure leaves
earlier rows queued; it does not roll back a partially admitted rectangle.
Widths must respect the queue literal-record limit.
Pitch is source bytes per row. Each copied literal record costs width+3 queue bytes;
check total admission needs when partial updates are unacceptable.

kitaqfc/lib/nametable_asset.c:137

c
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src)
{
    unsigned char row;
    unsigned short ppu_addr;
    row = 0;
    while (row != height)
    {
        ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
        if (nes_vram_queue_try_write(ppu_addr, src, width) == 0)
        {
            return 0;
        }
        src = src + pitch;
        row = (unsigned char)(row + 1);
    }
    return 1;
}
nes_nt_queue_row

32타일로 된 한 행 전체를 큐에 복사합니다.

사용 형식

c
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

row

행 0~29입니다. 범위는 검사하지 않습니다.

src32

읽기 가능한 타일 ID 32개입니다.

반환값

수락하면 1, 가득 차면 0을 반환합니다. 큐 35바이트를 사용합니다.

사용 조건과 제한

runtime.c를 링크하세요. 큐는 나중의 NMI나 렌더링이 꺼진 안전한 시점에 반영할 데이터를 복사합니다. 큐 생성과 소비가 겹치지 않게 하세요. 여러 행을 처리하다 용량이 부족해져도 이미 수락한 행은 큐에 남습니다.

사용 예

c
result[7]=nes_nt_queue_row(0x2000,4,row);nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_queue_row: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:26

선언 주석 원문
Copy one complete nametable row into the runtime queue.

kitaqfc/lib/nametable_asset.c:111

c
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32)
{
    unsigned short ppu_addr;
    ppu_addr = (unsigned short)(nt_base + ((unsigned short)row << 5));
    return nes_vram_queue_try_write(ppu_addr, src32, 32);
}
nes_nt_stream_rect

사각형의 각 행을 원본 행 간격에 맞춰 읽어 PPU로 직접 전송합니다.

사용 형식

c
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

tile_x

왼쪽 타일 열이며, 가시 범위는 0~31입니다.

tile_y

맨 위 타일 행이며, 가시 범위는 0~29입니다.

width

타일 단위 너비입니다.

height

타일 단위 높이입니다.

pitch

각 원본 행 시작점 사이의 바이트 간격입니다. 뒤쪽 패딩을 건너뛸 수 있습니다.

src

최소 (height−1)*pitch+width바이트에 걸친 읽기 가능한 원본입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

직접 전송할 때는 PPUCTRL의 증가량을 1로 설정하고, 렌더링을 끈 초기화 구간이나 전송에 충분히 긴 VBlank를 사용하세요. 이 도우미들은 렌더링을 끄거나 대기하지 않습니다.

좌표와 크기를 가시 네임테이블 안에 유지하세요. 보통 pitch≥width여야 합니다. 클리핑하지 않습니다. 큐에 넣는 행 길이는 최대 127이며, 일반적인 네임테이블 사각형의 너비는 최대 32타일입니다.

사용 예

c
// A pitch of four skips two source bytes at the end of each row.
nes_nt_stream_rect(0x2000,4,8,2,2,4,patch);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_stream_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:29

선언 주석 원문
Write each rectangle row immediately, advancing source by pitch rather than
width. Caller-provided coordinates, storage and timing are not clipped or checked.

kitaqfc/lib/nametable_asset.c:120

c
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src)
{
    unsigned char row;
    unsigned short ppu_addr;
    row = 0;
    while (row != height)
    {
        ppu_addr = (unsigned short)(nt_base + ((unsigned short)(tile_y + row) << 5) + tile_x);
        nes_ppu_stream_write(ppu_addr, src, width);
        src = src + pitch;
        row = (unsigned char)(row + 1);
    }
}
nes_nt_stream_row

32타일로 된 한 행 전체를 PPU에 직접 복사합니다.

사용 형식

c
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32);

매개변수

nt_base

논리 네임테이블 시작 주소 $2000/$2400/$2800/$2C00입니다. 물리적으로 독립적인지는 미러링에 따라 결정됩니다.

row

행 0~29입니다. 범위는 검사하지 않습니다.

src32

읽기 가능한 타일 ID 32개입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

직접 전송할 때는 PPUCTRL의 증가량을 1로 설정하고, 렌더링을 끈 초기화 구간이나 전송에 충분히 긴 VBlank를 사용하세요. 이 도우미들은 렌더링을 끄거나 대기하지 않습니다.

사용 예

c
nes_nt_stream_row(0x2000,2,row);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

행, 행 간격이 있는 사각형, 채우기, 테이블 전체 전송을 조합합니다. 빨간 32×8 막대 두 개 아래에 파란 16×16 정사각형과 빨간 16×24/32×24 사각형이 나타납니다. 직접 전송과 큐 전송은 서로 다른 위치에 그립니다.

nes_nt_stream_row: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch3_nametable.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch3_nametable.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/nametable_asset.h:24

선언 주석 원문
Write one complete 32-tile row directly. The caller validates the row and PPU timing.

kitaqfc/lib/nametable_asset.c:103

c
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32)
{
    unsigned short ppu_addr;
    ppu_addr = (unsigned short)(nt_base + ((unsigned short)row << 5));
    nes_ppu_stream_write(ppu_addr, src32, 32);
}

nes_game.h — 게임용 연산 매크로

nes_game.h는 VRAM 큐, OAM과 메타스프라이트, 컨트롤러 읽기, 매퍼 인터럽트, 분할 스크롤, FDS 연산에 읽기 쉬운 매크로 이름을 제공합니다. 각 매크로는 컴파일러 내장 함수로 확장되므로 진단과 KUROSAKI 메타데이터에도 실제 하드웨어 연산이 기록됩니다. 큐 매크로는 내장 큐를 사용하며 OAM DMA 매크로는 인수가 없습니다.

헤더 구성을 명확히 선택하세요. nes_game.h와 통합 헤더 fc.h는 매크로를 정의합니다. C 함수를 쓰려면 runtime.h 또는 metasprite.h와 해당 구현 파일을 사용하되 같은 이름의 매크로를 포함하지 않거나 먼저 #undef로 해제하세요. 인수가 없는 DMA 매크로에는 C 함수의 page 인수를 전달할 수 없습니다.

nes_fds_farcall

nes_game.h의 FDS 원거리 호출 편의 매크로입니다. __fds_overlay_farcall((bank), (func))로 확장됩니다.

사용 형식

c
#define nes_fds_farcall(bank, func)        __fds_overlay_farcall((bank), (func))

매개변수

bank

뱅크를 나타내는 식입니다. 호출 전에 한 번 평가되지만 실제로 로드하는 뱅크는 링크된 func의 배치로 결정됩니다. 런타임에 임의로 선택한 뱅크의 주소를 호출하는 기능이 아닙니다. __bankof(func)처럼 일치하는 값을 전달하세요.

func

선언된 무인수 함수의 이름입니다. 예제는 u8 overlay_answer(void)를 전달합니다. 숫자 주소, 런타임 함수 포인터, 인수가 필요한 함수는 거부됩니다. 인수가 필요하면 일반적인 target(a, b) 호출을 사용하세요.

반환값

대상 함수의 8비트 결과입니다. 로드에 실패했으나 이전 뱅크를 복원할 수 있으면 대상은 호출하지 않고 대신 로드 오류 바이트를 반환합니다. 정상 결과와 오류를 구분할 수 있도록 반환값 규약을 설계하세요.

사용 조건과 제한

--mapper=fds --fds-layout=fds32와 함께 사용하세요. 뱅크 0은 $A000–$DFFF의 공통 코드입니다. 시작 시 뱅크 1이 $6000–$9FFF를 차지하고, 뱅크 2 이상은 같은 16KiB 창에 읽어 오는 오버레이입니다. #pragma fixed_bank 2 같은 지시문으로 오버레이 코드나 데이터를 배치하세요.

뱅크 1은 $6000을 로드 주소로 하는 별도의 16,384바이트 부팅 PRG 파일에서 복원됩니다. 매니페스트가 없으면 기본 파일 ID는 뱅크 1이 0, 공통 코드가 1, CHR이 2입니다. 사용자 정의 매니페스트는 부팅 PRG를 $6000과 $A000의 16,384바이트 파일로 나눠야 합니다. 뱅크 2 이상의 파일 ID는 --fds-overlay-start-id(기본값 32) + 뱅크 번호 − 2입니다.

다른 뱅크를 호출할 때는 이전 뱅크를 저장하고 반환 전에 복원합니다. 중첩 호출도 같은 규칙을 따릅니다. 전송이 일부 진행된 후 실패해도 복원을 시도합니다. 복원 자체가 실패하면 덮어쓴 코드로 돌아가지 않고 공통 코드에서 실행을 멈춥니다. 공통 뱅크의 대상이나 같은 뱅크를 호출할 때는 불필요한 전송을 하지 않습니다.

BIOS 호출은 동기식이므로 이 API는 로드 중 게임 루프를 프레임마다 계속 진행시키지 않습니다. 인터럽트 핸들러가 아닌 공통 뱅크의 일반 제어 코드에서 사용하세요. 화면·음악의 연속성과 실제 BIOS와의 공존은 별도로 검증해야 합니다.

이 캡처는 독자적으로 만든 LoadFiles 호출 규약 검증 코드를 사용하여 컴파일된 CPU 코드, 메모리 전송, 표시된 모든 픽셀을 확인합니다. 실제 BIOS, 디스크 전송 타이밍, CRC, 실기 부팅을 검증하는 것은 아닙니다. KUROSAKI의 FDS 컨트롤러 모델은 부분 구현입니다. 이 검증 코드 없이 일반 실행한 결과만으로 같은 전송 결과를 입증할 수는 없습니다.

아래 첫 컴파일러 명령으로 다운로드 가능한 예제를 빌드합니다. Python 검사 도구는 5개 학습 예제를 최적화 사용 및 미사용으로 모두 빌드한 뒤 KUROSAKI에서 확인합니다. scripts/fds_abi_fixture.py에서 임시 검증 코드를 생성하고 제거하므로 이 검증에는 외부 BIOS가 필요 없습니다. 이 코드는 일반 게임용 BIOS가 아닙니다.

사용 예

c
#pragma fixed_bank 0
#include "fc_fds_scene.h"
u8 choose_overlay(void){fds_result[2]++;return 2;}
#pragma fixed_bank 2
u8 overlay_answer(void){fds_result[1]++;fds_result[3]=__fds_current_bank();return 42;}
#pragma fixed_bank 0
void main(void) {
    __irq_disable();fds_result[1]=0;fds_result[2]=0;
    fds_result[0]=nes_fds_farcall(choose_overlay(),overlay_answer);
    fds_result[4]=__fds_current_bank();fds_result[15]=165;
    fds_call_screen();while(1){m_wait();}
}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

독자적으로 만든 LoadFiles ABI 검증 코드에서 RETURN VALUE는 042, CALLBACK COUNT와 BANK EXPR COUNT는 각각 001, BANK IN CALLBACK은 002, BANK AFTER RETURN은 001입니다. 함수와 뱅크 식이 각각 한 번 실행되었으며 뱅크 2를 호출한 뒤 뱅크 1이 복원되었습니다. 실제 디스크 전송 테스트를 캡처한 것은 아닙니다.

nes_fds_farcall: CALL_ALIAS-DEFAULT — 독자적으로 만든 LoadFiles ABI 검증 코드에서 RETURN VALUE는 042, CALLBACK COUNT와 BANK EXPR COUNT는 각각 001, BANK IN CALLBACK은 002, BANK AFTER RETURN은 001입니다. 함수와 뱅크 식이 각각 한 번 실행되었으며 뱅크 2를 호출한 뒤 뱅크 1이 복원되었습니다. 실제 디스크 전송 테스트를 캡처한 것은 아닙니다.
CALL_ALIAS-DEFAULT — 독자적으로 만든 LoadFiles ABI 검증 코드에서 RETURN VALUE는 042, CALLBACK COUNT와 BANK EXPR COUNT는 각각 001, BANK IN CALLBACK은 002, BANK AFTER RETURN은 001입니다. 함수와 뱅크 식이 각각 한 번 실행되었으며 뱅크 2를 호출한 뒤 뱅크 1이 복원되었습니다. 실제 디스크 전송 테스트를 캡처한 것은 아닙니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\fds_call_alias.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fds_call_alias.fds --mapper=fds --fds-layout=fds32 --fds-no-license-bypass --fds-overlay-trim --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
python .\kitaq-docs\tools\check_fds_load_examples.py
구현과 소스

kitaqfc/lib/nes_game.h:41

nes_fds_load_bank

nes_game.h에 있는 __fds_load_bank()의 매크로 표기입니다. 게임 편의 헤더를 통해 논리 뱅크를 명시적으로 로드할 때 사용합니다.

사용 형식

c
#define nes_fds_load_bank(bank)            __fds_load_bank((bank))

매개변수

bank

로드할 논리 뱅크 번호(1~254)입니다. 뱅크 0은 공통 코드이며 로드 가능한 창이 아닙니다. 255는 현재 뱅크를 알 수 없음을 나타내기 위해 예약되어 있습니다. 뱅크 2 이상에서는 계산된 파일 ID도 0~254 범위여야 합니다.

반환값

성공하면 0입니다. 뱅크가 0 또는 255이거나 파일 ID가 넘치거나 뱅크 1 복원 파일이 없으면 255를 반환합니다. BIOS 전송에 실패하면 해당 오류 바이트를 반환합니다. 전송 전 인수 오류는 현재 뱅크 기록을 유지하지만 BIOS 전송 실패는 255(알 수 없음)로 표시합니다.

사용 조건과 제한

--mapper=fds --fds-layout=fds32와 함께 사용하세요. 뱅크 0은 $A000–$DFFF의 공통 코드입니다. 시작 시 뱅크 1이 $6000–$9FFF를 차지하고, 뱅크 2 이상은 같은 16KiB 창에 읽어 오는 오버레이입니다. #pragma fixed_bank 2 같은 지시문으로 오버레이 코드나 데이터를 배치하세요.

bank를 중복 평가하지 않고 __fds_load_bank((bank))로 확장됩니다. 인수 제한, 상주 뱅크 검사, 오류 동작은 내장 함수와 같습니다.

뱅크 1은 $6000을 로드 주소로 하는 별도의 16,384바이트 부팅 PRG 파일에서 복원됩니다. 매니페스트가 없으면 기본 파일 ID는 뱅크 1이 0, 공통 코드가 1, CHR이 2입니다. 사용자 정의 매니페스트는 부팅 PRG를 $6000과 $A000의 16,384바이트 파일로 나눠야 합니다. 뱅크 2 이상의 파일 ID는 --fds-overlay-start-id(기본값 32) + 뱅크 번호 − 2입니다.

기본 상주 뱅크 검사에서는 이미 로드되었다고 기록된 뱅크이면 디스크를 읽지 않고 0을 반환합니다. --fds-no-overlay-guard는 __fds_load_bank()의 이 생략 기능을 끕니다. 로드에 성공하면 요청한 뱅크를 기록합니다. 이 기록은 메모리 내용의 유효성을 검증한 결과가 아닙니다.

$6000–$9FFF 창에서 실행 중인 코드가 직접 다른 뱅크를 로드하면 실행 중인 함수나 복귀 주소를 덮어쓸 수 있습니다. 이 예제는 호출자와 표시 코드를 #pragma fixed_bank 0에 배치합니다. 다른 뱅크의 함수를 호출하고 돌아오려면 __fds_overlay_farcall()이나 일반적인 타입 지정 함수 호출을 사용하세요.

BIOS 호출은 동기식이므로 이 API는 로드 중 게임 루프를 프레임마다 계속 진행시키지 않습니다. 인터럽트 핸들러가 아닌 공통 뱅크의 일반 제어 코드에서 사용하세요. 화면·음악의 연속성과 실제 BIOS와의 공존은 별도로 검증해야 합니다.

이 캡처는 독자적으로 만든 LoadFiles 호출 규약 검증 코드를 사용하여 컴파일된 CPU 코드, 메모리 전송, 표시된 모든 픽셀을 확인합니다. 실제 BIOS, 디스크 전송 타이밍, CRC, 실기 부팅을 검증하는 것은 아닙니다. KUROSAKI의 FDS 컨트롤러 모델은 부분 구현입니다. 이 검증 코드 없이 일반 실행한 결과만으로 같은 전송 결과를 입증할 수는 없습니다.

아래 첫 컴파일러 명령으로 다운로드 가능한 예제를 빌드합니다. Python 검사 도구는 5개 학습 예제를 최적화 사용 및 미사용으로 모두 빌드한 뒤 KUROSAKI에서 확인합니다. scripts/fds_abi_fixture.py에서 임시 검증 코드를 생성하고 제거하므로 이 검증에는 외부 BIOS가 필요 없습니다. 이 코드는 일반 게임용 BIOS가 아닙니다.

사용 예

c
#pragma fixed_bank 0
#include "fc_fds_scene.h"
#pragma fixed_bank 2
__prg_rom u8 bank_payload[4]={11,22,33,44};
#pragma fixed_bank 0
void main(void) {
    __irq_disable();
    fds_result[0]=__fds_load_bank(2);
    fds_result[1]=__fds_current_bank();
    fds_result[2]=__fds_require_bank(2);
    fds_result[3]=bank_payload[0];fds_result[4]=bank_payload[3];
    fds_result[5]=nes_fds_load_bank(1);
    fds_result[6]=__fds_current_bank();
    fds_result[7]=__fds_require_bank(1);
    fds_result[8]=__fds_load_bank(0);
    fds_result[9]=__fds_load_bank(255);
    fds_result[15]=165;
    m_init();m_text(2,2,"FDS DATA WINDOW");m_wait();
    m_text(2,5,"LOAD BANK 2 STATUS");fds_number(25,5,fds_result[0]);m_wait();
    m_text(2,7,"FIRST / LAST BYTE");fds_number(21,7,fds_result[3]);fds_number(25,7,fds_result[4]);m_wait();
    m_text(2,9,"REQUIRE SAME BANK");fds_number(25,9,fds_result[2]);m_wait();
    m_text(2,11,"RESTORE BANK 1");fds_number(25,11,fds_result[5]);m_wait();
    m_text(2,13,"RESIDENT BANK");fds_number(25,13,fds_result[6]);m_wait();
    m_text(2,16,"INVALID BANKS 0 / 255");m_wait();
    fds_number(2,18,fds_result[8]);fds_number(8,18,fds_result[9]);m_wait();
    while(1){m_wait();}
}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

독자적으로 만든 LoadFiles ABI 검증 코드에서 LOAD BANK 2 STATUS, REQUIRE SAME BANK, RESTORE BANK 1은 모두 000입니다. FIRST / LAST BYTE는 전송된 4바이트 데이터의 011과 044를 표시하며, 복원 후 RESIDENT BANK는 001입니다. 잘못된 뱅크 0과 255는 각각 255를 반환합니다. 같은 뱅크를 요구해도 추가 로드는 없으며 확인된 파일 요청은 32, 그다음 0입니다.

nes_fds_load_bank: BANK_LOAD-DEFAULT — 독자적으로 만든 LoadFiles ABI 검증 코드에서 LOAD BANK 2 STATUS, REQUIRE SAME BANK, RESTORE BANK 1은 모두 000입니다. FIRST / LAST BYTE는 전송된 4바이트 데이터의 011과 044를 표시하며, 복원 후 RESIDENT BANK는 001입니다. 잘못된 뱅크 0과 255는 각각 255를 반환합니다. 같은 뱅크를 요구해도 추가 로드는 없으며 확인된 파일 요청은 32, 그다음 0입니다.
BANK_LOAD-DEFAULT — 독자적으로 만든 LoadFiles ABI 검증 코드에서 LOAD BANK 2 STATUS, REQUIRE SAME BANK, RESTORE BANK 1은 모두 000입니다. FIRST / LAST BYTE는 전송된 4바이트 데이터의 011과 044를 표시하며, 복원 후 RESIDENT BANK는 001입니다. 잘못된 뱅크 0과 255는 각각 255를 반환합니다. 같은 뱅크를 요구해도 추가 로드는 없으며 확인된 파일 요청은 32, 그다음 0입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\fds_bank_load.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fds_bank_load.fds --mapper=fds --fds-layout=fds32 --fds-no-license-bypass --fds-overlay-trim --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
python .\kitaq-docs\tools\check_fds_load_examples.py
구현과 소스

kitaqfc/lib/nes_game.h:40

nes_fds_wave_load

배열을 __fds_wave_load에 전달해 64바이트 파형을 로드하는 매크로입니다.

사용 형식

c
#define nes_fds_wave_load(wave64)          __fds_wave_load((wave64))

매개변수

wave64

FDS 파형 64바이트이며 각 값은 6비트 진폭 0..63입니다. NULL은 검사하지 않습니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=fds로 .fds 이미지를 빌드하세요. 이 예제는 디스크 입출력 없이 사운드를 다룹니다. 녹음은 KUROSAKI의 RAM 직접 부팅과 오디오 모델을 사용합니다. 빈 테스트 펌웨어는 BIOS 루틴을 구현하지 않으며 배포하지 않습니다. 일반 디스크 부팅과 실기에는 적절한 부팅 환경이 필요합니다.

사용 예

c
// Mapper 20 sound lesson: original 64-step triangle and two sustained pitches.
#include "fc_sound_example.h"
#include "fds_sound.h"
#include "nes_game.h"
__location(0x4087) u8 mod_control;
__location(0x4085) u8 mod_counter;
u8 triangle[64]; u8 modulation[32];
void main(void) {
    u8 i;
    sound_begin("FDS WAVE AND VOLUME");
    for(i=0;i<32;i++) { triangle[i]=(u8)(i*2); triangle[63-i]=(u8)(i*2); modulation[i]=0; }
    __fds_sound_enable();
    // Halt/reset modulation before writing the 32 values (each writes two slots).
    mod_control=0x80; mod_counter=0;
    __fds_mod_load(modulation);
    __fds_wave_load(triangle);
    sound_result[0]=*((u8*)0x4040); sound_result[1]=*((u8*)0x405F);
    __fds_freq_set(1031);
    __fds_volume_set(24);
    sound_wait(60);
    __fds_volume_set(0); sound_wait(30);
    // This macro forwards the same 64-byte load. Use raw envelope registers.
    nes_fds_wave_load(triangle);
    __fds_env_set(0x98,0x80,0);
    __fds_freq_set(1545);
    sound_wait(60);
    __fds_volume_set(0); sound_wait(30);
    sound_end();
}

예상 결과

독자적인 64단계 삼각파가 약 440 Hz로 재생되고 쉬었다가 약 660 Hz로 재생된 뒤 멈춥니다. 두 번째 음은 nes_fds_wave_load와 원시 엔벌로프 설정을 사용합니다. 이는 오디오 모델과 직접 부팅의 검증이며 BIOS 기반 디스크 시작 검증은 아닙니다.

nes_fds_wave_load: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_fds.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-fds.fds --mapper=fds --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:39

c
#define nes_fds_wave_load(wave64)          __fds_wave_load((wave64))
nes_mapper_irq_ack

nes_game.h의 핸들러 확인 처리 매크로입니다. __mapper_irq_ack()로 확장되어 요청을 지우면서 동시에 비활성화합니다.

사용 형식

c
#define nes_mapper_irq_ack()               __mapper_irq_ack()

반환값

반환값은 없습니다.

사용 조건과 제한

내장 함수 선언은 intrinsics.h에 있습니다. nes_mapper_irq_* 매크로를 쓰려면 nes_game.h를 포함하세요. 빌드 시 --mapper로 보드를 선택합니다.

MMC3 전용입니다. __nes_irq 핸들러, 조건을 만족하는 PPU A12 전이, 매퍼 IRQ 활성화, CPU __irq_enable()가 필요합니다. 예제는 배경 패턴 $0000, 스프라이트 패턴 $1000으로 렌더링을 켭니다. 이 예제는 실기에서 정확한 한 스캔라인에 동기화되는지 검증하지 않습니다.

MMC3의 확인 처리와 비활성화는 모두 $E000에 씁니다. 현재 요청을 지우고 이후 요청도 끕니다. 다음 인터럽트를 준비하려면 원하는 시점에 __mapper_irq_enable()를 다시 호출하세요. 두 연산 모두 CPU 인터럽트 마스크나 NMI 활성화 상태는 바꾸지 않습니다.

컴파일러는 사용자 정의 인터럽트 핸들러에 RTI를 생성하지만 핸들러가 바꾸는 A/X/Y 레지스터와 공유 임시 공간은 핸들러가 보존해야 합니다. 예제는 A/X/Y를 저장하고 바이트 증가와 인수 없는 확인 처리만 사용합니다. C 함수 호출을 추가하면 해당 함수가 쓰는 공유 저장 공간도 확인하세요.

사용 예

c
void __nes_irq(void) {
    __asm {
        PHA
        TXA
        PHA
        TYA
        PHA
    }
    irq_count++;
    if (irq_phase == 1) { nes_mapper_irq_ack(); }
    else { __mapper_irq_ack(); }
    __asm {
        PLA
        TAY
        PLA
        TAX
        PLA
    }
}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

MMC3 예제는 CHR 표식 19/20, 21/22, 23/24를 표시합니다. CIRAM 읽기 결과는 수직 미러링에서 A B A B, 수평 미러링에서 A A B B입니다. IRQ 수는 01→02→03→03입니다. 처음 세 번의 명시적 활성화는 각각 인터럽트 한 번을 허용하고 비활성화 후에는 수가 변하지 않습니다. 각 단계는 6프레임을 기다립니다.

nes_mapper_irq_ack: MMC3-DEFAULT
MMC3-DEFAULT

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:36

nes_mapper_irq_disable

nes_game.h를 통해 MMC3 IRQ를 끄고 대기 중인 요청을 지웁니다. __mapper_irq_disable()로 확장됩니다.

사용 형식

c
#define nes_mapper_irq_disable()           __mapper_irq_disable()

반환값

반환값은 없습니다.

사용 조건과 제한

내장 함수 선언은 intrinsics.h에 있습니다. nes_mapper_irq_* 매크로를 쓰려면 nes_game.h를 포함하세요. 빌드 시 --mapper로 보드를 선택합니다.

MMC3 전용입니다. __nes_irq 핸들러, 조건을 만족하는 PPU A12 전이, 매퍼 IRQ 활성화, CPU __irq_enable()가 필요합니다. 예제는 배경 패턴 $0000, 스프라이트 패턴 $1000으로 렌더링을 켭니다. 이 예제는 실기에서 정확한 한 스캔라인에 동기화되는지 검증하지 않습니다.

MMC3의 확인 처리와 비활성화는 모두 $E000에 씁니다. 현재 요청을 지우고 이후 요청도 끕니다. 다음 인터럽트를 준비하려면 원하는 시점에 __mapper_irq_enable()를 다시 호출하세요. 두 연산 모두 CPU 인터럽트 마스크나 NMI 활성화 상태는 바꾸지 않습니다.

컴파일러는 사용자 정의 인터럽트 핸들러에 RTI를 생성하지만 핸들러가 바꾸는 A/X/Y 레지스터와 공유 임시 공간은 핸들러가 보존해야 합니다. 예제는 A/X/Y를 저장하고 바이트 증가와 인수 없는 확인 처리만 사용합니다. C 함수 호출을 추가하면 해당 함수가 쓰는 공유 저장 공간도 확인하세요.

사용 예

c
nes_mapper_irq_disable();
    for (i=0;i<6;i++) { m_wait(); }
    mapper_result[18]=irq_count;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

MMC3 예제는 CHR 표식 19/20, 21/22, 23/24를 표시합니다. CIRAM 읽기 결과는 수직 미러링에서 A B A B, 수평 미러링에서 A A B B입니다. IRQ 수는 01→02→03→03입니다. 처음 세 번의 명시적 활성화는 각각 인터럽트 한 번을 허용하고 비활성화 후에는 수가 변하지 않습니다. 각 단계는 6프레임을 기다립니다.

nes_mapper_irq_disable: MMC3-DEFAULT
MMC3-DEFAULT

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:35

nes_mapper_irq_enable

nes_game.h를 통해 MMC3 IRQ를 활성화합니다. __mapper_irq_enable()로 확장되며 CPU 인터럽트 마스크는 바꾸지 않습니다.

사용 형식

c
#define nes_mapper_irq_enable()            __mapper_irq_enable()

반환값

반환값은 없습니다.

사용 조건과 제한

내장 함수 선언은 intrinsics.h에 있습니다. nes_mapper_irq_* 매크로를 쓰려면 nes_game.h를 포함하세요. 빌드 시 --mapper로 보드를 선택합니다.

MMC3 전용입니다. __nes_irq 핸들러, 조건을 만족하는 PPU A12 전이, 매퍼 IRQ 활성화, CPU __irq_enable()가 필요합니다. 예제는 배경 패턴 $0000, 스프라이트 패턴 $1000으로 렌더링을 켭니다. 이 예제는 실기에서 정확한 한 스캔라인에 동기화되는지 검증하지 않습니다.

MMC3의 확인 처리와 비활성화는 모두 $E000에 씁니다. 현재 요청을 지우고 이후 요청도 끕니다. 다음 인터럽트를 준비하려면 원하는 시점에 __mapper_irq_enable()를 다시 호출하세요. 두 연산 모두 CPU 인터럽트 마스크나 NMI 활성화 상태는 바꾸지 않습니다.

컴파일러는 사용자 정의 인터럽트 핸들러에 RTI를 생성하지만 핸들러가 바꾸는 A/X/Y 레지스터와 공유 임시 공간은 핸들러가 보존해야 합니다. 예제는 A/X/Y를 저장하고 바이트 증가와 인수 없는 확인 처리만 사용합니다. C 함수 호출을 추가하면 해당 함수가 쓰는 공유 저장 공간도 확인하세요.

사용 예

c
nes_mapper_irq_set(15); nes_mapper_irq_enable();
    for (i=0;i<6;i++) { m_wait(); }
    mapper_result[16]=irq_count;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

MMC3 예제는 CHR 표식 19/20, 21/22, 23/24를 표시합니다. CIRAM 읽기 결과는 수직 미러링에서 A B A B, 수평 미러링에서 A A B B입니다. IRQ 수는 01→02→03→03입니다. 처음 세 번의 명시적 활성화는 각각 인터럽트 한 번을 허용하고 비활성화 후에는 수가 변하지 않습니다. 각 단계는 6프레임을 기다립니다.

nes_mapper_irq_enable: MMC3-DEFAULT
MMC3-DEFAULT

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:34

nes_mapper_irq_set

nes_game.h의 MMC3 래치 설정 매크로입니다. 인수를 __mapper_irq_set에 한 번 전달하고 재로드를 요청합니다.

사용 형식

c
#define nes_mapper_irq_set(scanline)       __mapper_irq_set((scanline))

매개변수

scanline

8비트 카운터 래치값 0~255이며 화면의 절대 Y 좌표가 아닙니다. 재로드 시점과 조건을 만족하는 PPU A12 전이가 발동 시점을 결정합니다. 0도 유효하며 “인터럽트 끄기”를 뜻하지 않습니다.

반환값

반환값은 없습니다.

사용 조건과 제한

내장 함수 선언은 intrinsics.h에 있습니다. nes_mapper_irq_* 매크로를 쓰려면 nes_game.h를 포함하세요. 빌드 시 --mapper로 보드를 선택합니다.

MMC3 전용입니다. __nes_irq 핸들러, 조건을 만족하는 PPU A12 전이, 매퍼 IRQ 활성화, CPU __irq_enable()가 필요합니다. 예제는 배경 패턴 $0000, 스프라이트 패턴 $1000으로 렌더링을 켭니다. 이 예제는 실기에서 정확한 한 스캔라인에 동기화되는지 검증하지 않습니다.

MMC3의 확인 처리와 비활성화는 모두 $E000에 씁니다. 현재 요청을 지우고 이후 요청도 끕니다. 다음 인터럽트를 준비하려면 원하는 시점에 __mapper_irq_enable()를 다시 호출하세요. 두 연산 모두 CPU 인터럽트 마스크나 NMI 활성화 상태는 바꾸지 않습니다.

컴파일러는 사용자 정의 인터럽트 핸들러에 RTI를 생성하지만 핸들러가 바꾸는 A/X/Y 레지스터와 공유 임시 공간은 핸들러가 보존해야 합니다. 예제는 A/X/Y를 저장하고 바이트 증가와 인수 없는 확인 처리만 사용합니다. C 함수 호출을 추가하면 해당 함수가 쓰는 공유 저장 공간도 확인하세요.

사용 예

c
nes_mapper_irq_set(15); nes_mapper_irq_enable();
    for (i=0;i<6;i++) { m_wait(); }
    mapper_result[16]=irq_count;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

MMC3 예제는 CHR 표식 19/20, 21/22, 23/24를 표시합니다. CIRAM 읽기 결과는 수직 미러링에서 A B A B, 수평 미러링에서 A A B B입니다. IRQ 수는 01→02→03→03입니다. 처음 세 번의 명시적 활성화는 각각 인터럽트 한 번을 허용하고 비활성화 후에는 수가 변하지 않습니다. 각 단계는 6프레임을 기다립니다.

nes_mapper_irq_set: MMC3-DEFAULT
MMC3-DEFAULT

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\mapper_controls.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\mapper_controls.nes --mapper=mmc3 --nes-chr=.\kitaq-docs\samples\mapper_banks.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:33

nes_oam_clear

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

페이지 02의 섀도 버퍼에 있는 스프라이트 64개 모두의 Y를 0xF0으로 설정해 숨깁니다. 타일 번호, 속성, X 좌표는 유지합니다.

사용 형식

c
#define nes_oam_clear()                    __oam_clear()

반환값

없음(void).

사용 조건과 제한

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

사용 예

c
nes_sprite_set(63,144,111,128,0); // Seed the CLEAR row with a visible entry.
nes_oam_clear(); // Set every Y byte to 240; tile, attributes and X are retained.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_oam_clear: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:21

c
#define nes_oam_clear()                    __oam_clear()
nes_oam_dma

이 이름에는 두 가지 형식이 있습니다. 인수가 없는 매크로 nes_oam_dma()는 __oam_dma()를 통해 페이지 02를 전송합니다. C 함수 nes_oam_dma(page)는 지정한 256바이트 CPU 메모리 페이지를 전송합니다. 두 형식 모두 OAMADDR을 0으로 설정하고 원본을 변경하지 않은 채 페이지 전체를 전송합니다.

사용 형식

c
#define nes_oam_dma()                      __oam_dma()

// C function declared in runtime.h:
void nes_oam_dma(unsigned char page);

매개변수

page (C)

원본 CPU 주소의 상위 바이트입니다. 0x0400–0x04FF를 전송하려면 0x0400이 아니라 4를 전달하세요. 256바이트 전체를 읽을 수 있는 페이지를 준비하고 필요한 뱅크를 선택해 두어야 합니다. 읽기에 부수 효과가 있는 I/O 레지스터 페이지는 피하세요. 원본 유효성은 검사하지 않습니다.

반환값

없음(void).

사용 조건과 제한

헤더 구성을 명확히 선택하세요. nes_game.h와 통합 헤더 fc.h는 매크로를 정의합니다. C 함수를 쓰려면 runtime.h 또는 metasprite.h와 해당 구현 파일을 사용하되 같은 이름의 매크로를 포함하지 않거나 먼저 #undef로 해제하세요. 인수가 없는 DMA 매크로에는 C 함수의 page 인수를 전달할 수 없습니다.

C 메타스프라이트 함수에는 nes_oam_shadow[256]이 필요합니다. runtime.c는 이를 0x0200에 정의하며, VRAM 큐용으로 0x0300–0x03BF를 예약하고 __nes_nmi도 제공합니다. 각 구현은 한 번만 포함하고 인터럽트 처리를 프로그램 전체와 조율하세요.

DMA는 VBlank 중이거나 렌더링을 끈 상태에서 호출하세요. 두 형식 모두 VBlank를 기다리지 않습니다. 원본 페이지 전체를 준비한 뒤 전송하고, 준비 도중 인터럽트가 해당 페이지를 수정하지 않도록 하세요.

사용 예

c
m_wait();nes_oam_dma(); // Expand to the page-02 compiler intrinsic.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_oam_dma: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm

사용 예

c
m_wait();nes_oam_dma(4); // C function: source address high byte, 0x0400 -> 4.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

C-RUNTIME은 C 함수를 함께 사용하는 예제입니다. PAIR에는 (144,32)와 (152,32)에 8×8 화살표 두 개가 나타나며 두 번째는 좌우 반전됩니다. y=64의 다른 두 화살표는 숨기므로 HIDE RANGE는 비어 있어야 합니다. 두 번의 그리기는 슬롯 4–5와 6–7을 사용하여 NEXT 08을 표시합니다. 페이지 02를 페이지 04로 복사한 뒤 슬롯 8의 타일만 화살표 128에서 사각형 129로 바꿉니다. nes_oam_dma(4)는 PAGE 04 옆 (144,96)에 초록 사각형을 표시하고 페이지 02에는 타일 128을 남겨야 합니다.

화살표는 빨간 테두리, 초록 몸통, 끝부분의 파란 세부 무늬로 구성되며 사각형 타일 129는 초록색입니다. 검증에서는 이미지마다 8,704픽셀의 색상과 전송된 OAM 256바이트 전체, 표시된 레이블을 비교합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_oam_dma: C 런타임
C 런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_c_helpers.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_c_helpers.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:23

kitaqfc/lib/runtime.c:245

c
void nes_oam_dma(unsigned char page)
{
    OAMADDR = 0;
    OAMDMA = page;
}
nes_pad1

정확히 __pad_read1_safe()로 확장되는 함수형 매크로입니다. 컨트롤러 1을 읽어 NES 원시 마스크를 반환하며, 저장된 상태를 읽는 input_current()의 별칭은 아닙니다.

사용 형식

c
#define nes_pad1()                         __pad_read1_safe()

반환값

NES 원시 비트 순서의 8비트 눌림 마스크입니다. 설정된 비트가 눌린 버튼을 나타내며 0은 없음을 뜻합니다. 0/1로 정규화한 불리언 값이 아닙니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

이 호출은 하드웨어 상태를 새로 읽습니다. input_*나 nes_pad1_*의 저장 상태는 갱신하지 않으며 누름·뗌 에지나 반복도 만들지 않습니다. 필요하면 결과를 저장하고 직접 보관한 이전 마스크와 비교해 변화를 구하세요.

safe 읽기는 전체 버튼을 두 번씩 읽어 두 마스크가 같아질 때까지 반복합니다. 매번 $4016에 스트로브를 보내 두 컨트롤러의 직렬 읽기 위치를 처음으로 돌립니다. 재시도 횟수 제한은 없습니다. 이 일치 확인만으로 모든 DMC·주변기기 조합의 호환성이 입증되지는 않습니다.

사용 예

c
alias1=nes_pad1(); // Macro for __pad_read1_safe(), using NES raw bits.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

두 컨트롤러 포트, 일반 읽기와 일치 확인 읽기, 마스크 필터, 단축 매크로를 비교하는 프로그램입니다. 컨트롤러 1에서 A와 오른쪽을, 컨트롤러 2에서 B와 왼쪽을 함께 누릅니다. RAW P1, SAFE P1, ALIAS P1은 129이고 P2의 세 값은 66입니다. BUTTONS P1은 1, DIRS P1은 128입니다. 방향 필터는 상위 비트의 위치를 유지합니다. FAILED CHECKS는 0이어야 합니다. 관측값은 화면에 그대로 남습니다.

nes_pad1: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\input_raw.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_raw.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/nes_game.h:30

nes_pad2

정확히 __pad_read2_safe()로 확장되는 함수형 매크로입니다. 컨트롤러 2을 읽어 NES 원시 마스크를 반환하며, 저장된 상태를 읽는 input_current()의 별칭은 아닙니다.

사용 형식

c
#define nes_pad2()                         __pad_read2_safe()

반환값

NES 원시 비트 순서의 8비트 눌림 마스크입니다. 설정된 비트가 눌린 버튼을 나타내며 0은 없음을 뜻합니다. 0/1로 정규화한 불리언 값이 아닙니다.

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

이 호출은 하드웨어 상태를 새로 읽습니다. input_*나 nes_pad1_*의 저장 상태는 갱신하지 않으며 누름·뗌 에지나 반복도 만들지 않습니다. 필요하면 결과를 저장하고 직접 보관한 이전 마스크와 비교해 변화를 구하세요.

safe 읽기는 전체 버튼을 두 번씩 읽어 두 마스크가 같아질 때까지 반복합니다. 매번 $4016에 스트로브를 보내 두 컨트롤러의 직렬 읽기 위치를 처음으로 돌립니다. 재시도 횟수 제한은 없습니다. 이 일치 확인만으로 모든 DMC·주변기기 조합의 호환성이 입증되지는 않습니다.

사용 예

c
alias2=nes_pad2(); // Macro for __pad_read2_safe(), using NES raw bits.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

두 컨트롤러 포트, 일반 읽기와 일치 확인 읽기, 마스크 필터, 단축 매크로를 비교하는 프로그램입니다. 컨트롤러 1에서 A와 오른쪽을, 컨트롤러 2에서 B와 왼쪽을 함께 누릅니다. RAW P1, SAFE P1, ALIAS P1은 129이고 P2의 세 값은 66입니다. BUTTONS P1은 1, DIRS P1은 128입니다. 방향 필터는 상위 비트의 위치를 유지합니다. FAILED CHECKS는 0이어야 합니다. 관측값은 화면에 그대로 남습니다.

nes_pad2: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\input_raw.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\input_raw.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/nes_game.h:31

nes_split_scroll_sprite0

nes_game.h의 화면 분할 매크로입니다. X와 Y를 각각 한 번 __split_scroll_sprite0에 전달해 같은 대기와 스크롤 쓰기를 수행합니다.

사용 형식

c
#define nes_split_scroll_sprite0(x,y)      __split_scroll_sprite0((x),(y))

매개변수

x

수평 스크롤값 0..255이며 오브젝트의 화면 X 좌표가 아닙니다. 샘플은 32를 사용해 같은 배경의 아래쪽을 왼쪽으로 32픽셀 옮깁니다.

y

임시 수직 스크롤 설정 0..255이며 일반 가시 범위는 0..239입니다. $2005를 쓰므로 렌더링 중 실제 수직 위치가 즉시 바뀌지는 않습니다. 수평 분할 샘플은 0을 전달합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

두 레이어를 켜고 OAM 스프라이트 0의 불투명 픽셀을 불투명 배경 픽셀과 겹치게 하세요. 배경 뒤 우선순위 플래그가 있어도 히트는 가능합니다. 맨 왼쪽 8픽셀에서는 각 레이어의 해당 표시 허용이 필요하며 X=255의 겹침은 히트를 발생시키지 못합니다.

PPUSTATUS($2002)의 비트 6이 0이 될 때까지, 이어 1이 될 때까지 폴링합니다. 호출 시 이미 히트가 발생했다면 다음 프레임의 새 히트를 기다립니다. 플래그 읽기는 확인 처리가 아니며 PPU가 프리렌더 구간에서 지웁니다.

시간 제한이나 실패 반환값이 없습니다. 숨겨진 스프라이트 0, 투명 배경, 렌더링 비활성화 등 겹침이 없는 설정에서는 대기가 끝나지 않습니다. 호출 전에 히트를 보장하세요. CPU IRQ 활성화는 필요 없으며 NMI도 켜지 않습니다. 샘플은 별도 프레임 대기 때문에 NMI를 켭니다.

$2002 읽기는 공유 PPU 주소/스크롤 쓰기 래치와 VBlank 플래그에도 영향을 줍니다. 두 번 쓰는 $2005/$2006 연산 사이에 이 호출을 넣지 마세요. NMI/IRQ 핸들러가 아닌 메인 코드에서 사용하세요.

히트 후 $2002를 읽고 $2005에 X, Y 순으로 씁니다. 미세 X는 즉시, 거친 X는 다음 수평 재로드에서 적용됩니다. Y는 다른 스크롤 쓰기로 먼저 대체되지 않으면 보통 다음 프리렌더 재로드에서 적용됩니다. 임의 X/Y로 즉시 래스터 위치를 옮기는 연산은 아닙니다.

폴링과 명령 실행 때문에 쓰기에 지연이 생깁니다. KUROSAKI는 주사선 단위로 렌더링하므로 캡처가 실기의 도트 단위 경계를 입증하지는 않습니다. 샘플은 Y=96..103의 흰 띠 안에서 변경해 경계 불확실성이 파란·빨간 막대에 닿지 않게 합니다.

공유 헤더 fc_sprite0_scene.h는 독자적인 파란·빨간 막대와 흰 수평 띠를 준비합니다. 스프라이트 0은 화면 (16,96)에서 배경 뒤에 숨습니다. 표시 Y는 OAM Y+1이므로 원시 OAM Y는 95입니다. 매 프레임 VBlank에서 위쪽 스크롤을 0으로 복원한 뒤 히트를 기다립니다.

NESdev: PPU 스크롤

NESdev: PPU 프로그래머 레퍼런스

사용 예

c
void main(void) {
    s0_prepare();
    while (1) {
        s0_frame();
        nes_split_scroll_sprite0(32,0);
        s0_completed++;
    }
}

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

흰 띠 위에서는 파랑 X=64..95, 빨강 X=160..191입니다. 아래쪽 Y=104부터는 파랑 X=32..63, 빨강 X=128..159로 옮겨져 아래 영역만 왼쪽으로 32픽셀 이동합니다. 수평 띠는 Y=96..103이며 막대는 Y=207에서 끝납니다. 검증은 프레임당 완료 한 번과 모든 이미지 픽셀의 위치·색을 검사합니다.

nes_split_scroll_sprite0: SPLIT_ALIAS-DEFAULT — 흰 띠 위에서는 파랑 X=64..95, 빨강 X=160..191입니다. 아래쪽 Y=104부터는 파랑 X=32..63, 빨강 X=128..159로 옮겨져 아래 영역만 왼쪽으로 32픽셀 이동합니다. 수평 띠는 Y=96..103이며 막대는 Y=207에서 끝납니다. 검증은 프레임당 완료 한 번과 모든 이미지 픽셀의 위치·색을 검사합니다.
SPLIT_ALIAS-DEFAULT — 흰 띠 위에서는 파랑 X=64..95, 빨강 X=160..191입니다. 아래쪽 Y=104부터는 파랑 X=32..63, 빨강 X=128..159로 옮겨져 아래 영역만 왼쪽으로 32픽셀 이동합니다. 수평 띠는 Y=96..103이며 막대는 Y=207에서 끝납니다. 검증은 프레임당 완료 한 번과 모든 이미지 픽셀의 위치·색을 검사합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite0_split_alias.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite0_split_alias.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\sprite0_scene.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:37

nes_sprite_hide

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

섀도 스프라이트 하나의 Y를 0xF0으로 설정해 숨깁니다. X, 타일, 속성은 남아 있어 다시 사용할 수 있습니다. 할당을 해제하거나 스프라이트 라이브러리의 사용 개수를 변경하지는 않습니다.

사용 형식

c
#define nes_sprite_hide(i)                 __sprite_hide((i))

매개변수

i

스프라이트 항목 번호 0–63입니다. 바이트 오프셋이 아닙니다. 내부에서 8비트 커서로 4를 곱하므로 범위를 벗어나면 64를 주기로 순환해 다른 항목을 덮어쓸 수 있습니다. 할당 여부는 검사하지 않습니다.

반환값

없음(void).

사용 조건과 제한

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

사용 예

c
nes_sprite_hide(4); // The HIDE row remains empty after DMA.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_sprite_hide: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:26

c
#define nes_sprite_hide(i)                 __sprite_hide((i))
nes_sprite_move

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

섀도 스프라이트 하나의 X와 Y 바이트만 변경합니다. 타일 번호와 속성을 유지하므로 팔레트나 반전 상태를 바꾸지 않고 같은 그림을 이동할 수 있습니다.

사용 형식

c
#define nes_sprite_move(i,x,y)             __sprite_move((i),(x),(y))

매개변수

i

스프라이트 항목 번호 0–63입니다. 바이트 오프셋이 아닙니다. 내부에서 8비트 커서로 4를 곱하므로 범위를 벗어나면 64를 주기로 순환해 다른 항목을 덮어쓸 수 있습니다. 할당 여부는 검사하지 않습니다.

x

픽셀 단위의 가로 위치를 부호 없는 8비트 값으로 전달합니다. 타일 좌표가 아닙니다.

y

OAM에 저장할 Y 값입니다. 화면 1번 줄 이후에 윗부분을 놓으려면 표시할 윗좌표에서 1을 뺍니다. 239~255는 240줄 표시 영역 밖입니다.

반환값

없음(void).

사용 조건과 제한

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

사용 예

c
nes_sprite_move(1,144,31); // Move to (144,32), retaining tile 128 and attributes 0.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_sprite_move: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:25

c
#define nes_sprite_move(i,x,y)             __sprite_move((i),(x),(y))
nes_sprite_set

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

스프라이트 하나의 네 필드를 페이지 02 섀도 버퍼에 기록합니다. 인수 순서는 번호, X, Y, 타일, 속성이며 저장 순서는 Y, 타일, 속성, X입니다.

사용 형식

c
#define nes_sprite_set(i,x,y,t,a)          __sprite_set((i),(x),(y),(t),(a))

매개변수

i

스프라이트 항목 번호 0–63입니다. 바이트 오프셋이 아닙니다. 내부에서 8비트 커서로 4를 곱하므로 범위를 벗어나면 64를 주기로 순환해 다른 항목을 덮어쓸 수 있습니다. 할당 여부는 검사하지 않습니다.

x

픽셀 단위의 가로 위치를 부호 없는 8비트 값으로 전달합니다. 타일 좌표가 아닙니다.

y

OAM에 저장할 Y 값입니다. 화면 1번 줄 이후에 윗부분을 놓으려면 표시할 윗좌표에서 1을 뺍니다. 239~255는 240줄 표시 영역 밖입니다.

t

8비트 타일 번호입니다. 그래픽을 먼저 로드하세요. 8×16 스프라이트의 하드웨어 규칙은 그대로 적용되며 두 타일 묶음에 맞게 번호를 보정하지 않습니다.

a

속성 바이트 전체입니다. 비트 0–1은 스프라이트 팔레트 0–3, 비트 5는 불투명 배경 뒤에 표시, 비트 6과 7은 좌우 및 상하 반전을 지정합니다. 비트 2–4는 0으로 두세요. 플래그를 비트 OR로 조합하며 전달한 바이트가 기존 속성 전체를 대체합니다.

반환값

없음(void).

사용 조건과 제한

이 작업은 CPU RAM의 0x0200–0x02FF를 수정하며 하드웨어 OAM에는 즉시 반영되지 않습니다. 페이지를 완성한 뒤 __oam_dma로 전송하세요. 스프라이트 라이브러리의 할당 플래그, 좌표 배열, 사용 개수는 갱신하지 않습니다. 준비 중인 페이지를 인터럽트 처리에서 수정하거나 전송하지 않도록 하세요.

사용 예

c
nes_sprite_set(0,144,15,128,0); // An 8x8 arrow at visible position (144,16).

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

작업마다 이름을 붙인 행으로 구분한 예제입니다. SET과 MOVE는 (144,16), (144,32)의 8×8 오른쪽 화살표, TILE은 (144,48)의 초록 정사각형, ATTR은 (144,64)의 상하좌우 반전 화살표입니다. HIDE의 (144,80)과 CLEAR의 (144,112)는 비어 있어야 합니다. META는 (136,96), (144,96)의 두 화살표이며 두 번째는 좌우 반전입니다. NEXT 07은 5번과 6번에 기록한 뒤의 다음 위치입니다. 화살표 테두리는 빨강, 몸통은 초록, 끝부분의 일부는 파랑입니다. 이미지당 8,704픽셀과 전송된 OAM 바이트를 대조하며 실제 기기에서는 검증하지 않았습니다.

ALIASES 프로그램은 nes_game.h 매크로로 각 행을 준비하고 nes_oam_dma()로 페이지 02를 전송합니다. TRANSFER에는 (144,128)의 8×8 화살표가 표시됩니다. runtime.c나 metasprite.c를 연결하지 않고 매크로 형식을 사용하는 예제입니다.

nes_sprite_set: 별칭
별칭

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_aliases.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:24

c
#define nes_sprite_set(i,x,y,t,a)          __sprite_set((i),(x),(y),(t),(a))
nes_vram_clear_queue

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

내장 큐의 대기 레코드를 모두 버리고 길이와 준비 플래그를 0으로 만듭니다. VRAM에 쓰거나 오버플로 기록을 지우지는 않습니다. 기록을 지우려면 __vramq_clear_overflow를 별도로 호출하세요.

사용 형식

c
#define nes_vram_clear_queue()             __vramq_clear()

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 매크로들은 lib/vram.c도 사용하는 컴파일러 내장 명령 큐를 조작합니다. C 런타임의 nes_vram_queue_* 버퍼를 선택하지는 않습니다. 대기 중인 인코딩 바이트 수는 __vramq_len(), 용량 부족은 __vramq_overflow()로 확인하고, 실패 플래그는 __vramq_clear_overflow()로 해제하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
nes_vram_clear_queue();
if (__vramq_len()!=0 || __vramq_overflow()!=1) failures++;
// Clearing queued work deliberately leaves the overflow latch set.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 예제는 1바이트 추가, 복사, 채우기, 커밋, 큐 비우기에 nes_game.h 매크로를 사용합니다. 효과를 확인하기 위해 __vramq_* 조회 및 실행 내장 함수도 사용합니다. 화면 제목은 VRAM QUEUE MACROS입니다. 큰 전송은 렌더링을 끈 상태에서 실행합니다. 커밋한다고 해서 큰 전송이 VBlank 안에 끝나는 것은 아닙니다.

완성 프로그램으로 큐 용량, 보관된 포인터, 두 종류의 초기화, 직접 실행과 NMI 실행의 차이를 익힙니다. 기대값은 CAPACITY 128, ENCODED BYTES 030, AFTER EXEC 000, OVERFLOW SEEN 001, FAILED CHECKS 000입니다. 레코드 6개가 버퍼 30바이트를 사용해 512바이트를 씁니다. OVERFLOW SEEN은 앞서 의도적으로 거부된 시점의 값을 보관하며 종료 시 플래그가 남아 있다는 뜻은 아닙니다.

파란 복사 영역은 픽셀 (0,16), 초록 채우기 영역은 (0,88)에서 시작합니다. 각각 너비 256픽셀인 타일 행 7개와 여덟 번째 행의 248픽셀을 그려 총 255타일이 됩니다. 마지막 8 × 8칸은 검게 남습니다. 복사는 8 × 8 전체 채움·왼쪽 절반·삼각형을 반복하지만 등록 후 원본을 바꾸므로 첫 타일은 삼각형입니다. 채우기는 호출한 쪽의 변수를 바꿔도 전체 채움을 유지합니다. 모양과 색을 모두 검사합니다.

y=160에서 x=16에는 빨간 정사각형, x=224에는 빨간 왼쪽 절반 타일, x=240에는 빨간 정사각형이 나타나야 합니다. 첫 도형은 길이 0 복사와 채우기 후에도 남고, 가운데는 기본 NMI 실행, 마지막은 단일 쓰기를 확인합니다. 취소된 (248,160)은 검게 남아야 합니다. 긴 전송은 렌더링과 NMI를 끄고 실행하며 마지막 작은 쓰기만 NMI를 사용합니다. NROM 에뮬레이터 검증이며 실제 기기 검증은 아닙니다.

nes_vram_clear_queue: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:19

c
#define nes_vram_clear_queue()             __vramq_clear()
nes_vram_commit

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

실행기가 큐를 처리할 수 있도록 준비 플래그를 1로 설정합니다. 데이터 복사, PPU 쓰기, NMI 대기는 하지 않습니다. 커밋 후에는 실행이 끝날 때까지 큐와 보관된 원본을 변경하지 마세요. NMI가 활성화되어 있으면 기본 핸들러가 비동기적으로 처리할 수 있습니다.

사용 형식

c
#define nes_vram_commit()                  __vramq_commit()

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 매크로들은 lib/vram.c도 사용하는 컴파일러 내장 명령 큐를 조작합니다. C 런타임의 nes_vram_queue_* 버퍼를 선택하지는 않습니다. 대기 중인 인코딩 바이트 수는 __vramq_len(), 용량 부족은 __vramq_overflow()로 확인하고, 실패 플래그는 __vramq_clear_overflow()로 해제하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
nes_vram_commit(); // NMI is disabled here: this only marks the queue ready.
if (__vramq_len()!=30) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 예제는 1바이트 추가, 복사, 채우기, 커밋, 큐 비우기에 nes_game.h 매크로를 사용합니다. 효과를 확인하기 위해 __vramq_* 조회 및 실행 내장 함수도 사용합니다. 화면 제목은 VRAM QUEUE MACROS입니다. 큰 전송은 렌더링을 끈 상태에서 실행합니다. 커밋한다고 해서 큰 전송이 VBlank 안에 끝나는 것은 아닙니다.

완성 프로그램으로 큐 용량, 보관된 포인터, 두 종류의 초기화, 직접 실행과 NMI 실행의 차이를 익힙니다. 기대값은 CAPACITY 128, ENCODED BYTES 030, AFTER EXEC 000, OVERFLOW SEEN 001, FAILED CHECKS 000입니다. 레코드 6개가 버퍼 30바이트를 사용해 512바이트를 씁니다. OVERFLOW SEEN은 앞서 의도적으로 거부된 시점의 값을 보관하며 종료 시 플래그가 남아 있다는 뜻은 아닙니다.

파란 복사 영역은 픽셀 (0,16), 초록 채우기 영역은 (0,88)에서 시작합니다. 각각 너비 256픽셀인 타일 행 7개와 여덟 번째 행의 248픽셀을 그려 총 255타일이 됩니다. 마지막 8 × 8칸은 검게 남습니다. 복사는 8 × 8 전체 채움·왼쪽 절반·삼각형을 반복하지만 등록 후 원본을 바꾸므로 첫 타일은 삼각형입니다. 채우기는 호출한 쪽의 변수를 바꿔도 전체 채움을 유지합니다. 모양과 색을 모두 검사합니다.

y=160에서 x=16에는 빨간 정사각형, x=224에는 빨간 왼쪽 절반 타일, x=240에는 빨간 정사각형이 나타나야 합니다. 첫 도형은 길이 0 복사와 채우기 후에도 남고, 가운데는 기본 NMI 실행, 마지막은 단일 쓰기를 확인합니다. 취소된 (248,160)은 검게 남아야 합니다. 긴 전송은 렌더링과 NMI를 끄고 실행하며 마지막 작은 쓰기만 NMI를 사용합니다. NROM 에뮬레이터 검증이며 실제 기기 검증은 아닙니다.

nes_vram_commit: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:18

c
#define nes_vram_commit()                  __vramq_commit()
nes_vram_copy

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

실행 시 src에서 ppu_addr로 len바이트를 복사하는 6바이트 레코드 하나를 추가합니다. 큐는 데이터가 아닌 원본 포인터를 보관합니다. 255바이트 복사도 명령 버퍼에서는 6바이트만 차지합니다. 원본 주소가 증가하는 순서로 읽으며 연속된 대상에는 PPU 증가량 1을 선택하세요.

사용 형식

c
#define nes_vram_copy(addr, src, len)      __vramq_copy((addr), (src), (len))

매개변수

addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

src

실행 때까지 보관하는 읽기 가능한 바이트의 근거리 포인터입니다. 실행이 끝날 때까지 데이터의 수명과 메모리·ROM 뱅크 매핑을 유지하세요. 뱅크 번호나 데이터 사본을 저장하지 않습니다. 비어 있지 않은 전송에는 유효한 원본이 필요하며 수명이 끝난 지역 배열을 사용하면 안 됩니다.

len

바이트 수이며 범위는 0~255입니다. 레코드 하나로 전체 개수를 처리하고 127바이트씩 나누지 않습니다. 0도 복사나 채우기 레코드를 차지합니다. 실행 시 PPU 주소는 설정하지만 PPUDATA 바이트는 쓰지 않습니다. 256은 이 인수의 범위를 벗어납니다.

반환값

없음(void).

사용 조건과 제한

추가 함수는 성공 여부를 반환하지 않습니다. 레코드 전체가 들어가지 않으면 __vramq_overflow()를 1로 설정하고 기존 레코드와 길이는 유지합니다. 정확히 맞으면 등록됩니다. 이전 오버플로가 있어도 공간이 충분하면 추가할 수 있으므로 새 작업 묶음을 판단하기 전에 기록을 처리하거나 지우세요. 여러 추가를 묶어 되돌리는 기능은 없습니다.

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 매크로들은 lib/vram.c도 사용하는 컴파일러 내장 명령 큐를 조작합니다. C 런타임의 nes_vram_queue_* 버퍼를 선택하지는 않습니다. 대기 중인 인코딩 바이트 수는 __vramq_len(), 용량 부족은 __vramq_overflow()로 확인하고, 실패 플래그는 __vramq_clear_overflow()로 해제하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
nes_vram_copy(0x2040,source_bytes,255);
source_bytes[0]=3; // The executor must see this change through its retained pointer.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 예제는 1바이트 추가, 복사, 채우기, 커밋, 큐 비우기에 nes_game.h 매크로를 사용합니다. 효과를 확인하기 위해 __vramq_* 조회 및 실행 내장 함수도 사용합니다. 화면 제목은 VRAM QUEUE MACROS입니다. 큰 전송은 렌더링을 끈 상태에서 실행합니다. 커밋한다고 해서 큰 전송이 VBlank 안에 끝나는 것은 아닙니다.

완성 프로그램으로 큐 용량, 보관된 포인터, 두 종류의 초기화, 직접 실행과 NMI 실행의 차이를 익힙니다. 기대값은 CAPACITY 128, ENCODED BYTES 030, AFTER EXEC 000, OVERFLOW SEEN 001, FAILED CHECKS 000입니다. 레코드 6개가 버퍼 30바이트를 사용해 512바이트를 씁니다. OVERFLOW SEEN은 앞서 의도적으로 거부된 시점의 값을 보관하며 종료 시 플래그가 남아 있다는 뜻은 아닙니다.

파란 복사 영역은 픽셀 (0,16), 초록 채우기 영역은 (0,88)에서 시작합니다. 각각 너비 256픽셀인 타일 행 7개와 여덟 번째 행의 248픽셀을 그려 총 255타일이 됩니다. 마지막 8 × 8칸은 검게 남습니다. 복사는 8 × 8 전체 채움·왼쪽 절반·삼각형을 반복하지만 등록 후 원본을 바꾸므로 첫 타일은 삼각형입니다. 채우기는 호출한 쪽의 변수를 바꿔도 전체 채움을 유지합니다. 모양과 색을 모두 검사합니다.

y=160에서 x=16에는 빨간 정사각형, x=224에는 빨간 왼쪽 절반 타일, x=240에는 빨간 정사각형이 나타나야 합니다. 첫 도형은 길이 0 복사와 채우기 후에도 남고, 가운데는 기본 NMI 실행, 마지막은 단일 쓰기를 확인합니다. 취소된 (248,160)은 검게 남아야 합니다. 긴 전송은 렌더링과 NMI를 끄고 실행하며 마지막 작은 쓰기만 NMI를 사용합니다. NROM 에뮬레이터 검증이며 실제 기기 검증은 아닙니다.

nes_vram_copy: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:16

c
#define nes_vram_copy(addr, src, len)      __vramq_copy((addr), (src), (len))
nes_vram_fill

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

한 바이트 값 value를 PPU에 len번 쓰는 5바이트 레코드 하나를 추가합니다. 값은 레코드에 저장되므로 호출한 쪽의 변수를 나중에 바꿔도 등록된 채우기 값은 바뀌지 않습니다. 여러 바이트의 타일 패턴이 아닌 한 바이트를 반복합니다.

사용 형식

c
#define nes_vram_fill(addr, value, len)    __vramq_fill((addr), (value), (len))

매개변수

addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

len

바이트 수이며 범위는 0~255입니다. 레코드 하나로 전체 개수를 처리하고 127바이트씩 나누지 않습니다. 0도 복사나 채우기 레코드를 차지합니다. 실행 시 PPU 주소는 설정하지만 PPUDATA 바이트는 쓰지 않습니다. 256은 이 인수의 범위를 벗어납니다.

반환값

없음(void).

사용 조건과 제한

추가 함수는 성공 여부를 반환하지 않습니다. 레코드 전체가 들어가지 않으면 __vramq_overflow()를 1로 설정하고 기존 레코드와 길이는 유지합니다. 정확히 맞으면 등록됩니다. 이전 오버플로가 있어도 공간이 충분하면 추가할 수 있으므로 새 작업 묶음을 판단하기 전에 기록을 처리하거나 지우세요. 여러 추가를 묶어 되돌리는 기능은 없습니다.

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 매크로들은 lib/vram.c도 사용하는 컴파일러 내장 명령 큐를 조작합니다. C 런타임의 nes_vram_queue_* 버퍼를 선택하지는 않습니다. 대기 중인 인코딩 바이트 수는 __vramq_len(), 용량 부족은 __vramq_overflow()로 확인하고, 실패 플래그는 __vramq_clear_overflow()로 해제하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
nes_vram_fill(0x2160,fill_value,255); // Seven complete rows and 31 tiles in the eighth.
fill_value=3; // The queued value stays 1, so the green block remains solid.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 예제는 1바이트 추가, 복사, 채우기, 커밋, 큐 비우기에 nes_game.h 매크로를 사용합니다. 효과를 확인하기 위해 __vramq_* 조회 및 실행 내장 함수도 사용합니다. 화면 제목은 VRAM QUEUE MACROS입니다. 큰 전송은 렌더링을 끈 상태에서 실행합니다. 커밋한다고 해서 큰 전송이 VBlank 안에 끝나는 것은 아닙니다.

완성 프로그램으로 큐 용량, 보관된 포인터, 두 종류의 초기화, 직접 실행과 NMI 실행의 차이를 익힙니다. 기대값은 CAPACITY 128, ENCODED BYTES 030, AFTER EXEC 000, OVERFLOW SEEN 001, FAILED CHECKS 000입니다. 레코드 6개가 버퍼 30바이트를 사용해 512바이트를 씁니다. OVERFLOW SEEN은 앞서 의도적으로 거부된 시점의 값을 보관하며 종료 시 플래그가 남아 있다는 뜻은 아닙니다.

파란 복사 영역은 픽셀 (0,16), 초록 채우기 영역은 (0,88)에서 시작합니다. 각각 너비 256픽셀인 타일 행 7개와 여덟 번째 행의 248픽셀을 그려 총 255타일이 됩니다. 마지막 8 × 8칸은 검게 남습니다. 복사는 8 × 8 전체 채움·왼쪽 절반·삼각형을 반복하지만 등록 후 원본을 바꾸므로 첫 타일은 삼각형입니다. 채우기는 호출한 쪽의 변수를 바꿔도 전체 채움을 유지합니다. 모양과 색을 모두 검사합니다.

y=160에서 x=16에는 빨간 정사각형, x=224에는 빨간 왼쪽 절반 타일, x=240에는 빨간 정사각형이 나타나야 합니다. 첫 도형은 길이 0 복사와 채우기 후에도 남고, 가운데는 기본 NMI 실행, 마지막은 단일 쓰기를 확인합니다. 취소된 (248,160)은 검게 남아야 합니다. 긴 전송은 렌더링과 NMI를 끄고 실행하며 마지막 작은 쓰기만 NMI를 사용합니다. NROM 에뮬레이터 검증이며 실제 기기 검증은 아닙니다.

nes_vram_fill: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:17

c
#define nes_vram_fill(addr, value, len)    __vramq_fill((addr), (value), (len))
nes_vram_put

nes_game.h에 정의된 매크로로, 문법에 표시된 내장 함수로 확장됩니다. 이 헤더를 포함하면 사용할 수 있으며, 이 연산을 위한 C 구현 파일은 필요하지 않습니다.

PPU 주소와 한 바이트 값을 4바이트 레코드로 추가합니다. 값은 지금 큐에 복사되고 큐 실행 시 PPU에 쓰입니다. 네임테이블 타일 하나를 갱신할 수 있으며 다른 쓰기 가능한 PPU 바이트를 지정할 수도 있습니다.

사용 형식

c
#define nes_vram_put(addr, value)          __vramq_put((addr), (value))

매개변수

addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

반환값

없음(void).

사용 조건과 제한

추가 함수는 성공 여부를 반환하지 않습니다. 레코드 전체가 들어가지 않으면 __vramq_overflow()를 1로 설정하고 기존 레코드와 길이는 유지합니다. 정확히 맞으면 등록됩니다. 이전 오버플로가 있어도 공간이 충분하면 추가할 수 있으므로 새 작업 묶음을 판단하기 전에 기록을 처리하거나 지우세요. 여러 추가를 묶어 되돌리는 기능은 없습니다.

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 매크로들은 lib/vram.c도 사용하는 컴파일러 내장 명령 큐를 조작합니다. C 런타임의 nes_vram_queue_* 버퍼를 선택하지는 않습니다. 대기 중인 인코딩 바이트 수는 __vramq_len(), 용량 부족은 __vramq_overflow()로 확인하고, 실패 플래그는 __vramq_clear_overflow()로 해제하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
nes_vram_put(0x2282,1); // Red 8x8 sentinel at pixel (16,160).

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 예제는 1바이트 추가, 복사, 채우기, 커밋, 큐 비우기에 nes_game.h 매크로를 사용합니다. 효과를 확인하기 위해 __vramq_* 조회 및 실행 내장 함수도 사용합니다. 화면 제목은 VRAM QUEUE MACROS입니다. 큰 전송은 렌더링을 끈 상태에서 실행합니다. 커밋한다고 해서 큰 전송이 VBlank 안에 끝나는 것은 아닙니다.

완성 프로그램으로 큐 용량, 보관된 포인터, 두 종류의 초기화, 직접 실행과 NMI 실행의 차이를 익힙니다. 기대값은 CAPACITY 128, ENCODED BYTES 030, AFTER EXEC 000, OVERFLOW SEEN 001, FAILED CHECKS 000입니다. 레코드 6개가 버퍼 30바이트를 사용해 512바이트를 씁니다. OVERFLOW SEEN은 앞서 의도적으로 거부된 시점의 값을 보관하며 종료 시 플래그가 남아 있다는 뜻은 아닙니다.

파란 복사 영역은 픽셀 (0,16), 초록 채우기 영역은 (0,88)에서 시작합니다. 각각 너비 256픽셀인 타일 행 7개와 여덟 번째 행의 248픽셀을 그려 총 255타일이 됩니다. 마지막 8 × 8칸은 검게 남습니다. 복사는 8 × 8 전체 채움·왼쪽 절반·삼각형을 반복하지만 등록 후 원본을 바꾸므로 첫 타일은 삼각형입니다. 채우기는 호출한 쪽의 변수를 바꿔도 전체 채움을 유지합니다. 모양과 색을 모두 검사합니다.

y=160에서 x=16에는 빨간 정사각형, x=224에는 빨간 왼쪽 절반 타일, x=240에는 빨간 정사각형이 나타나야 합니다. 첫 도형은 길이 0 복사와 채우기 후에도 남고, 가운데는 기본 NMI 실행, 마지막은 단일 쓰기를 확인합니다. 취소된 (248,160)은 검게 남아야 합니다. 긴 전송은 렌더링과 NMI를 끄고 실행하며 마지막 작은 쓰기만 NMI를 사용합니다. NROM 에뮬레이터 검증이며 실제 기기 검증은 아닙니다.

nes_vram_put: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\vram_queue_macros.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_macros.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/nes_game.h:15

c
#define nes_vram_put(addr, value)          __vramq_put((addr), (value))

oam_fair.h — 우선순위를 유지하는 스프라이트 표시 순환

64개 항목의 소프트웨어 풀에서 활성 항목을 OAM 섀도 버퍼에 추가합니다. 호출할 때마다 탐색 시작 항목을 13만큼 옮기고 64를 기준으로 순환시킵니다. 반복 호출하면 OAM 우선순위가 돌아가며 스프라이트 깜박임을 분산할 수 있지만, 하드웨어의 스프라이트 한도가 늘어나지는 않습니다.

X, Y, active, shadow 배열과 used 커서는 호출 측에서 정의하고, 고정 PRG 뱅크에 oam_fair_impl.h를 한 번만 포함하세요. 8×8 스프라이트 모드의 메인 루프에서 호출합니다. 좌표는 클리핑 없이 256을 기준으로 순환합니다. 이 루틴은 OAM을 전송하지 않으므로 shadow 버퍼에 맞는 페이지를 DMA로 전송해야 합니다. 하드웨어 한도는 전체 64개, 주사선당 8개입니다.

OAM_FairDraw

64개 항목의 소프트웨어 풀에서 활성 항목을 OAM 섀도 버퍼에 추가합니다. 호출할 때마다 탐색 시작 항목을 13만큼 옮기고 64를 기준으로 순환시킵니다. 반복 호출하면 OAM 우선순위가 돌아가며 스프라이트 깜박임을 분산할 수 있지만, 하드웨어의 스프라이트 한도가 늘어나지는 않습니다.

사용 형식

c
void OAM_FairDraw(void);

반환값

C 반환값은 없습니다. oam_fair_drawn에는 이번에 추가한 항목 수, oam_fair_used에는 갱신된 바이트 커서, oam_fair_phase에는 이번 탐색의 시작 인덱스가 저장됩니다. 아무 항목도 그리지 않아도 phase는 진행하며, 이때 oam_fair_drawn은 0입니다.

사용 조건과 제한

함수 인수 대신 공유 변수를 설정합니다. oam_fair_x[64]와 oam_fair_y[64]는 8×8 스프라이트의 중심 좌표를 픽셀 단위로 저장합니다. OAM 원시 X는 중심 X−4, 원시 Y는 중심 Y−5입니다. oam_fair_active[64]의 값이 0이 아닌 항목을 선택하며, 출력되는 모든 항목에 oam_fair_tile과 oam_fair_attr를 공통으로 적용합니다.

oam_fair_used에 oam_fair_shadow[256]의 다음 빈 바이트 위치를 4바이트 경계로 설정합니다. oam_fair_limit은 이번 호출에서 추가할 항목 수의 상한입니다. 최대 64개 풀 항목을 확인하며 바이트 위치 252에서 쓰기를 멈춰 슬롯 63은 사용하지 않습니다. 커서 앞의 항목은 유지되고, 뒤쪽 미사용 항목은 따로 숨겨야 합니다.

X, Y, active, shadow 배열과 used 커서는 호출 측에서 정의하고, 고정 PRG 뱅크에 oam_fair_impl.h를 한 번만 포함하세요. 8×8 스프라이트 모드의 메인 루프에서 호출합니다. 좌표는 클리핑 없이 256을 기준으로 순환합니다. 이 루틴은 OAM을 전송하지 않으므로 shadow 버퍼에 맞는 페이지를 DMA로 전송해야 합니다. 하드웨어 한도는 전체 64개, 주사선당 8개입니다.

사용 예

c
oam_fair_used=4; // Byte cursor: reserve the already prepared slot 0.
OAM_FairDraw(); // Phase becomes 13; append pool entries 13, 14 and 15.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

FAIR-POOL은 슬롯 0의 (144,32)에 있는 초록색 8×8 사각형을 유지하고 중심이 (148,68), (164,68), (180,68)인 화살표 세 개를 추가합니다. 화면상의 왼쪽 위 좌표는 각각 (144,64), (160,64), (176,64)여야 합니다. phase=0, used=4에서 시작하면 PHASE 13, DRAWN 03, BYTES 16이 표시됩니다. 이 정지 화면은 배치와 기존 항목 보존을 검증하며, 별도의 64회 호출 상태 테스트는 모든 풀 항목이 한 번씩 시작 항목이 되는지 확인합니다.

화살표는 빨간 테두리, 초록 몸통, 끝부분의 파란 세부 무늬로 구성되며 사각형 타일 129는 초록색입니다. 검증에서는 이미지마다 8,704픽셀의 색상과 전송된 OAM 256바이트 전체, 표시된 레이블을 비교합니다. 실제 하드웨어에서는 검증하지 않았습니다.

OAM_FairDraw: FAIR-POOL
FAIR-POOL

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\oam_fair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --nes-chr=.\kitaq-docs\samples\sprite_example.chr -o .\out\oam_fair.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/oam_fair.h:24

선언 주석 원문
Append active pool entries using the configured common tile/attributes. The emitter stops at byte cursor 252,
leaving slot 63 unused to avoid cursor wrap. Hide unused trailing OAM entries separately before DMA.

kitaqfc/lib/oam_fair_impl.h:12

c
void OAM_FairDraw(void) {
    __asm {
        LDA oam_fair_phase
        CLC
        ADC #13
        AND #63
        STA oam_fair_phase
        TAX
        LDY oam_fair_used
        LDA #0
        STA oam_fair_drawn
        STA oam_fair_scanned
    // Visit each pool entry at most once, preserving already-written OAM entries
    // and sharing the configured tile/attribute values across emitted sprites.
    kq_fair_loop:
        LDA oam_fair_active,X
        BEQ kq_fair_next
        LDA oam_fair_drawn
        CMP oam_fair_limit
        BCS kq_fair_done
        // Reserve the final four-byte slot so advancing this byte cursor cannot wrap to zero.
        CPY #252
        BCS kq_fair_done
        // Convert the 8x8 center to raw OAM coordinates; Y also needs the hardware minus-one bias.
        LDA oam_fair_y,X
        SEC
        SBC #5
        STA oam_fair_shadow,Y
        LDA oam_fair_tile
        STA oam_fair_shadow+1,Y
        LDA oam_fair_attr
        STA oam_fair_shadow+2,Y
        LDA oam_fair_x,X
        SEC
        SBC #4
        STA oam_fair_shadow+3,Y
        INY
        INY
        INY
        INY
        INC oam_fair_drawn
    // Advance cyclically and stop after returning to this frame's starting entry.
    kq_fair_next:
        TXA
        CLC
        ADC #1
        AND #63
        TAX
        CPX oam_fair_phase
        BNE kq_fair_loop
    // Publish the updated byte cursor for later OAM writers and the VBlank DMA path.
    kq_fair_done:
        STY oam_fair_used
    }
}

pad.h — NES 컨트롤러 입력

pad.h와 pad.c는 컨트롤러 1 상태를 NES 비트 순서로 제공합니다. 폴링은 이전·눌림·새 누름·뗌 마스크를 계산하지만 반복을 예약하지는 않습니다. 버튼별 반복을 설정하려면 input_repeat.c도 링크하세요. 컨트롤러 2는 포트 2 내장 함수로 읽고 이전 상태는 게임에서 관리합니다.

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

nes_pad_poll

$4016에 스트로브를 보내고 컨트롤러 1의 D0에서 8비트를 직렬로 읽어 nes_pad1_prev, nes_pad1_cur, nes_pad1_pressed, nes_pad1_released를 갱신합니다. safe 내장 함수처럼 일치할 때까지 재시도하지 않고 한 번만 읽습니다. 끝에서 $4017을 읽지만 컨트롤러 2 상태를 저장하지는 않습니다.

사용 형식

c
void nes_pad_poll(void);

반환값

없음(void).

사용 조건과 제한

NES 원시 형식은 A=0x01, B=0x02, SELECT=0x04, START=0x08, UP=0x10, DOWN=0x20, LEFT=0x40, RIGHT=0x80입니다. 1인 비트가 눌림을 뜻합니다. nes_pad_*와 원시 __pad_* 결과에는 이 값을 사용하세요. input.h의 공통 BTN_* 마스크와 혼동하지 마세요.

첫 폴링 전에 네 nes_pad1_* 상태 바이트를 0으로 초기화하세요. 이후에는 입력 틱마다 한 번 폴링합니다. pressed = current & ~previous, released = previous & ~current이며 둘 다 마지막 폴링의 변화를 나타냅니다. 반복 상태는 nes_pad_repeat_step()으로 따로 갱신합니다.

사용 예

c
nes_pad_poll(); // Read controller 1 and update its held/pressed/released snapshots.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

폴링 → 반복 갱신 → 상태 조회 순서를 익히고, NES 조회 함수가 논리값 1이 아닌 해당 비트를 반환한다는 점을 확인하는 프로그램입니다. nes_pad_repeat_config(3, 1)로 설정한 뒤 B를 40회 갱신 동안 누르고 놓습니다. FIRST HELD, FIRST TRIGGER, FIRST REPEAT는 2, HELD TICKS는 40, PULSE COUNT는 19, RELEASE BITS는 2입니다. 첫 입력 펄스에서 4회 갱신 뒤에 첫 반복이 발생하고, 이후에는 2회 갱신마다 발생합니다. FAILED CHECKS는 0이어야 합니다. 누르는 시간을 바꾸면 펄스 횟수도 달라집니다.

nes_pad_poll: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\pad.c .\kitaqfc\lib\input_repeat.c .\kitaq-docs\samples\api-examples\fc\pad_repeat.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\pad_repeat.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr --no-disasm --no-cache
구현과 소스

kitaqfc/lib/pad.h:16

선언 주석 원문
Latch controller input with a 1-to-0 strobe and shift eight controller-1 bits
into held/pressed/released masks. This direct path does not perform repeated
reads for DMC interference; the final JOY2 read does not populate player-2 state.

kitaqfc/lib/pad.c:21

c
void nes_pad_poll(void)
{
    unsigned char state;
    unsigned char mask;
    unsigned char sample;

    nes_pad1_prev = nes_pad1_cur;

    JOY1 = 1;
    JOY1 = 0;

    state = 0;
    mask = 1;
    while (mask != 0)
    {
        sample = JOY1;
        if ((sample & 1) != 0)
        {
            state = state | mask;
        }
        mask = mask + mask;
    }

    nes_pad1_cur = state;
    nes_pad1_pressed = (unsigned char)(state & (unsigned char)(state ^ nes_pad1_prev));
    nes_pad1_released = (unsigned char)(nes_pad1_prev & (unsigned char)(state ^ nes_pad1_prev));

    sample = JOY2;
    sample = sample;
}

palette.h — NES 팔레트

32바이트 CPU 측 복사본과 즉시·큐 전송으로 배경색과 스프라이트색을 관리합니다. PPU 팔레트 주소에는 같은 저장 위치를 가리키는 별칭이 포함됩니다.

nes_palette_apply_now

색상 32개를 CPU 측 복사본에 복사한 뒤 PPU로 직접 전송합니다.

사용 형식

c
void nes_palette_apply_now(unsigned char* src32);

매개변수

src32

읽기 가능한 32바이트 팔레트입니다. 배경색 16개 다음에 스프라이트색 16개가 오며, 색상 코드는 0~63을 사용하세요.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다. 안전한 VBlank 구간이나 렌더링이 꺼진 상태에서 PPU 증가량을 1로 설정하여 사용하세요. 이 도우미는 대기, 인터럽트 마스킹, 스크롤 복원을 수행하지 않습니다.

사용 예

c
nes_palette_apply_now(colors);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_apply_now: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:11

선언 주석 원문
Replace shadow palette data and upload all 32 bytes immediately. The caller
provides a safe PPU access period; this helper does not wait for VBlank.

kitaqfc/lib/palette.c:28

c
void nes_palette_apply_now(unsigned char* src32)
{
    nes_palette_copy(src32);
    nes_ppu_stream_write(0x3F00, nes_palette_shadow, 32);
}
nes_palette_apply_shadow_now

CPU 측 복사본의 현재 색상 32개를 PPU로 직접 전송합니다.

사용 형식

c
void nes_palette_apply_shadow_now(void);

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다. 안전한 VBlank 구간이나 렌더링이 꺼진 상태에서 PPU 증가량을 1로 설정하여 사용하세요. 이 도우미는 대기, 인터럽트 마스킹, 스크롤 복원을 수행하지 않습니다.

사용 예

c
nes_palette_apply_shadow_now();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_apply_shadow_now: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:13

선언 주석 원문
Upload the current palette shadow directly, with timing controlled by the caller.

kitaqfc/lib/palette.c:35

c
void nes_palette_apply_shadow_now(void)
{
    nes_ppu_stream_write(0x3F00, nes_palette_shadow, 32);
}
nes_palette_copy

32바이트 팔레트를 CPU 측 복사본에 복사합니다. PPU로는 전송하지 않습니다.

사용 형식

c
void nes_palette_copy(unsigned char* src32);

매개변수

src32

읽기 가능한 32바이트 팔레트입니다. 배경색 16개 다음에 스프라이트색 16개가 오며, 색상 코드는 0~63을 사용하세요.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다.

사용 예

c
nes_palette_copy(colors);result[0]=nes_palette_shadow[5];

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_copy: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:8

선언 주석 원문
Copy exactly 32 palette bytes to the fixed shadow buffer without writing the PPU.

kitaqfc/lib/palette.c:19

c
void nes_palette_copy(unsigned char* src32)
{
    __memcpy(nes_palette_shadow, src32, 32);
}
nes_palette_queue_all

CPU 측 복사본의 색상 32개를 모두 교체하고 런타임 VRAM 큐에 복사합니다.

사용 형식

c
unsigned char nes_palette_queue_all(unsigned char* src32);

매개변수

src32

읽기 가능한 32바이트 팔레트입니다. 배경색 16개 다음에 스프라이트색 16개가 오며, 색상 코드는 0~63을 사용하세요.

반환값

큐에 넣으면 1, 큐 용량이 부족하면 0입니다. 실패해도 CPU 측 복사본의 새 값은 유지됩니다. 성공이 PPU 갱신 완료를 뜻하지는 않습니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다. 큐는 값을 복사하므로 성공적으로 등록한 뒤에는 원본을 변경해도 됩니다. 큐를 채우는 처리와 이를 소비하는 NMI 사이의 경쟁 상태를 방지하세요.

사용 예

c
nes_vram_queue_clear();result[1]=nes_palette_queue_all(colors);result[2]=nes_vram_queue_used;nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_queue_all: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:18

선언 주석 원문
Update the shadow and copy all colors into the runtime queue. A queue
failure leaves the new shadow contents in place.
Return one if the runtime queue accepted the copied payload, zero if it was full.
Admission does not mean the NMI consumer has already updated the PPU.

kitaqfc/lib/palette.c:42

c
unsigned char nes_palette_queue_all(unsigned char* src32)
{
    nes_palette_copy(src32);
    return nes_vram_queue_try_write(0x3F00, nes_palette_shadow, 32);
}
nes_palette_queue_bg4

CPU 측 복사본에서 배경 팔레트 하나의 색상 4개를 갱신하고 VRAM 큐에 복사합니다.

사용 형식

c
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4);

매개변수

pal_index

팔레트 인덱스 0~3입니다. 함수는 이 값을 검사하지 않습니다.

src4

선택한 팔레트의 4바이트입니다. 색상 코드는 0~63을 사용하세요.

반환값

큐에 넣으면 1, 큐 용량이 부족하면 0입니다. 실패해도 CPU 측 복사본의 새 값은 유지됩니다. 성공이 PPU 갱신 완료를 뜻하지는 않습니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다. 큐는 값을 복사하므로 성공적으로 등록한 뒤에는 원본을 변경해도 됩니다. 큐를 채우는 처리와 이를 소비하는 NMI 사이의 경쟁 상태를 방지하세요.

사용 예

c
result[3]=nes_palette_queue_bg4(3,four);nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_queue_bg4: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:21

선언 주석 원문
Update and queue four background palette bytes. pal_index must be 0..3;
there is no range check before writing the shadow.

kitaqfc/lib/palette.c:50

c
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4)
{
    unsigned char dst;
    dst = pal_index + pal_index;
    dst = dst + dst;
    __memcpy(nes_palette_shadow + dst, src4, 4);
    return nes_vram_queue_try_write((unsigned short)(0x3F00 + dst), nes_palette_shadow + dst, 4);
}
nes_palette_queue_sprite4

CPU 측 복사본에서 스프라이트 팔레트 하나의 색상 4개를 갱신하고 VRAM 큐에 복사합니다.

사용 형식

c
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4);

매개변수

pal_index

팔레트 인덱스 0~3입니다. 함수는 이 값을 검사하지 않습니다.

src4

선택한 팔레트의 4바이트입니다. 색상 코드는 0~63을 사용하세요.

반환값

큐에 넣으면 1, 큐 용량이 부족하면 0입니다. 실패해도 CPU 측 복사본의 새 값은 유지됩니다. 성공이 PPU 갱신 완료를 뜻하지는 않습니다.

사용 조건과 제한

CPU 측 복사본은 CPU 주소 0x03C0~0x03DF를 예약합니다. PPU 주소 0x3F10/14/18/1C는 0x3F00/04/08/0C와 같은 저장 위치를 가리키므로, 복사본의 원시 32바이트가 PPU에서 읽은 서로 독립된 32개 값과 일치하는 것은 아닙니다. 큐는 값을 복사하므로 성공적으로 등록한 뒤에는 원본을 변경해도 됩니다. 큐를 채우는 처리와 이를 소비하는 NMI 사이의 경쟁 상태를 방지하세요.

사용 예

c
result[4]=nes_palette_queue_sprite4(2,four);nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

(16,32), (80,32), (144,32)에 빨강·파랑·초록 32×32 배경 사각형을 표시하고, (16,96), (80,96), (144,96)에 같은 색 순서의 8×8 스프라이트 정사각형을 표시합니다. 배경과 스프라이트의 별도 팔레트로 직접 전송과 큐 전송을 보여 줍니다.

nes_palette_queue_sprite4: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_palette.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_palette.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/palette.h:24

선언 주석 원문
Update and queue four sprite palette bytes at the 0x3F10 region. The
caller supplies pal_index 0..3; NES palette mirroring still applies.

kitaqfc/lib/palette.c:61

c
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4)
{
    unsigned char dst;
    dst = pal_index + pal_index;
    dst = dst + dst;
    __memcpy(nes_palette_shadow + (unsigned char)(0x10 + dst), src4, 4);
    return nes_vram_queue_try_write((unsigned short)(0x3F10 + dst), nes_palette_shadow + (unsigned char)(0x10 + dst), 4);
}

Q5.3 데이터 타입으로 구현하는 이동

physics2d.h는 world/body 함수 외에도 독자적인 소수 이동에 쓸 타입과 상수를 정의합니다. 이 예제는 Q5.3 데이터 규칙으로 game_step을 구현합니다. world/body 적분 처리와는 별개의 방식입니다.

KQ2DPosition은 정수 픽셀 위치(0..255), KQ2DFraction은 나머지(0..7), KQ2DSpeed는 틱당 이동량의 크기(1/8픽셀 단위, 0..255), KQ2DDirection은 방향을 저장합니다. 네 타입 모두 u8입니다. 방향은 속도의 부호로 표현하지 않고 따로 저장합니다.

KQ2D_SUBPIXEL_BITS=3, ONE=8, MASK=7을 사용해 position*8+fraction을 16비트 중간값으로 계산합니다. 방향 NONE=0은 정지, NEGATIVE=1은 빼기, POSITIVE=2는 더하기를 뜻합니다. 게임 쪽에서 경계를 처리해 빼기 결과가 음수가 되지 않고 최종 정수 위치가 0..255에 머물도록 해야 합니다.

KQ2D_DRAG_BRAKE=1, STUN=2, COAST=3, THRUST=4는 게임에서 정의하는 제동, 기절, 관성 이동, 추진 동작의 식별값입니다. 값을 대입하는 것만으로 가속이나 저항이 적용되지는 않습니다. 갱신 루틴에서 각 효과와 지속 시간을 정의하세요.

예제: 반 픽셀 이동 누적

위쪽의 빨간 16×16픽셀 상자는 X=24에서 시작하고, 아래쪽 상자는 16틱 뒤의 X=32를 보여 줍니다. 속도 4는 틱당 4/8=0.5픽셀, 즉 16틱 동안 8픽셀을 뜻합니다. 비교하기 쉽도록 두 상태의 Y 위치를 달리했습니다. 이어지는 수치 검사는 8틱 동안 뒤로 이동해 X=28, 소수부 0을 얻습니다.

c
void game_step() {
    u16 total;
    total=(u16)position*KQ2D_SUBPIXEL_ONE+fraction;
    if(direction==KQ2D_DIR_POSITIVE) total=total+speed;
    else if(direction==KQ2D_DIR_NEGATIVE) total=total-speed;
    position=(KQ2DPosition)(total>>KQ2D_SUBPIXEL_BITS);
    fraction=(KQ2DFraction)(total&KQ2D_SUBPIXEL_MASK);
}
// Initial state: position=24; fraction=0; speed=4;
// direction=KQ2D_DIR_POSITIVE; call game_step() 16 times.
위쪽의 빨간 16×16픽셀 상자는 X=24에서 시작하고, 아래쪽 상자는 16틱 뒤의 X=32를 보여 줍니다. 속도 4는 틱당 4/8=0.5픽셀, 즉 16틱 동안 8픽셀을 뜻합니다. 비교하기 쉽도록 두 상태의 Y 위치를 달리했습니다. 이어지는 수치 검사는 8틱 동안 뒤로 이동해 X=28, 소수부 0을 얻습니다.
KUROSAKI에서 확인한 화면: 초기 위치와 반 픽셀 이동을 16회 누적한 위치.

완전한 샘플 프로그램

빌드

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_subpixel.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\physics_subpixel.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm

physics2d

박스 적분과 AABB 접촉 처리, 선택적으로 사용할 수 있는 표면 반응 솔버를 제공합니다. 물체는 중심과 반크기로 표현합니다. active=0이면 해당 슬롯을 제외하고 inv_mass_q8<=0이면 정적 물체로 취급합니다. KQRect는 왼쪽 위 좌표와 너비·높이를 사용합니다. 박스 쌍의 접촉에는 물체의 restitution 필드를 사용하지 않으며, 표면 반응은 KQSurface2D의 계수를 사용합니다.

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

월드는 호출자가 소유한 배열을 빌려 사용합니다. 호출자가 물체 저장 공간을 할당하고 소유하며, 사용하는 동안 유효하게 유지해야 합니다. 월드를 초기화해도 배열 원소는 초기화되지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

kq2d_world_init

배열을 2D 월드에 연결하고 중력을 (0,32), 접촉 반복 횟수를 4, 축별 속도 제한을 512로 설정합니다.

사용 형식

c
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

bodies

body_count개의 원소를 가진 호출자 소유 물체 배열의 시작 주소입니다.

body_count

비활성 항목을 포함한 배열 슬롯 수이며, 범위는 0..255입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

월드는 호출자가 소유한 배열을 빌려 사용합니다. 호출자가 물체 저장 공간을 할당하고 소유하며, 사용하는 동안 유효하게 유지해야 합니다. 월드를 초기화해도 배열 원소는 초기화되지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_world_init(&world,bodies,2);
    result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_world_init: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:36

선언 주석 원문
Retain the caller-owned body array without initializing its elements.
Set gravity to (0,32), four contact iterations and a per-axis speed limit of 512.
A null world is ignored; the array must outlive subsequent world operations.

kitaqfc/lib/physics2d.c:351

c
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count)
{
    if (world == 0) return;

    world->bodies = bodies;
    world->body_count = body_count;
    world->gravity_x = 0;
    world->gravity_y = 32;
    world->solver_iterations = 4;
    world->max_speed = 512;
}
kq2d_step

활성 동적 물체를 적분한 뒤, 배열의 활성 항목 사이에서 접촉을 해결합니다.

사용 형식

c
void kq2d_step(KQWorld2D* world);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

월드는 호출자가 소유한 배열을 빌려 사용합니다. 호출자가 물체 저장 공간을 할당하고 소유하며, 사용하는 동안 유효하게 유지해야 합니다. 월드를 초기화해도 배열 원소는 초기화되지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

침투 깊이가 작은 축을 따라 접촉을 해결하며, 같으면 Y축을 선택합니다. 위치 보정은 역질량을 사용하지만, 두 동적 물체의 해당 축 속도는 단순 평균으로 설정됩니다. 정적 물체와 충돌하면 해당 속도 성분은 0이 됩니다. 이 솔버는 restitution_q8을 사용하지 않습니다. 이산 충돌 처리이므로 빠르게 통과하는 물체를 놓칠 수 있습니다.

박스 적분 및 충돌 함수는 임시 저장 공간을 공유합니다. 인터럽트에서 이 물리 함수들을 재진입 호출하지 마세요.

world나 배열이 NULL이면 무시합니다. solver_iterations가 0이어도 접촉 처리를 한 번 수행합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_step(&world);
    result[22]=bodies[0].x; result[23]=bodies[0].y;
    result[24]=bodies[0].vx; result[25]=bodies[0].vy;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_step: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:41

선언 주석 원문
Advance active dynamic bodies, then visit each active unordered pair for
the configured contact passes (zero means one). Static bodies still participate
as obstacles. The world borrows its array and uses non-reentrant helper scratch.
This is discrete integration; fast bodies can pass through thin obstacles.

kitaqfc/lib/physics2d.c:581

c
void kq2d_step(KQWorld2D* world)
{
    if (world == 0) return;
    if (world->bodies == 0) return;

    KQBody2D* bodies = world->bodies;
    u8 count = world->body_count;

    // Integrate dynamic bodies.
    u8 i;
    for (i = 0; i < count; i++)
    {
        KQBody2D* b = &bodies[(u8)i];
        if (b->active == 0) continue;
        if (b->inv_mass_q8 <= 0) continue;

        kq2d_asm_value = b->vx;
        kq2d_asm_operand = world->gravity_x;
        kq2d__add_value();
        kq2d_asm_limit = world->max_speed;
        kq2d__clamp_value();
        b->vx = kq2d_asm_value;

        kq2d_asm_value = b->vy;
        kq2d_asm_operand = world->gravity_y;
        kq2d__add_value();
        kq2d_asm_limit = world->max_speed;
        kq2d__clamp_value();
        b->vy = kq2d_asm_value;

        kq2d_asm_value = b->x;
        kq2d_asm_operand = b->vx;
        kq2d__add_value();
        b->x = kq2d_asm_value;

        kq2d_asm_value = b->y;
        kq2d_asm_operand = b->vy;
        kq2d__add_value();
        b->y = kq2d_asm_value;
    }

    // Solve contacts with a small fixed iteration count.
    u8 iterations = world->solver_iterations;
    if (iterations == 0) iterations = 1;

    u8 it;
    for (it = 0; it < iterations; it++)
    {
        for (i = 0; i < count; i++)
        {
            KQBody2D* a = &bodies[(u8)i];
            if (a->active == 0) continue;

            u8 j;
            for (j = (u8)(i + 1); j < count; j++)
            {
                KQBody2D* b = &bodies[(u8)j];
                if (b->active == 0) continue;
                kq2d__resolve_pair(a, b);
            }
        }
    }
}
kq2d_integrate_body

동적 물체 하나에 월드의 중력과 축별 속도 제한을 적용한 다음, 속도를 위치에 더합니다.

사용 형식

c
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

박스 적분 및 충돌 함수는 임시 저장 공간을 공유합니다. 인터럽트에서 이 물리 함수들을 재진입 호출하지 마세요.

NULL, 비활성 물체, 역질량이 0 이하인 물체는 무시합니다. 속도 제한에는 0 이상의 값을 지정하세요.

이 호출은 다른 물체와의 접촉을 해결하지 않습니다. world가 NULL인 경우도 무시합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_integrate_body(&world,&temp);
    result[18]=temp.x; result[19]=temp.y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_integrate_body: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:45

선언 주석 원문
For one active dynamic body, add gravity, clamp each velocity axis and then
add velocity to position. Null inputs are ignored; no contacts are solved.
Use a nonnegative speed limit and keep additions in signed 16-bit range.

kitaqfc/lib/physics2d.c:451

c
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body)
{
    if (world == 0) return;
    if (body == 0) return;
    if (body->active == 0) return;
    if (body->inv_mass_q8 <= 0) return;

    kq2d_asm_value = body->vx;
    kq2d_asm_operand = world->gravity_x;
    kq2d__add_value();
    kq2d_asm_limit = world->max_speed;
    kq2d__clamp_value();
    body->vx = kq2d_asm_value;

    kq2d_asm_value = body->vy;
    kq2d_asm_operand = world->gravity_y;
    kq2d__add_value();
    kq2d_asm_limit = world->max_speed;
    kq2d__clamp_value();
    body->vy = kq2d_asm_value;

    kq2d_asm_value = body->x;
    kq2d_asm_operand = body->vx;
    kq2d__add_value();
    body->x = kq2d_asm_value;

    kq2d_asm_value = body->y;
    kq2d_asm_operand = body->vy;
    kq2d__add_value();
    body->y = kq2d_asm_value;
}
kq2d_overlap_aabb

중심으로 표현한 두 박스가 X와 Y 양쪽 축에서 엄격히 겹치는지 검사합니다.

사용 형식

c
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b);

매개변수

a

유효한 물체 포인터입니다. 이 질의는 활성 상태, 질량, 플래그를 무시합니다.

b

유효한 물체 포인터입니다. 이 질의는 활성 상태, 질량, 플래그를 무시합니다.

반환값

내부가 겹치면 1, 접촉만 하거나 떨어져 있으면 0을 반환합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[20]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);
    bodies[1].x=40; result[21]=kq2d_overlap_aabb(&bodies[0],&bodies[1]);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_overlap_aabb: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:49

선언 주석 원문
Test strict overlap of two centered boxes; touching edges return false.
Both pointers must be valid. Active/mass flags are ignored, and coordinate
differences and extent sums must fit the signed 16-bit calculations.

kitaqfc/lib/physics2d.c:366

c
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b)
{
    s16 dx = (s16)(b->x - a->x);
    s16 dy = (s16)(b->y - a->y);

    s16 adx = kq2d__abs_s16(dx);
    s16 ady = kq2d__abs_s16(dy);

    s16 sx = (s16)(a->half_x + b->half_x);
    s16 sy = (s16)(a->half_y + b->half_y);

    if (adx >= sx) return 0;
    if (ady >= sy) return 0;
    return 1;
}
kq2d_body_init

주어진 반크기로 중심 기준 박스를 초기화하고 속도 0, 역질량 256, 활성 상태로 설정합니다.

사용 형식

c
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

x

중심 또는 점의 X 좌표입니다. 샘플에서는 픽셀 단위입니다.

y

중심 또는 점의 Y 좌표입니다. 샘플에서는 아래쪽이 양의 방향입니다.

half_x

X축 반크기입니다. 박스의 전체 너비는 이 값의 두 배입니다.

half_y

Y축 반크기입니다. 박스의 전체 높이는 이 값의 두 배입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

restitution_q8을 0으로 설정합니다. body가 NULL이면 무시합니다. 양의 반크기를 지정하세요.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_init(&temp,10,20,8,8);
    result[3]=temp.inv_mass_q8; result[4]=temp.active;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_body_init: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:52

선언 주석 원문
Initialize an active unit-inverse-mass body at the supplied center and half
extents, with zero velocity and reserved restitution set to zero. Null is ignored.

kitaqfc/lib/physics2d.c:384

c
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y)
{
    if (body == 0) return;
    body->x = x;
    body->y = y;
    body->vx = 0;
    body->vy = 0;
    body->half_x = half_x;
    body->half_y = half_y;
    body->inv_mass_q8 = 256;
    body->restitution_q8 = 0;
    body->active = 1;
}
kq2d_body_set_pos

물체 중심만 바꾸고 속도, 크기, 활성 상태는 유지합니다.

사용 형식

c
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

x

중심 또는 점의 X 좌표입니다. 샘플에서는 픽셀 단위입니다.

y

중심 또는 점의 Y 좌표입니다. 샘플에서는 아래쪽이 양의 방향입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

NULL은 무시합니다. 포인터가 유효하면 비활성 물체와 정적 물체도 변경합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_set_pos(&temp,-12,20);
    result[5]=temp.x; result[6]=temp.y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_body_set_pos: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:54

선언 주석 원문
Replace the center without altering velocity, extents or activity. Null is ignored.

kitaqfc/lib/physics2d.c:399

c
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y)
{
    if (body == 0) return;
    body->x = x;
    body->y = y;
}
kq2d_body_set_velocity

제한을 적용하거나 위치를 갱신하지 않고 속도를 직접 설정합니다.

사용 형식

c
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

vx

시뮬레이션 1틱당 X 변위이며, 위치와 같은 단위를 사용합니다.

vy

시뮬레이션 1틱당 Y 변위이며, 위치와 같은 단위를 사용합니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

NULL은 무시합니다. 포인터가 유효하면 비활성 물체와 정적 물체도 변경합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_set_velocity(&temp,40,-30);
    result[7]=temp.vx; result[8]=temp.vy;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_body_set_velocity: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:56

선언 주석 원문
Replace velocity without clamping or checking activity/mass. Null is ignored.

kitaqfc/lib/physics2d.c:407

c
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy)
{
    if (body == 0) return;
    body->vx = vx;
    body->vy = vy;
}
kq2d_body_apply_gravity

활성 동적 물체에 중력을 더하고 각 축 속도를 +/-max_speed로 제한합니다. 위치는 갱신하지 않습니다.

사용 형식

c
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

gravity_x

이번 호출에서 X 속도에 더하는 가속도입니다.

gravity_y

이번 호출에서 Y 속도에 더하는 가속도입니다.

max_speed

속도 제한값입니다. 각 축을 제한하는지 전체 벡터를 제한하는지는 해당 항목의 설명을 참조하세요.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

박스 적분 및 충돌 함수는 임시 저장 공간을 공유합니다. 인터럽트에서 이 물리 함수들을 재진입 호출하지 마세요.

NULL, 비활성 물체, 역질량이 0 이하인 물체는 무시합니다. 속도 제한에는 0 이상의 값을 지정하세요.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_apply_gravity(&temp,5,-10,32);
    result[9]=temp.vx; result[10]=temp.vy;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_body_apply_gravity: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:60

선언 주석 원문
Add gravity and clamp each velocity axis separately for an active dynamic
body. Null, inactive and static bodies are ignored. Supply a nonnegative limit
and avoid overflow in the additions before clamping; position is unchanged.

kitaqfc/lib/physics2d.c:417

c
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed)
{
    if (body == 0) return;
    if (body->active == 0) return;
    if (body->inv_mass_q8 <= 0) return;

    kq2d_asm_value = body->vx;
    kq2d_asm_operand = gravity_x;
    kq2d__add_value();
    kq2d_asm_limit = max_speed;
    kq2d__clamp_value();
    body->vx = kq2d_asm_value;

    kq2d_asm_value = body->vy;
    kq2d_asm_operand = gravity_y;
    kq2d__add_value();
    kq2d_asm_limit = max_speed;
    kq2d__clamp_value();
    body->vy = kq2d_asm_value;
}
kq2d_body_apply_friction

위치나 활성 상태를 확인하지 않고 두 속도 성분에 부호 있는 바이트 계수를 256으로 나눈 배율을 적용합니다.

사용 형식

c
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

friction_q8

이 함수가 사용하는 속도 배율입니다. s8로 변환하므로 128..255는 음수 계수가 됩니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

0..127은 0..127/256의 배율입니다. 128..255는 s8 변환 후 음수가 되어 속도 방향을 뒤집습니다. 0..255 전체 범위에서 양의 감쇠를 적용하려면 각 속도에 kq2d_scale_q8을 사용하세요. 곱셈 결과는 0 방향으로 버림합니다. NULL은 무시합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_apply_friction(&temp,64);
    result[11]=temp.vx; result[12]=temp.vy;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_body_apply_friction: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:64

선언 주석 원문
Scale both velocities by a signed eight-bit coefficient divided by 256.
Despite the unsigned parameter, values 128..255 become negative coefficients;
this is not a full-range unsigned Q8 damping helper. Null is ignored.

kitaqfc/lib/physics2d.c:441

c
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8)
{
    if (body == 0) return;
    body->vx = kq2d_scale_q8(body->vx, (s16)(s8)friction_q8);
    body->vy = kq2d_scale_q8(body->vy, (s16)(s8)friction_q8);
}
kq2d_rect_intersect

왼쪽 위 위치와 크기로 표현한 두 사각형의 내부가 겹치는지 검사합니다.

사용 형식

c
u8 kq2d_rect_intersect(KQRect a, KQRect b);

매개변수

a

왼쪽 위 좌표와 양의 너비·높이를 갖는 KQRect 값입니다.

b

왼쪽 위 좌표와 양의 너비·높이를 갖는 KQRect 값입니다.

반환값

내부가 겹치면 1, 접촉만 하거나 떨어져 있으면 0을 반환합니다.

사용 조건과 제한

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

0 이하의 크기를 명시적으로 거부하지 않습니다. 물체의 중심과 반크기를 KQRect에 그대로 복사하지 마세요.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[14]=kq2d_rect_intersect(r1,r2);
    r2.x=16; result[15]=kq2d_rect_intersect(r1,r2);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_rect_intersect: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:67

선언 주석 원문
Delegate strict rectangle overlap to the fixed-point utility; edge-only
contact is false. Coordinate-plus-size sums must remain representable as s16.

kitaqfc/lib/physics2d.c:485

c
u8 kq2d_rect_intersect(KQRect a, KQRect b)
{
    return kq_rect_intersect(a, b);
}
kq2d_point_in_rect

점이 사각형 안에 있는지 검사합니다. 왼쪽·위쪽 변은 포함하고 오른쪽·아래쪽 변은 제외합니다.

사용 형식

c
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r);

매개변수

x

중심 또는 점의 X 좌표입니다. 샘플에서는 픽셀 단위입니다.

y

중심 또는 점의 Y 좌표입니다. 샘플에서는 아래쪽이 양의 방향입니다.

r

왼쪽 위 위치 x/y와 너비/높이 w/h를 담은 KQRect 값입니다.

반환값

안에 있으면 1, 밖에 있으면 0을 반환합니다.

사용 조건과 제한

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[16]=kq2d_point_in_rect(0,0,r1);
    result[17]=kq2d_point_in_rect(16,0,r1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

kq2d_point_in_rect: NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.
NTSC — 왼쪽은 초기 상태, 오른쪽은 한 틱 후입니다. 중심 (24,24), 속도 (4,16)의 빨간 16×16 상자는 (28,36)에 도달하며 파란 바닥에서 Y 속도가 0이 됩니다. 패널 원점은 화면 좌표 (8,48), (88,48)입니다. RAM 검증은 초기화, 설정 함수, 적분, 사각형·점 조회, 부호 있는 스케일 계산도 포함합니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body2d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body2d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:70

선언 주석 원문
Delegate the half-open point test: include left/top and exclude right/bottom.
Coordinate-plus-size sums must remain representable as s16.

kitaqfc/lib/physics2d.c:492

c
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r)
{
    return kq_point_in_rect(x, y, r);
}
kq2d_scale_q8

부호 있는 계수를 곱하고 256으로 나눕니다. 결과의 부호와 관계없이 0 방향으로 버림합니다.

사용 형식

c
s16 kq2d_scale_q8(s16 value, s16 coefficient);

매개변수

value

배율을 적용할 부호 있는 16비트 값입니다.

coefficient

-256..256 범위의 부호 있는 계수이며, 256이 1.0을 나타냅니다.

반환값

0 방향으로 버림한 결과를 부호 있는 16비트 값으로 반환합니다.

사용 조건과 제한

계수는 -256..256 범위에서만 사용하세요. +/-256은 +/-1배입니다. value=-32768, coefficient=-256일 때 수학적 결과는 32768이므로 s16에 담을 수 없습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[0]=kq2d_scale_q8(-257,128);
    result[1]=kq2d_scale_q8(300,-256);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

kq2d_scale_q8: NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.
NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:86

선언 주석 원문
Truncates toward zero, including negative values; coefficient is -256..256.

kitaqfc/lib/physics2d.c:8

c
s16 kq2d_scale_q8(s16 value,s16 coefficient) {
    u16 magnitude;u16 product;u8 factor;u8 negative;
    __asm {
        LDA value
        ORA value+1
        BEQ kqfc_scale_zero
        LDA coefficient
        BNE kqfc_scale_fraction
        LDA coefficient+1
        BEQ kqfc_scale_zero
        BMI kqfc_scale_negate_value
        LDA value
        LDX value+1
        RTS
kqfc_scale_negate_value:
        SEC
        LDA #0
        SBC value
        STA product
        LDA #0
        SBC value+1
        TAX
        LDA product
        RTS
kqfc_scale_zero:
        LDA #0
        LDX #0
        RTS
kqfc_scale_fraction:
        LDA value+1
        EOR coefficient+1
        AND #128
        STA negative
        LDA value
        STA magnitude
        LDA value+1
        STA magnitude+1
        BPL kqfc_scale_value_positive
        SEC
        LDA #0
        SBC magnitude
        STA magnitude
        LDA #0
        SBC magnitude+1
        STA magnitude+1
kqfc_scale_value_positive:
        LDA coefficient
        STA factor
        LDA coefficient+1
        BPL kqfc_scale_factor_positive
        SEC
        LDA #0
        SBC factor
        STA factor
kqfc_scale_factor_positive:
        LDA #0
        STA product
        STA product+1
        // Process coefficient bit 0; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_0
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_0:
        ROR product+1
        ROR product
        // Process coefficient bit 1; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_1
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_1:
        ROR product+1
        ROR product
        // Process coefficient bit 2; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_2
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_2:
        ROR product+1
        ROR product
        // Process coefficient bit 3; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_3
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_3:
        ROR product+1
        ROR product
        // Process coefficient bit 4; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_4
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_4:
        ROR product+1
        ROR product
        // Process coefficient bit 5; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_5
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_5:
        ROR product+1
        ROR product
        // Process coefficient bit 6; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_6
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_6:
        ROR product+1
        ROR product
        // Process coefficient bit 7; carry is the product's seventeenth bit.
        LSR factor
        BCC kqfc_scale_shift_7
        CLC
        LDA product
        ADC magnitude
        STA product
        LDA product+1
        ADC magnitude+1
        STA product+1
kqfc_scale_shift_7:
        ROR product+1
        ROR product
        LDA negative
        BEQ kqfc_scale_positive
        SEC
        LDA #0
        SBC product
        STA product
        LDA #0
        SBC product+1
        TAX
        LDA product
        RTS
kqfc_scale_positive:
        LDA product
        LDX product+1
        RTS
    }
}
kq2d_body_limit_speed

벡터 길이를 보수적으로 추정하고 두 축에 같은 배율을 적용해 전체 속력을 제한합니다.

사용 형식

c
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

max_speed

속도 제한값입니다. 각 축을 제한하는지 전체 벡터를 제한하는지는 해당 항목의 설명을 참조하세요.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

속도 성분의 절댓값과 양의 max_speed를 16383 이하로 유지하세요. 제한값이 0 이하이면 속도를 0으로 만듭니다. NULL은 무시하지만 활성 상태나 질량은 변경 여부에 영향을 주지 않습니다. 정수 양자화로 결과가 제한값보다 작아질 수 있으며, 배율이 매우 작으면 0이 될 수 있습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq2d_body_limit_speed(&body,250);
    result[2]=body.vx; result[3]=body.vy;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

kq2d_body_limit_speed: NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.
NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:90

선언 주석 원문
Conservative circular bound, with a shared Q8 scale for both components.
Quantizes toward zero; very small limits relative to input may yield zero.
Components and positive max_speed must be <= 16383 in magnitude.

kitaqfc/lib/physics2d.c:214

c
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed)
{
    u16 x;
    u16 y;
    u16 swap;
    u16 ratio;
    u16 length;
    if (body == 0) return;
    if (max_speed <= 0) { body->vx = 0; body->vy = 0; return; }
    x = (u16)body->vx;
    y = (u16)body->vy;
    if (body->vx < 0) x = (u16)(0 - body->vx);
    if (body->vy < 0) y = (u16)(0 - body->vy);
    if (x + y <= (u16)max_speed) return;
    if (x < y) { swap = x; x = y; y = swap; }
    // major + minor/2 bounds hypot from above for 0 <= minor <= major.
    if (x + (y >> 1) + 1 <= (u16)max_speed) return;
    // Round the minor/major ratio upward for the 65-entry upper-bound table.
    // The extra unit covers truncation when converting the estimate back to speed.
    ratio = (kq2d__fraction_q8(y, x) + 4) >> 2;
    if (ratio > 64) ratio = 64;
    length = x + (u16)kq2d_scale_q8((s16)x, (s16)kq2d_hypot_extra[(u8)ratio]) + 1;
    if (length <= (u16)max_speed) return;
    ratio = kq2d__fraction_q8((u16)max_speed, length);
    body->vx = kq2d_scale_q8(body->vx, (s16)ratio);
    body->vy = kq2d_scale_q8(body->vy, (s16)ratio);
}
kq2d_body_resolve_surface

움직이는 표면에 대한 상대 속도로 법선 방향 반발, 최소 반발 속력, 접선 방향 마찰을 적용합니다.

사용 형식

c
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

surface

법선, 속도, 반발, 마찰 설정을 담은 KQSurface2D의 포인터입니다.

반환값

법선 방향 입사 속력을 양수로 반환합니다. 입력이 NULL이거나 서로 멀어지는 운동이면 0을 반환합니다.

사용 조건과 제한

길이가 256인 Q8 단위 법선을 사용하세요. 상대 속도 성분은 +/-8191 이내, threshold/kick은 0 이상, 계수는 0..255로 유지하세요. 입사 속력이 임계값을 엄격히 초과할 때만 반발을 적용하며 kick은 최소 반발 속력입니다. 입력이 NULL이거나 서로 멀어지는 운동이면 속도를 바꾸지 않습니다. 접촉 감지와 침투 보정은 별도이며, 활성 상태와 질량은 검사하지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[6]=kq2d_body_resolve_surface(&body,&surface);
    result[7]=body.vx; result[8]=body.vy; result[9]=body.y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

kq2d_body_resolve_surface: NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.
NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:93

선언 주석 원문
Kinematic surface response; returns incoming normal speed, or zero if separating.
Caller resolves penetration separately. Relative components must fit +/-8191.

kitaqfc/lib/physics2d.c:268

c
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface)
{
    s16 vx;
    s16 vy;
    s16 speed;
    s16 tangent;
    s16 bounce = 0;
    s16 impulse;
    s16 friction;
    if (body == 0 || surface == 0) return 0;
    vx = (s16)(body->vx - surface->vx);
    vy = (s16)(body->vy - surface->vy);
    speed = (s16)(kq2d_scale_q8(vx,surface->nx_q8) + kq2d_scale_q8(vy,surface->ny_q8));
    if (speed >= 0) return 0;
    speed = (s16)(0 - speed);
    if (speed > surface->bounce_threshold) bounce = kq2d_scale_q8(speed,(s16)surface->restitution_q8);
    if (bounce < surface->kick) bounce = surface->kick;
    impulse = (s16)(speed + bounce);
    // A resting contact is a tangent constraint, not a tiny rounded bounce.
    if (bounce == 0) {
        tangent = (s16)(kq2d__rest_component(vy,surface->nx_q8) - kq2d__rest_component(vx,surface->ny_q8));
        friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
        if (tangent > friction) tangent = (s16)(tangent - friction);
        else if (tangent < (s16)(0 - friction)) tangent = (s16)(tangent + friction);
        else tangent = 0;
        body->vx = (s16)(surface->vx - kq2d__rest_component(tangent,surface->ny_q8));
        body->vy = (s16)(surface->vy + kq2d__rest_component(tangent,surface->nx_q8));
        return speed;
    }
    body->vx = (s16)(body->vx + kq2d_scale_q8(impulse,surface->nx_q8));
    body->vy = (s16)(body->vy + kq2d_scale_q8(impulse,surface->ny_q8));
    if (surface->friction_q8 != 0) {
        tangent = (s16)(kq2d_scale_q8(vy,surface->nx_q8) - kq2d_scale_q8(vx,surface->ny_q8));
        friction = kq2d_scale_q8(impulse,(s16)surface->friction_q8);
        if (tangent > friction) tangent = friction;
        if (tangent < (s16)(0 - friction)) tangent = (s16)(0 - friction);
        body->vx = (s16)(body->vx + kq2d_scale_q8(tangent,surface->ny_q8));
        body->vy = (s16)(body->vy - kq2d_scale_q8(tangent,surface->nx_q8));
    }
    return speed;
}
kq2d_surface_toi_q8

선형 운동을 가정하고 시작·끝의 부호 있는 간격으로 틱 내부의 교차 시각을 추정합니다.

사용 형식

c
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap);

매개변수

start_gap

틱 시작 시점의 부호 있는 간격입니다. 양수이면 표면 바깥쪽입니다.

end_gap

틱 종료 시점의 부호 있는 간격입니다. 음수이면 표면을 지나 안쪽으로 들어간 상태입니다.

반환값

0..256 범위의 Q8 시각을 반환합니다. 192는 틱의 75%에 해당합니다.

사용 조건과 제한

간격을 +/-8191 이내로 유지하세요. 시작 간격이 0 이하이면 0, 끝 간격이 0 이상이면 256을 반환합니다. 처음부터 겹친 상태와 시각 0은 같은 값을 반환하며, 교차하지 않는 경우와 정확히 끝점에서 접촉하는 경우도 같은 값을 반환합니다. 위치는 변경하지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[4]=kq2d_surface_toi_q8(6,-2);
    result[5]=kq2d_surface_toi_q8(6,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

kq2d_surface_toi_q8: NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.
NTSC — 빨간 물체는 제자리에 있습니다. 초록색 속도선은 왼쪽의 (8,16)에서 오른쪽의 (2,−8)로 바뀌어 파란 표면에서의 반사와 접선 방향 마찰을 보여 줍니다. 패널 원점은 화면 (8,48), (88,48)이며 물체 중심은 각 패널의 (24,40)입니다. 진입 속력은 16이고 교차 시각 예제의 반환값은 192/256입니다. 별도의 속력 제한 호출은 (300,400)을 (147,196)으로 줄입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_surface.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_surface.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics2d.h:96

선언 주석 원문
Linear crossing time in Q8 (0..256); positive separation is outside.
Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.

kitaqfc/lib/physics2d.c:245

c
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap)
{
    if (start_gap <= 0) return 0;
    if (end_gap >= 0) return 256;
    return kq2d__fraction_q8((u16)start_gap, (u16)(start_gap - end_gap));
}

physics3d

중심과 축별 반크기로 표현하는, 회전하지 않는 3D 박스용 물리 기능입니다. 질량/역질량으로 접촉 가중치를 정하고 restitution_q8로 반발을 제어하며 ax/ay/az로 지속 가속도를 적용합니다. 렌더링은 별도입니다. 샘플은 같은 물체의 정면도와 평면도를 그려 깊이 방향 이동과 벽 반발을 보여 줍니다.

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

last_impact_speed는 접촉 축을 따라 서로 가까워지는 속도의 최댓값을 기록합니다. 적분 시 활성 동적 물체에서는 초기화되지만 정적 물체와 비활성 물체에서는 유지됩니다. break_speed나 KQ3D_BODY_BROKEN은 자동 파괴를 일으키지 않습니다. 파괴 시점은 게임에서 판단하고 flags/active를 직접 변경해야 합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

kq3d_world_init

물체 배열을 3D 월드에 연결하고 중력을 (0,24,0), 반복 횟수를 4, 축별 속도 제한을 512로 설정합니다.

사용 형식

c
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

bodies

body_count개의 원소를 가진 호출자 소유 물체 배열의 시작 주소입니다.

body_count

비활성 항목을 포함한 배열 슬롯 수이며, 범위는 0..255입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

월드는 호출자가 소유한 배열을 빌려 사용합니다. 호출자가 물체 저장 공간을 할당하고 소유하며, 사용하는 동안 유효하게 유지해야 합니다. 월드를 초기화해도 배열 원소는 초기화되지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq3d_world_init(&world,bodies,2);
    result[0]=world.gravity_y; result[1]=world.solver_iterations; result[2]=world.max_speed;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_world_init: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:47

선언 주석 원문
Borrow the body array without initializing it. Set gravity (0,24,0), four
contact passes and a per-axis speed limit of 512. Null world is ignored;
the caller retains ownership and must keep the array alive.

kitaqfc/lib/physics3d.c:125

c
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count)
{
    if (world == 0) return;

    world->bodies = bodies;
    world->body_count = body_count;
    world->gravity_x = 0;
    world->gravity_y = 24;
    world->gravity_z = 0;
    world->solver_iterations = 4;
    world->max_speed = 512;
}
kq3d_body_init

3D 중심, 반크기, 질량을 초기화합니다. 속도, 가속도, 충격값, 플래그를 0으로 만들고 물체를 활성화합니다.

사용 형식

c
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

x

중심 또는 점의 X 좌표입니다. 샘플에서는 픽셀 단위입니다.

y

중심 또는 점의 Y 좌표입니다. 샘플에서는 아래쪽이 양의 방향입니다.

z

중심의 Z 좌표입니다. 샘플의 평면도에서는 세로 방향으로 표시됩니다.

half_x

X축 반크기입니다. 박스의 전체 너비는 이 값의 두 배입니다.

half_y

Y축 반크기입니다. 박스의 전체 높이는 이 값의 두 배입니다.

half_z

Z축 반크기입니다. 전체 깊이는 이 값의 두 배입니다.

mass_q8

256이 1.0을 나타내는 질량값입니다. 0 이하이면 정적 물체가 됩니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

반발 계수는 224, break_speed는 0입니다. 질량은 kq3d_body_set_mass의 규칙을 따릅니다. NULL은 무시하며, 호출자가 유효한 반크기를 지정해야 합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq3d_body_init(&temp,10,20,30,8,8,8,256);
    result[3]=temp.inv_mass_q8; result[4]=temp.restitution_q8; result[5]=temp.active;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_body_init: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:51

선언 주석 원문
Initialize an active centered box with zero velocity/acceleration,
restitution 224, cleared impact/flags and mass set through the weight helper.
The caller supplies valid half extents. Null body is ignored.

kitaqfc/lib/physics3d.c:161

c
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8)
{
    if (body == 0) return;

    body->x = x;
    body->y = y;
    body->z = z;
    body->vx = 0;
    body->vy = 0;
    body->vz = 0;
    body->ax = 0;
    body->ay = 0;
    body->az = 0;
    body->half_x = half_x;
    body->half_y = half_y;
    body->half_z = half_z;
    body->restitution_q8 = 224;
    body->break_speed = 0;
    body->last_impact_speed = 0;
    body->active = 1;
    body->flags = 0;
    kq3d_body_set_mass(body, mass_q8);
}
kq3d_body_set_mass

요청한 질량을 저장하고 위치 보정과 반발에 사용할 역질량 가중치를 계산합니다.

사용 형식

c
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8);

매개변수

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

mass_q8

256이 1.0을 나타내는 질량값입니다. 0 이하이면 정적 물체가 됩니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

질량이 0 이하이면 역질량은 0이 되어 정적 물체가 됩니다. 양의 질량에는 정수식 32767/max(mass,64)를 사용하며 최솟값은 1입니다. 저장된 질량은 요청값 그대로이므로 이 가중치는 정확한 Q8 역수가 아닙니다. 질량 256의 역질량은 127입니다. 속도와 활성 상태는 유지하며 NULL은 무시합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq3d_body_set_mass(&temp,32); result[6]=temp.mass_q8; result[7]=temp.inv_mass_q8;
    kq3d_body_set_mass(&temp,0); result[8]=temp.inv_mass_q8;
    kq3d_body_set_mass(&temp,256);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_body_set_mass: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:56

선언 주석 원문
Store the requested mass; nonpositive mass makes the body static.
For positive mass compute inverse weight as max(1,32767/max(mass,64)).
The stored mass retains the original input; this is a relative solver weight,
not an exact Q8 reciprocal. Null body is ignored.

kitaqfc/lib/physics3d.c:142

c
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8)
{
    if (body == 0) return;

    body->mass_q8 = mass_q8;
    if (mass_q8 <= 0)
    {
        body->inv_mass_q8 = 0;
        return;
    }

    if (mass_q8 < 64) mass_q8 = 64;
    body->inv_mass_q8 = (s16)(32767 / mass_q8);
    if (body->inv_mass_q8 <= 0) body->inv_mass_q8 = 1;
}
kq3d_step

활성 동적 물체를 적분하고, 월드의 활성 항목 사이에서 접촉 위치 보정, 반발, 충격 기록을 처리합니다.

사용 형식

c
void kq3d_step(KQWorld3D* world);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

월드는 호출자가 소유한 배열을 빌려 사용합니다. 호출자가 물체 저장 공간을 할당하고 소유하며, 사용하는 동안 유효하게 유지해야 합니다. 월드를 초기화해도 배열 원소는 초기화되지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

박스 적분 및 충돌 함수는 임시 저장 공간을 공유합니다. 인터럽트에서 이 물리 함수들을 재진입 호출하지 마세요.

접촉에는 침투 깊이가 가장 작은 축을 사용하며, 같으면 X, Y, Z 순으로 우선합니다. 서로 가까워지는 운동일 때만 반발을 적용합니다. 역질량과 두 반발 계수의 평균을 사용하며, 평균은 0..256으로 제한합니다. 빠른 관통, 회전, 각운동량, 마찰은 처리하지 않습니다.

last_impact_speed는 접촉 축을 따라 서로 가까워지는 속도의 최댓값을 기록합니다. 적분 시 활성 동적 물체에서는 초기화되지만 정적 물체와 비활성 물체에서는 유지됩니다. break_speed나 KQ3D_BODY_BROKEN은 자동 파괴를 일으키지 않습니다. 파괴 시점은 게임에서 판단하고 flags/active를 직접 변경해야 합니다.

world나 배열이 NULL이면 무시합니다. solver_iterations가 0이어도 접촉 처리를 한 번 수행합니다.

중간 연산 impact*(256+restitution)과 correction*inverse-mass도 s16입니다. 곱이 32767 이내가 되도록 하세요. 예를 들어 restitution이 256이면 impact는 63 이하여야 합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq3d_step(&world);
    result[16]=bodies[0].x; result[17]=bodies[0].y; result[18]=bodies[0].z;
    result[19]=bodies[0].vx; result[20]=bodies[0].vz;
    result[21]=bodies[0].last_impact_speed; result[22]=bodies[0].flags;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_step: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:62

선언 주석 원문
Integrate active dynamic bodies and reset their impact values, then solve
each active unordered pair for the configured passes (zero means one).
Static bodies retain previous impact maxima until the game clears them.
Acceleration persists; flags and break thresholds remain game-owned.
This discrete solver uses non-reentrant helper scratch and can miss fast crossings.

kitaqfc/lib/physics3d.c:323

c
void kq3d_step(KQWorld3D* world)
{
    if (world == 0) return;
    if (world->bodies == 0) return;

    KQBody3D* bodies = world->bodies;
    u8 count = world->body_count;

    u8 i;
    for (i = 0; i < count; i++)
    {
        KQBody3D* b = &bodies[(u8)i];
        if (b->active == 0) continue;
        if (b->inv_mass_q8 <= 0) continue;

        b->last_impact_speed = 0;
        kq3d_asm_value = b->vx;
        kq3d_asm_operand = world->gravity_x;
        kq3d__add_value();
        kq3d_asm_operand = b->ax;
        kq3d__add_value();
        kq3d_asm_limit = world->max_speed;
        kq3d__clamp_value();
        b->vx = kq3d_asm_value;

        kq3d_asm_value = b->vy;
        kq3d_asm_operand = world->gravity_y;
        kq3d__add_value();
        kq3d_asm_operand = b->ay;
        kq3d__add_value();
        kq3d_asm_limit = world->max_speed;
        kq3d__clamp_value();
        b->vy = kq3d_asm_value;

        kq3d_asm_value = b->vz;
        kq3d_asm_operand = world->gravity_z;
        kq3d__add_value();
        kq3d_asm_operand = b->az;
        kq3d__add_value();
        kq3d_asm_limit = world->max_speed;
        kq3d__clamp_value();
        b->vz = kq3d_asm_value;

        kq3d_asm_value = b->x;
        kq3d_asm_operand = b->vx;
        kq3d__add_value();
        b->x = kq3d_asm_value;

        kq3d_asm_value = b->y;
        kq3d_asm_operand = b->vy;
        kq3d__add_value();
        b->y = kq3d_asm_value;

        kq3d_asm_value = b->z;
        kq3d_asm_operand = b->vz;
        kq3d__add_value();
        b->z = kq3d_asm_value;
    }

    u8 iterations = world->solver_iterations;
    if (iterations == 0) iterations = 1;

    u8 it;
    for (it = 0; it < iterations; it++)
    {
        for (i = 0; i < count; i++)
        {
            KQBody3D* a = &bodies[(u8)i];
            if (a->active == 0) continue;

            u8 j;
            for (j = (u8)(i + 1); j < count; j++)
            {
                KQBody3D* b = &bodies[(u8)j];
                if (b->active == 0) continue;
                kq3d__resolve_pair(a, b);
            }
        }
    }
}
kq3d_integrate_body

활성 동적 물체 하나의 충격값을 초기화하고 월드 중력과 물체 가속도를 더합니다. 각 축 속도를 제한한 다음 위치를 갱신합니다.

사용 형식

c
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);

매개변수

world

물체 배열과 시뮬레이션 설정을 보관하는 월드의 포인터입니다.

body

수정할 물체의 포인터입니다. 이 연산에서 사용하지 않는 필드는 유지됩니다.

반환값

반환값은 없습니다. 이 항목에서는 갱신되는 필드와 변경이 적용되는 시점을 설명합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

박스 적분 및 충돌 함수는 임시 저장 공간을 공유합니다. 인터럽트에서 이 물리 함수들을 재진입 호출하지 마세요.

last_impact_speed는 접촉 축을 따라 서로 가까워지는 속도의 최댓값을 기록합니다. 적분 시 활성 동적 물체에서는 초기화되지만 정적 물체와 비활성 물체에서는 유지됩니다. break_speed나 KQ3D_BODY_BROKEN은 자동 파괴를 일으키지 않습니다. 파괴 시점은 게임에서 판단하고 flags/active를 직접 변경해야 합니다.

ax/ay/az는 유지되며 다음 적분에서도 다시 적용됩니다. 이 함수만으로는 접촉을 해결하지 않습니다. NULL, 비활성 물체, 정적 물체는 변경하지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
kq3d_integrate_body(&world,&temp);
    result[9]=temp.x; result[10]=temp.y; result[11]=temp.z; result[12]=temp.last_impact_speed;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_integrate_body: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:67

선언 주석 원문
Reset impact for one active dynamic body, add gravity and persistent body
acceleration, clamp velocity per axis and advance position. Static/inactive
bodies retain impact state. Null inputs are ignored; no contacts are solved.
Use a nonnegative speed limit and avoid overflow before clamping.

kitaqfc/lib/physics3d.c:267

c
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body)
{
    if (world == 0) return;
    if (body == 0) return;
    if (body->active == 0) return;
    if (body->inv_mass_q8 <= 0) return;

    body->last_impact_speed = 0;
    kq3d_asm_value = body->vx;
    kq3d_asm_operand = world->gravity_x;
    kq3d__add_value();
    kq3d_asm_operand = body->ax;
    kq3d__add_value();
    kq3d_asm_limit = world->max_speed;
    kq3d__clamp_value();
    body->vx = kq3d_asm_value;

    kq3d_asm_value = body->vy;
    kq3d_asm_operand = world->gravity_y;
    kq3d__add_value();
    kq3d_asm_operand = body->ay;
    kq3d__add_value();
    kq3d_asm_limit = world->max_speed;
    kq3d__clamp_value();
    body->vy = kq3d_asm_value;

    kq3d_asm_value = body->vz;
    kq3d_asm_operand = world->gravity_z;
    kq3d__add_value();
    kq3d_asm_operand = body->az;
    kq3d__add_value();
    kq3d_asm_limit = world->max_speed;
    kq3d__clamp_value();
    body->vz = kq3d_asm_value;

    kq3d_asm_value = body->x;
    kq3d_asm_operand = body->vx;
    kq3d__add_value();
    body->x = kq3d_asm_value;

    kq3d_asm_value = body->y;
    kq3d_asm_operand = body->vy;
    kq3d__add_value();
    body->y = kq3d_asm_value;

    kq3d_asm_value = body->z;
    kq3d_asm_operand = body->vz;
    kq3d__add_value();
    body->z = kq3d_asm_value;
}
kq3d_overlap_aabb

중심으로 표현한 두 박스가 세 축 모두에서 엄격히 겹치는지 검사합니다.

사용 형식

c
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b);

매개변수

a

유효한 물체 포인터입니다. 이 질의는 활성 상태, 질량, 플래그를 무시합니다.

b

유효한 물체 포인터입니다. 이 질의는 활성 상태, 질량, 플래그를 무시합니다.

반환값

내부가 겹치면 1, 접촉만 하거나 떨어져 있으면 0을 반환합니다.

사용 조건과 제한

위치, 반지름, 반크기, 속도, 중력에는 일관된 정수 단위를 사용하세요. 한 단계는 시뮬레이션 1틱입니다. 샘플에서는 1단위가 1픽셀입니다. 이 함수들은 위치를 Q8.8로 자동 변환하거나 화면에 그리지 않습니다.

합, 차, 반크기의 합, 접촉 위치 보정에 쓰는 곱이 부호 있는 16비트 범위 안에 들어가도록 하세요. 속도 제한은 덧셈 후에 적용되므로 덧셈 도중의 오버플로를 막지 못합니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[14]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);
    bodies[1].z=32; result[15]=kq3d_overlap_aabb(&bodies[0],&bodies[1]);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_overlap_aabb: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:71

선언 주석 원문
Test strict overlap on all three axes; face-only contact returns false.
Valid pointers are required and flags are ignored. Center differences and
extent sums must fit the signed 16-bit magnitude calculations.

kitaqfc/lib/physics3d.c:188

c
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b)
{
    s16 dx = kq3d__abs_s16((s16)(b->x - a->x));
    s16 dy = kq3d__abs_s16((s16)(b->y - a->y));
    s16 dz = kq3d__abs_s16((s16)(b->z - a->z));

    s16 sx = (s16)(a->half_x + b->half_x);
    s16 sy = (s16)(a->half_y + b->half_y);
    s16 sz = (s16)(a->half_z + b->half_z);

    if (dx >= sx) return 0;
    if (dy >= sy) return 0;
    if (dz >= sz) return 0;
    return 1;
}
kq3d_dot_q8_8

부호 있는 곱 세 개를 각각 256으로 나누고 각 항을 0 방향으로 버림한 다음 더합니다.

사용 형식

c
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az);

매개변수

x

대응하는 계수를 곱할 부호 있는 16비트 성분입니다. 위치나 속도를 나타낼 수 있습니다.

y

대응하는 계수를 곱할 부호 있는 16비트 성분입니다. 위치나 속도를 나타낼 수 있습니다.

z

대응하는 계수를 곱할 부호 있는 16비트 성분입니다. 위치나 속도를 나타낼 수 있습니다.

ax

이 축의 부호 있는 바이트 계수(-128..127)이며, 1/256 배율로 적용합니다.

ay

이 축의 부호 있는 바이트 계수(-128..127)이며, 1/256 배율로 적용합니다.

az

이 축의 부호 있는 바이트 계수(-128..127)이며, 1/256 배율로 적용합니다.

반환값

양자화한 세 항의 합을 s16으로 반환합니다.

사용 조건과 제한

각 항을 먼저 0 방향으로 버림하면 전체 합을 한 번 시프트하는 것과 결과가 다를 수 있습니다. 계수에는 1.0=256을 저장할 수 없습니다. 최종 합은 포화 처리하지 않습니다.

헤더를 포함하는 것뿐 아니라 구현 파일도 컴파일하세요. 2D에는 fixed.c와 physics2d.c를 사용하고 3D에는 physics3d.c도 추가하세요. 예제는 MMC3, CHR RAM, PRG RAM $6800–$6FFF의 그리기 저장 공간을 사용합니다. 이 그리기 버퍼는 물리 라이브러리 자체의 필수 조건은 아닙니다.

사용 예

c
result[13]=kq3d_dot_q8_8(256,128,0,64,-64,127);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

kq3d_dot_q8_8: NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.
NTSC — 왼쪽은 X/Y 정면도, 오른쪽은 X/Z 평면도입니다. 초록색 윤곽은 초기 위치 (24,24,16), 빨간 상자는 한 틱 후의 (36,24,24), 파란 영역은 고정 장애물입니다. X 속도는 16에서 −14로 바뀌고 Z 속도는 8을 유지합니다. 충격값은 16이지만 break_speed=1이어도 플래그는 0입니다. 패널 원점은 화면 (8,48), (88,48)입니다.

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\physics_body3d.c -I .\kitaqfc\lib -I .\kitaq-docs\samples --mapper=mmc3 --nes-chr-ram -o .\out\physics_body3d.nes --no-cache --no-disasm
구현과 소스

kitaqfc/lib/physics3d.h:75

선언 주석 원문
Sum three signed products, dividing each by 256 (toward zero) before addition.
Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.

kitaqfc/lib/physics3d.c:407

c
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az)
{
    return (s16)(kq2d_scale_q8(x,(s16)ax) + kq2d_scale_q8(y,(s16)ay) + kq2d_scale_q8(z,(s16)az));
}

ppu.h — PPU 화면 기능 선언

runtime.c와 함께 사용하지 말고 ppu.c를 한 번만 포함하거나 컴파일하세요. 상위·하위 주소 바이트를 받는 함수 앞에서 __ppu_read_status()를 호출해 공유 래치를 초기화하세요. ppu.h에는 이 함수들의 선언이 없으며, 그 안의 화면·팔레트·네임테이블용 선언 네 개는 배포본에 구현이 없습니다. 여기 나온 함수는 ppu.c를 포함해 사용하며 ppu.h만으로는 사용할 수 없습니다.

nes_ppu_clear_nt

nes_ppu_clear_nt(nt_base, tile, attr)는 배포본에 구현이 없습니다. 실행 가능한 예제는 네임테이블 0을 두 단계로 지웁니다. __nametable_rect_nt(0,0,0,32,30,0)으로 타일 칸 960개를 0으로 만들고 __vram_fill(0x23C0,0,64)로 압축된 속성 64바이트를 지웁니다. 둘 다 렌더링을 끄고 증가량 1로 실행합니다. 논리 네임테이블은 카트리지 미러링에 따라 저장 공간을 공유할 수 있습니다.

사용 형식

c
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr);

매개변수

nt_base / tile / attr

프로토타입에서 nt_base는 부호 없는 16비트, tile과 attr는 부호 없는 8비트입니다. 이 매개변수의 주소 선택, 채우기, 검증을 구현하는 본체는 없습니다. 대안은 논리 네임테이블 0을 명시적으로 선택하고 타일과 압축된 속성 값을 따로 지정합니다.

반환값

선언된 반환 형식은 void입니다. 링크할 구현이 없으므로 이 이름에 대해 설명할 실행 가능한 반환 동작은 없습니다.

사용 조건과 제한

샘플은 구현된 대안을 보여주며 해당 선언의 호출 성공 예제가 아닙니다. 검증에는 각 미구현 이름의 미해결 심벌 빌드와 대안의 별도 에뮬레이터 검사가 포함됩니다. 선언된 이름을 호출하려면 일치하는 구현을 직접 제공해야 하며 다른 헤더를 포함하는 것만으로 본체가 생기지는 않습니다.

사용 예

c
nes_ppu_clear_nt(0x2000,0,0); // All 960 tiles and 64 packed attribute bytes.
// Nametable reads are buffered: discard one read, then inspect all 1024 bytes.
__ppu_addr(0x2000);discarded_read=sample_ppudata;
for(i=0;i<1024;i++)if(sample_ppudata!=0)failures=1;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

실행 가능한 대안은 렌더링을 끄고 화면을 준비하며, 팔레트 설정과 타일·속성 초기화 후 이 이미지를 그립니다. (0,16)부터 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고 (0,96)부터 녹색으로 채워집니다. 각 영역은 256×64픽셀이지만 255개의 타일 바이트를 전송하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. y=208의 x=16에는 빨간 8×8 정사각형, x=224에는 왼쪽 4×8 막대, x=240에는 8행 삼각형이 표시됩니다. FAILED CHECKS는 0입니다. 별도 상태 검사는 제어 레지스터 두 개, 32바이트 팔레트 전송, 네임테이블 1,024바이트 전체 초기화를 확인합니다. 네 선언의 직접 호출은 링크 실패가 예상 결과입니다.

nes_ppu_clear_nt: 다른 사용 방법
다른 사용 방법

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.h:14

선언 주석 원문
Fill 960 tiles and 64 packed attributes; preserve PPUCTRL.
Accept exactly $2000/$2400/$2800/$2C00; invalid bases are ignored.

kitaqfc/lib/ppu.c:60

c
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr)
{
    unsigned char table;
    unsigned char saved_ctrl;
    if (nt_base != 0x2000 && nt_base != 0x2400 && nt_base != 0x2800 && nt_base != 0x2C00) return;
    table = (unsigned char)((nt_base - 0x2000) >> 10);
    saved_ctrl=__ppu_ctrl_get();
    __ppu_ctrl_set((u8)(saved_ctrl & 0xFB));
    __nametable_rect_nt(table,0,0,32,30,tile);
    __vram_fill((u16)(nt_base+0x3C0),attr,64);
    __ppu_ctrl_set(saved_ctrl);
}
nes_ppu_fill

hi와 lo로 주소를 설정하고 value를 PPUDATA에 count번 씁니다. 이 ppu.c 함수는 8비트 개수로 즉시 채우며, 호출자가 먼저 래치를 초기화해야 합니다. 행이나 사각형 경계에서 잘라내지 않습니다.

사용 형식

c
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count);

매개변수

hi / lo

hi와 lo는 PPU 주소의 상위·하위 바이트입니다. 예를 들어 0x23, 0x42는 0x2342를 뜻합니다. 타일 좌표가 아닌 주소 바이트이며, 일반적인 PPU 주소 마스킹과 미러링이 적용됩니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

count

부호 없는 8비트 바이트 수 0..255입니다. 0이면 주소만 설정하고 데이터는 쓰지 않습니다. 0인 경우에도 공유 래치를 초기화하지 않으므로 호출 전에 준비하세요.

반환값

없음(void).

사용 조건과 제한

runtime.c와 함께 사용하지 말고 ppu.c를 한 번만 포함하거나 컴파일하세요. 상위·하위 주소 바이트를 받는 함수 앞에서 __ppu_read_status()를 호출해 공유 래치를 초기화하세요. ppu.h에는 이 함수들의 선언이 없으며, 그 안의 화면·팔레트·네임테이블용 선언 네 개는 배포본에 구현이 없습니다. 여기 나온 함수는 ppu.c를 포함해 사용하며 ppu.h만으로는 사용할 수 없습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
__ppu_read_status();nes_ppu_fill(0x21,0x80,1,255);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

별도의 상위·하위 주소 예제에서는 명시적인 래치 준비와 8비트 전송 개수를 배웁니다. 파란색과 녹색 영역은 각각 255바이트를 사용하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. 주소 설정과 길이 0 연산을 나타내는 빨간 표시의 위치·모양은 런타임 예제와 같습니다. FAILED CHECKS는 0이며, 이 프로그램은 런타임 대기 함수를 검증하지 않습니다.

nes_ppu_fill: 함수 쌍
함수 쌍

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.c:27

선언 주석 원문
Set the address and issue count identical PPUDATA writes without waiting for VBlank.

kitaqfc/lib/ppu.c:27

c
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count)
{
    unsigned char i;
    nes_ppu_seek_bytes(hi, lo);
    for (i = 0; i != count; ++i)
        kq_ppu_data_register = value;
}
nes_ppu_load_palette

nes_ppu_load_palette(pal32)는 선언만 있으며 이 이름의 팔레트 전송은 구현되지 않았습니다. 동작하는 예제는 렌더링을 끄고 주소 증가량을 1로 설정한 뒤 __palette_bg_load로 배경 16바이트, __palette_sp_load로 스프라이트 16바이트를 전송합니다. 하드웨어 팔레트 주소 공유가 적용되므로 스프라이트 팔레트 쓰기가 배경 색상 항목도 바꿀 수 있습니다.

사용 형식

c
void nes_ppu_load_palette(unsigned char* pal32);

매개변수

pal32

pal32는 부호 없는 바이트 포인터로 선언되어 있습니다. 이름에 32가 있어도 32바이트 전송 구현이 제공되는 것은 아닙니다. 동작하는 대안에서는 각 팔레트 내장 함수에 읽을 수 있는 16바이트를 제공하고 반환할 때까지 원본 뱅크를 유지하세요.

반환값

선언된 반환 형식은 void입니다. 링크할 구현이 없으므로 이 이름에 대해 설명할 실행 가능한 반환 동작은 없습니다.

사용 조건과 제한

샘플은 구현된 대안을 보여주며 해당 선언의 호출 성공 예제가 아닙니다. 검증에는 각 미구현 이름의 미해결 심벌 빌드와 대안의 별도 에뮬레이터 검사가 포함됩니다. 선언된 이름을 호출하려면 일치하는 구현을 직접 제공해야 하며 다른 헤더를 포함하는 것만으로 본체가 생기지는 않습니다.

사용 예

c
nes_ppu_load_palette(stream_palette); // Sixteen BG bytes followed by sixteen sprite bytes.
// Palette reads are immediate; both halves use the same original sample palette.
__ppu_addr(0x3F00);
for(i=0;i<32;i++)if(sample_ppudata!=stream_palette[(u8)(i&15)])failures=1;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

실행 가능한 대안은 렌더링을 끄고 화면을 준비하며, 팔레트 설정과 타일·속성 초기화 후 이 이미지를 그립니다. (0,16)부터 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고 (0,96)부터 녹색으로 채워집니다. 각 영역은 256×64픽셀이지만 255개의 타일 바이트를 전송하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. y=208의 x=16에는 빨간 8×8 정사각형, x=224에는 왼쪽 4×8 막대, x=240에는 8행 삼각형이 표시됩니다. FAILED CHECKS는 0입니다. 별도 상태 검사는 제어 레지스터 두 개, 32바이트 팔레트 전송, 네임테이블 1,024바이트 전체 초기화를 확인합니다. 네 선언의 직접 호출은 링크 실패가 예상 결과입니다.

nes_ppu_load_palette: 다른 사용 방법
다른 사용 방법

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.h:11

선언 주석 원문
Copy 32 bytes into $3F00..$3F1F with increment one; preserve PPUCTRL.
Null is ignored. Hardware palette mirrors apply; source bank must stay mapped.

kitaqfc/lib/ppu.c:48

c
void nes_ppu_load_palette(unsigned char* pal32)
{
    u8 saved_ctrl;
    if (pal32 == 0) return;
    saved_ctrl=__ppu_ctrl_get();
    __ppu_ctrl_set((u8)(saved_ctrl & 0xFB));
    __palette_bg_load(pal32);
    __palette_sp_load(pal32 + 16);
    __ppu_ctrl_set(saved_ctrl);
}
nes_ppu_screen_off

nes_ppu_screen_off는 ppu.h에 선언되어 있지만 배포본에 함수 본체가 없어 직접 호출하면 링크할 수 없습니다. 실행 가능한 예제는 __ppu_off()로 PPUMASK와 소프트웨어 저장값을 0으로 만들어 렌더링을 중지합니다. NMI는 비활성화되지 않으므로 대량의 화면 준비를 위해 PPUCTRL에도 따로 0을 씁니다.

사용 형식

c
void nes_ppu_screen_off(void);

반환값

선언된 반환 형식은 void입니다. 링크할 구현이 없으므로 이 이름에 대해 설명할 실행 가능한 반환 동작은 없습니다.

사용 조건과 제한

샘플은 구현된 대안을 보여주며 해당 선언의 호출 성공 예제가 아닙니다. 검증에는 각 미구현 이름의 미해결 심벌 빌드와 대안의 별도 에뮬레이터 검사가 포함됩니다. 선언된 이름을 호출하려면 일치하는 구현을 직접 제공해야 하며 다른 헤더를 포함하는 것만으로 본체가 생기지는 않습니다.

사용 예

c
nes_ppu_screen_off();__ppu_ctrl_set(0); // Disable NMI during this bulk preparation.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

실행 가능한 대안은 렌더링을 끄고 화면을 준비하며, 팔레트 설정과 타일·속성 초기화 후 이 이미지를 그립니다. (0,16)부터 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고 (0,96)부터 녹색으로 채워집니다. 각 영역은 256×64픽셀이지만 255개의 타일 바이트를 전송하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. y=208의 x=16에는 빨간 8×8 정사각형, x=224에는 왼쪽 4×8 막대, x=240에는 8행 삼각형이 표시됩니다. FAILED CHECKS는 0입니다. 별도 상태 검사는 제어 레지스터 두 개, 32바이트 팔레트 전송, 네임테이블 1,024바이트 전체 초기화를 확인합니다. 네 선언의 직접 호출은 링크 실패가 예상 결과입니다.

nes_ppu_screen_off: 다른 사용 방법
다른 사용 방법

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.h:6

선언 주석 원문
Link ppu.c. Bulk palette/nametable transfers require rendering to be disabled.
Disable background and sprite rendering, preserving other PPUMASK bits.

kitaqfc/lib/ppu.c:36

c
void nes_ppu_screen_off(void) { __ppu_mask_set((u8)(__ppu_mask_get() & 0xE7)); }
nes_ppu_screen_on

nes_ppu_screen_on(ctrl, mask)는 구현 없는 선언이므로 이 이름을 호출해 화면을 켤 수 없습니다. 동작하는 예제는 스크롤을 설정한 뒤 __ppu_ctrl_set(0x80)과 __ppu_mask_set(0x0A)를 호출합니다. NMI와 배경 렌더링이 활성화되고 왼쪽 끝 8픽셀에도 배경이 표시되며 스프라이트는 비활성화됩니다. 게임에 맞는 레지스터 값을 선택하세요.

사용 형식

c
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask);

매개변수

ctrl / mask

선언에서 ctrl과 mask는 부호 없는 8비트 형식입니다. 실행 동작을 정의하는 함수 본체는 없습니다. 동작하는 대안은 별도의 내장 함수로 원하는 PPUCTRL과 PPUMASK 값을 씁니다.

반환값

선언된 반환 형식은 void입니다. 링크할 구현이 없으므로 이 이름에 대해 설명할 실행 가능한 반환 동작은 없습니다.

사용 조건과 제한

샘플은 구현된 대안을 보여주며 해당 선언의 호출 성공 예제가 아닙니다. 검증에는 각 미구현 이름의 미해결 심벌 빌드와 대안의 별도 에뮬레이터 검사가 포함됩니다. 선언된 이름을 호출하려면 일치하는 구현을 직접 제공해야 하며 다른 헤더를 포함하는 것만으로 본체가 생기지는 않습니다.

사용 예

c
nes_ppu_screen_on(0x80,0x0A);
// NMI and background rendering are now enabled, with left-edge background visible.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

실행 가능한 대안은 렌더링을 끄고 화면을 준비하며, 팔레트 설정과 타일·속성 초기화 후 이 이미지를 그립니다. (0,16)부터 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고 (0,96)부터 녹색으로 채워집니다. 각 영역은 256×64픽셀이지만 255개의 타일 바이트를 전송하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. y=208의 x=16에는 빨간 8×8 정사각형, x=224에는 왼쪽 4×8 막대, x=240에는 8행 삼각형이 표시됩니다. FAILED CHECKS는 0입니다. 별도 상태 검사는 제어 레지스터 두 개, 32바이트 팔레트 전송, 네임테이블 1,024바이트 전체 초기화를 확인합니다. 네 선언의 직접 호출은 링크 실패가 예상 결과입니다.

nes_ppu_screen_on: 다른 사용 방법
다른 사용 방법

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_implemented.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_implemented.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.h:8

선언 주석 원문
Set scroll to (0,0), then apply the requested PPUCTRL and PPUMASK values.

kitaqfc/lib/ppu.c:39

c
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask)
{
    __ppu_read_status();
    __scroll_set(0,0);
    __ppu_ctrl_set(ctrl);
    __ppu_mask_set(mask);
}
nes_ppu_seek_bytes

PPUSTATUS를 읽어 공유 PPU 주소 래치를 초기화한 다음, 별도의 hi·lo 바이트로 주소를 설정합니다. 게임에서 주소를 이미 두 바이트로 저장하는 경우 유용합니다.

사용 형식

c
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo);

매개변수

hi / lo

hi와 lo는 PPU 주소의 상위·하위 바이트입니다. 예를 들어 0x23, 0x42는 0x2342를 뜻합니다. 타일 좌표가 아닌 주소 바이트이며, 일반적인 PPU 주소 마스킹과 미러링이 적용됩니다.

반환값

없음(void).

사용 조건과 제한

runtime.c와 함께 사용하지 말고 ppu.c를 한 번만 포함하거나 컴파일하세요. 상위·하위 주소 바이트를 받는 함수 앞에서 __ppu_read_status()를 호출해 공유 래치를 초기화하세요. ppu.h에는 이 함수들의 선언이 없으며, 그 안의 화면·팔레트·네임테이블용 선언 네 개는 배포본에 구현이 없습니다. 여기 나온 함수는 ppu.c를 포함해 사용하며 ppu.h만으로는 사용할 수 없습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
nes_ppu_seek_bytes(0x23,0x42);
PPUDATA=1; // Red 8x8 marker at pixel (16,208).

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

별도의 상위·하위 주소 예제에서는 명시적인 래치 준비와 8비트 전송 개수를 배웁니다. 파란색과 녹색 영역은 각각 255바이트를 사용하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. 주소 설정과 길이 0 연산을 나타내는 빨간 표시의 위치·모양은 런타임 예제와 같습니다. FAILED CHECKS는 0이며, 이 프로그램은 런타임 대기 함수를 검증하지 않습니다.

nes_ppu_seek_bytes: 함수 쌍
함수 쌍

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.c:8

선언 주석 원문
Reset the shared scroll/address latch before writing the address byte pair.

kitaqfc/lib/ppu.c:8

c
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo)
{
    __ppu_read_status();
    kq_ppu_addr_register = hi;
    kq_ppu_addr_register = lo;
}
nes_ppu_write_bytes

hi와 lo로 주소를 설정한 뒤 원본의 연속된 count바이트를 PPUDATA에 직접 씁니다. 이 ppu.c 함수는 8비트 개수를 받으며 주소 래치 초기화는 호출자가 담당합니다. 큐를 사용하지 않고 반환 전에 전송을 마칩니다.

사용 형식

c
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count);

매개변수

hi / lo

hi와 lo는 PPU 주소의 상위·하위 바이트입니다. 예를 들어 0x23, 0x42는 0x2342를 뜻합니다. 타일 좌표가 아닌 주소 바이트이며, 일반적인 PPU 주소 마스킹과 미러링이 적용됩니다.

src

요청한 수만큼의 바이트를 읽을 수 있는 CPU 포인터입니다. 동기 전송이 반환될 때까지 원본의 매핑과 내용을 유지하세요. 뱅크 전환, 큐에 포인터 보관, 원본 길이 검사는 하지 않습니다. 길이가 0이면 포인터를 역참조하지 않습니다.

count

부호 없는 8비트 바이트 수 0..255입니다. 0이면 주소만 설정하고 데이터는 쓰지 않습니다. 0인 경우에도 공유 래치를 초기화하지 않으므로 호출 전에 준비하세요.

반환값

없음(void).

사용 조건과 제한

runtime.c와 함께 사용하지 말고 ppu.c를 한 번만 포함하거나 컴파일하세요. 상위·하위 주소 바이트를 받는 함수 앞에서 __ppu_read_status()를 호출해 공유 래치를 초기화하세요. ppu.h에는 이 함수들의 선언이 없으며, 그 안의 화면·팔레트·네임테이블용 선언 네 개는 배포본에 구현이 없습니다. 여기 나온 함수는 ppu.c를 포함해 사용하며 ppu.h만으로는 사용할 수 없습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
__ppu_read_status();nes_ppu_write_bytes(0x20,0x40,source_bytes,255);
source_bytes[0]=3; // The completed transfer retains tile 1.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

별도의 상위·하위 주소 예제에서는 명시적인 래치 준비와 8비트 전송 개수를 배웁니다. 파란색과 녹색 영역은 각각 255바이트를 사용하므로 마지막 8×8 칸인 (248,72)와 (248,152)는 검은색으로 남습니다. 주소 설정과 길이 0 연산을 나타내는 빨간 표시의 위치·모양은 런타임 예제와 같습니다. FAILED CHECKS는 0이며, 이 프로그램은 런타임 대기 함수를 검증하지 않습니다.

nes_ppu_write_bytes: 함수 쌍
함수 쌍

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\ppu_address_pair.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\ppu_address_pair.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/ppu.c:18

선언 주석 원문
Set the address and write count bytes directly through PPUDATA. The current
PPUCTRL increment mode determines how the destination advances.
count is 0..255; zero still changes PPU address/latch state. No bank switch or payload copy is performed.

kitaqfc/lib/ppu.c:18

c
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count)
{
    unsigned char i;
    nes_ppu_seek_bytes(hi, lo);
    for (i = 0; i != count; ++i)
        kq_ppu_data_register = src[i];
}

runtime.h — NMI·OAM·PPU 전송용 C 도우미

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

runtime.c가 CPU 주소 0x0300–0x03BF에 두는 별도의 192바이트 큐입니다. 실제 데이터 레코드는 3+len바이트, 채우기는 4바이트를 사용합니다. nes_vram_queue_used는 인코딩된 레코드 바이트 수이며, PPU 쓰기 횟수나 남은 VRAM 용량이 아닙니다. __vramq_* 내장 큐는 형식이 다르며 이 함수들로 비우거나 조회하거나 실행할 수 없습니다.

레코드 생성과 실행 단계를 분리하세요. 잠금이나 더블 버퍼링은 제공되지 않습니다. 확실하게 준비하려면 추가 중에는 NMI를 끄고, 렌더링을 끈 상태에서 실행하거나 VBlank 안에 처리할 수 있는 분량을 준비한 후 NMI를 켜세요. runtime.c는 커밋 호출 없이 __nes_nmi에서 비어 있지 않은 큐를 자동 실행합니다. 실행 시간 제한, 분할 처리, 스크롤 복원은 없습니다. 큐에 들어간다고 해서 VBlank 시간 안에 전송할 수 있는 것은 아닙니다.

nes_ppu_seek

이후 PPUDATA 접근에 사용할 PPU 주소를 설정합니다. runtime.c의 nes_ppu_seek(ppu_addr)는 PPUSTATUS를 읽어 공유 쓰기 래치를 초기화한 뒤 주소의 상위·하위 바이트를 씁니다. ppu.c의 별도 구현인 nes_ppu_seek(hi, lo)는 래치를 초기화하지 않습니다. 이름은 같지만 서로 다른 함수이므로 하나만 선택하고 둘을 함께 링크하지 마세요.

사용 형식

c
void nes_ppu_seek(unsigned short ppu_addr);

매개변수

ppu_addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

반환값

없음(void).

사용 조건과 제한

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
PPUADDR=0x21; // Deliberately leave the shared address latch half-written.
nes_ppu_seek(0x2342); // This runtime function resets the latch itself.
PPUDATA=1; // Red 8x8 marker at pixel (16,208).

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

런타임 예제에서는 직접 전송과 두 가지 대기 방식을 배웁니다. (0,16)에는 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고, (0,96)에는 녹색으로 채운 영역이 표시됩니다. 각 영역은 256개의 타일 바이트로 만든 256×64픽셀입니다. 원본을 바꿔도 첫 파란 타일은 꽉 찬 상태를 유지합니다. y=208의 x=16, 224, 240에는 주소 설정·길이 0 쓰기·길이 0 채우기를 나타내는 빨간 8×8 정사각형, 왼쪽 4×8 막대, 8행 삼각형이 표시됩니다. 두 대기 표시값은 1, FAILED CHECKS는 0입니다.

nes_ppu_seek: 런타임
런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:28

선언 주석 원문
Reset the PPU address latch with a status read, then write the high/low address bytes.

kitaqfc/lib/runtime.c:207

c
void nes_ppu_seek(unsigned short ppu_addr)
{
    unsigned char latch;
    latch = PPUSTATUS;
    PPUADDR = (unsigned char)(ppu_addr >> 8);
    PPUADDR = (unsigned char)ppu_addr;
    latch = latch;
}
nes_ppu_stream_fill

ppu_addr를 설정한 뒤 같은 1바이트 값 value를 PPUDATA에 len번 씁니다. 반환 전에 완료되는 직접 채우기입니다. 타일 크기, 속성 또는 사각형 경계는 해석하지 않습니다.

사용 형식

c
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len);

매개변수

ppu_addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

len

바이트 수이며 부호 없는 16비트 값 0..65535입니다. 0이어도 PPU 주소 설정과 래치 초기화는 수행하지만 PPUDATA에는 쓰지 않습니다. 대상 크기 검사나 전송 분할이 없으므로 큰 값은 PPU 주소 공간을 순환할 수 있습니다.

반환값

없음(void).

사용 조건과 제한

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
nes_ppu_stream_fill(0x2180,1,256); // Eight complete rows of solid green tiles.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

런타임 예제에서는 직접 전송과 두 가지 대기 방식을 배웁니다. (0,16)에는 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고, (0,96)에는 녹색으로 채운 영역이 표시됩니다. 각 영역은 256개의 타일 바이트로 만든 256×64픽셀입니다. 원본을 바꿔도 첫 파란 타일은 꽉 찬 상태를 유지합니다. y=208의 x=16, 224, 240에는 주소 설정·길이 0 쓰기·길이 0 채우기를 나타내는 빨간 8×8 정사각형, 왼쪽 4×8 막대, 8행 삼각형이 표시됩니다. 두 대기 표시값은 1, FAILED CHECKS는 0입니다.

nes_ppu_stream_fill: 런타임
런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:34

선언 주석 원문
Write value repeatedly through PPUDATA, using the current PPU address increment
mode. This routine does not wait for VBlank or disable rendering.

kitaqfc/lib/runtime.c:232

c
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len)
{
    nes_ppu_seek(ppu_addr);
    while (len != 0)
    {
        PPUDATA = value;
        len = len - 1;
    }
}
nes_ppu_stream_write

ppu_addr에서 시작하여 원본의 연속된 len바이트를 PPUDATA에 직접 씁니다. 주소를 한 번 설정하고 쓸 때마다 함수 내부의 원본 포인터를 이동합니다. 반환 전에 전송이 끝나므로 이후 원본을 바꿔도 VRAM은 바뀌지 않습니다. 호출자의 포인터 변수도 변경되지 않습니다.

사용 형식

c
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len);

매개변수

ppu_addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

src

요청한 수만큼의 바이트를 읽을 수 있는 CPU 포인터입니다. 동기 전송이 반환될 때까지 원본의 매핑과 내용을 유지하세요. 뱅크 전환, 큐에 포인터 보관, 원본 길이 검사는 하지 않습니다. 길이가 0이면 포인터를 역참조하지 않습니다.

len

바이트 수이며 부호 없는 16비트 값 0..65535입니다. 0이어도 PPU 주소 설정과 래치 초기화는 수행하지만 PPUDATA에는 쓰지 않습니다. 대상 크기 검사나 전송 분할이 없으므로 큰 값은 PPU 주소 공간을 순환할 수 있습니다.

반환값

없음(void).

사용 조건과 제한

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

직접 PPU 접근은 렌더링을 끄거나 전체 작업이 안전한 VBlank 구간 안에 끝나는 조건에서 수행하세요. 이 함수들은 대기, 큐 등록, NMI 차단, 스크롤 복원을 하지 않습니다. 호출 중 인터럽트가 공유 래치나 주소를 바꾸지 않도록 하세요. PPUCTRL 비트 2에 따라 대상 주소는 바이트마다 1 또는 32씩 증가합니다. 미러링과 주소 순환은 PPU 동작을 따르며 CHR-ROM에는 쓸 수 없습니다. 접근 후 렌더링 전에 원하는 스크롤 상태를 설정하세요.

사용 예

c
// Rendering/NMI are off; PPUCTRL increment is 1. Transfer 256 bytes now.
nes_ppu_stream_write(0x2040,source_bytes,256);
source_bytes[0]=3; // VRAM already holds tile 1 at the first blue cell.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

런타임 예제에서는 직접 전송과 두 가지 대기 방식을 배웁니다. (0,16)에는 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고, (0,96)에는 녹색으로 채운 영역이 표시됩니다. 각 영역은 256개의 타일 바이트로 만든 256×64픽셀입니다. 원본을 바꿔도 첫 파란 타일은 꽉 찬 상태를 유지합니다. y=208의 x=16, 224, 240에는 주소 설정·길이 0 쓰기·길이 0 채우기를 나타내는 빨간 8×8 정사각형, 왼쪽 4×8 막대, 8행 삼각형이 표시됩니다. 두 대기 표시값은 1, FAILED CHECKS는 0입니다.

nes_ppu_stream_write: 런타임
런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:31

선언 주석 원문
Write len source bytes directly through PPUDATA. Call only during an appropriate
PPU access period; the address increment mode comes from the current PPUCTRL.

kitaqfc/lib/runtime.c:219

c
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len)
{
    nes_ppu_seek(ppu_addr);
    while (len != 0)
    {
        PPUDATA = *src;
        src = src + 1;
        len = len - 1;
    }
}
nes_vblank_wait

PPUSTATUS 비트 7을 폴링하여 이미 설정된 알림을 소비한 뒤 새로운 VBlank 알림을 기다립니다. NMI가 비활성화되어 있어도 동작하지만 큐를 실행하거나 런타임 NMI 카운터를 바꾸지는 않습니다. 상태를 읽을 때마다 VBlank 플래그가 지워지고 스크롤·주소 공유 쓰기 래치가 초기화됩니다. 상태 읽기는 NMI 타이밍에 영향을 줄 수 있으므로 NMI 기반 스케줄링과 분리해서 사용하세요. 시간 제한, 전송, 스크롤 복원은 제공하지 않습니다.

사용 형식

c
void nes_vblank_wait(void);

반환값

없음(void).

사용 조건과 제한

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

사용 예

c
// With NMI disabled, poll a new PPU VBlank flag. No queued work is flushed.
nes_vblank_wait();blank_seen=1;
if((PPUSTATUS&0x80)!=0)failures++; // The wait's status read cleared the flag.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

런타임 예제에서는 직접 전송과 두 가지 대기 방식을 배웁니다. (0,16)에는 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고, (0,96)에는 녹색으로 채운 영역이 표시됩니다. 각 영역은 256개의 타일 바이트로 만든 256×64픽셀입니다. 원본을 바꿔도 첫 파란 타일은 꽉 찬 상태를 유지합니다. y=208의 x=16, 224, 240에는 주소 설정·길이 0 쓰기·길이 0 채우기를 나타내는 빨간 8×8 정사각형, 왼쪽 4×8 막대, 8행 삼각형이 표시됩니다. 두 대기 표시값은 1, FAILED CHECKS는 0입니다.

nes_vblank_wait: 런타임
런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:26

선언 주석 원문
Wait for the current VBlank to end, then for the next one to begin. Each
PPUSTATUS read has hardware side effects, including clearing the VBlank flag.

kitaqfc/lib/runtime.c:196

c
void nes_vblank_wait(void)
{
    while ((PPUSTATUS & 0x80) != 0)
    {
    }
    while ((PPUSTATUS & 0x80) == 0)
    {
    }
}
nes_vram_queue_clear

C 런타임 큐의 모든 대기 레코드를 버리고 nes_vram_queue_used와 nes_vram_queue_overflow를 모두 0으로 초기화합니다. PPU에는 쓰지 않으며 버퍼에 남은 데이터 바이트도 지우지 않습니다. 컴파일러 내장 큐에는 영향을 주지 않습니다.

사용 형식

c
void nes_vram_queue_clear(void);

반환값

없음(void).

사용 조건과 제한

runtime.c가 CPU 주소 0x0300–0x03BF에 두는 별도의 192바이트 큐입니다. 실제 데이터 레코드는 3+len바이트, 채우기는 4바이트를 사용합니다. nes_vram_queue_used는 인코딩된 레코드 바이트 수이며, PPU 쓰기 횟수나 남은 VRAM 용량이 아닙니다. __vramq_* 내장 큐는 형식이 다르며 이 함수들로 비우거나 조회하거나 실행할 수 없습니다.

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

레코드 생성과 실행 단계를 분리하세요. 잠금이나 더블 버퍼링은 제공되지 않습니다. 확실하게 준비하려면 추가 중에는 NMI를 끄고, 렌더링을 끈 상태에서 실행하거나 VBlank 안에 처리할 수 있는 분량을 준비한 후 NMI를 켜세요. runtime.c는 커밋 호출 없이 __nes_nmi에서 비어 있지 않은 큐를 자동 실행합니다. 실행 시간 제한, 분할 처리, 스크롤 복원은 없습니다. 큐에 들어간다고 해서 VBlank 시간 안에 전송할 수 있는 것은 아닙니다.

사용 예

c
nes_vram_queue_clear(); // Discard all records AND clear the overflow latch.
if(nes_vram_queue_used!=0 || nes_vram_queue_overflow!=0)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 먼저 큐의 192바이트를 모두 채우고 추가 레코드를 의도적으로 거부시킵니다. 그 큐를 비운 뒤 길이 128이 거부되는지 확인하고, 145바이트를 사용하는 레코드 다섯 개로 PPU 쓰기 255회를 예약합니다. CAPACITY 192, ENCODED BYTES 145, AFTER FLUSH 000, REJECTED LENGTH 001, OVERFLOW SEEN 001, FAILED CHECKS 000이 표시되어야 합니다. 거부 관련 표시는 프로그램 도중 관찰한 결과이며 최종 플래그 값은 아닙니다.

파란 복사 타일은 (0,16), 초록 채우기 타일은 (0,64)에서 시작합니다. 각 영역은 세 줄 전체와 네 번째 줄의 31개, 총 127타일이며 마지막 8×8 칸은 검은색입니다. 파란 영역은 꽉 찬 타일, 반폭 타일, 삼각 타일을 반복하며 원본이 바뀌어도 첫 타일은 꽉 찬 상태를 유지합니다. y=112에서 x=16의 빨간 사각형은 길이 0 레코드 이후에도 남고, x=224의 빨간 반폭 타일은 NMI 자동 실행을 입증하며, 취소된 x=248은 검은색으로 남습니다. 검증은 18,432픽셀을 비교하고 화면 렌더링 중 VRAM 쓰기가 0회인지 확인합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_vram_queue_clear: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:40

선언 주석 원문
Discard pending VRAM commands and clear the overflow latch; no PPU writes occur.

kitaqfc/lib/runtime.c:31

c
void nes_vram_queue_clear(void)
{
    nes_vram_queue_used = 0;
    nes_vram_queue_overflow = 0;
}
nes_vram_queue_nmi_flush

커밋 플래그 없이 C 런타임 큐의 대기 레코드를 순서대로 모두 실행합니다. 각 레코드의 PPU 주소를 설정하고 저장된 데이터를 전송하거나 채우기 바이트를 반복해서 씁니다. 완료 시 nes_vram_queue_used는 0이 되며 오버플로 플래그는 유지됩니다. 빈 큐는 PPU에 접근하지 않습니다.

사용 형식

c
void nes_vram_queue_nmi_flush(void);

반환값

없음(void).

사용 조건과 제한

runtime.c가 CPU 주소 0x0300–0x03BF에 두는 별도의 192바이트 큐입니다. 실제 데이터 레코드는 3+len바이트, 채우기는 4바이트를 사용합니다. nes_vram_queue_used는 인코딩된 레코드 바이트 수이며, PPU 쓰기 횟수나 남은 VRAM 용량이 아닙니다. __vramq_* 내장 큐는 형식이 다르며 이 함수들로 비우거나 조회하거나 실행할 수 없습니다.

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

레코드 생성과 실행 단계를 분리하세요. 잠금이나 더블 버퍼링은 제공되지 않습니다. 확실하게 준비하려면 추가 중에는 NMI를 끄고, 렌더링을 끈 상태에서 실행하거나 VBlank 안에 처리할 수 있는 분량을 준비한 후 NMI를 켜세요. runtime.c는 커밋 호출 없이 __nes_nmi에서 비어 있지 않은 큐를 자동 실행합니다. 실행 시간 제한, 분할 처리, 스크롤 복원은 없습니다. 큐에 들어간다고 해서 VBlank 시간 안에 전송할 수 있는 것은 아닙니다.

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

사용 예

c
// Rendering and NMI are disabled; PPUCTRL selects increment 1.
nes_vram_queue_nmi_flush(); // No commit flag: consume all queued records now.
after_flush=nes_vram_queue_used;
if(after_flush!=0)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 먼저 큐의 192바이트를 모두 채우고 추가 레코드를 의도적으로 거부시킵니다. 그 큐를 비운 뒤 길이 128이 거부되는지 확인하고, 145바이트를 사용하는 레코드 다섯 개로 PPU 쓰기 255회를 예약합니다. CAPACITY 192, ENCODED BYTES 145, AFTER FLUSH 000, REJECTED LENGTH 001, OVERFLOW SEEN 001, FAILED CHECKS 000이 표시되어야 합니다. 거부 관련 표시는 프로그램 도중 관찰한 결과이며 최종 플래그 값은 아닙니다.

파란 복사 타일은 (0,16), 초록 채우기 타일은 (0,64)에서 시작합니다. 각 영역은 세 줄 전체와 네 번째 줄의 31개, 총 127타일이며 마지막 8×8 칸은 검은색입니다. 파란 영역은 꽉 찬 타일, 반폭 타일, 삼각 타일을 반복하며 원본이 바뀌어도 첫 타일은 꽉 찬 상태를 유지합니다. y=112에서 x=16의 빨간 사각형은 길이 0 레코드 이후에도 남고, x=224의 빨간 반폭 타일은 NMI 자동 실행을 입증하며, 취소된 x=248은 검은색으로 남습니다. 검증은 18,432픽셀을 비교하고 화면 렌더링 중 VRAM 쓰기가 0회인지 확인합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_vram_queue_nmi_flush: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:51

선언 주석 원문
Consume complete records in order during a safe PPU access period. Reading
PPUSTATUS resets the shared address latch before each address pair. The caller
is responsible for ensuring the transfer fits its NMI/VBlank time budget.

kitaqfc/lib/runtime.c:127

c
void nes_vram_queue_nmi_flush(void)
{
    unsigned char i;
    unsigned char cmd;
    unsigned char len;
    unsigned char data_index;
    unsigned char latch;

    i = 0;
    while (i != nes_vram_queue_used)
    {
        latch = PPUSTATUS;
        PPUADDR = nes_vram_queue_data[i];
        PPUADDR = nes_vram_queue_data[(unsigned char)(i + 1)];
        cmd = nes_vram_queue_data[(unsigned char)(i + 2)];

        if ((cmd & 0x80) != 0)
        {
            len = (unsigned char)(cmd & 0x7F);
            data_index = nes_vram_queue_data[(unsigned char)(i + 3)];
            while (len != 0)
            {
                PPUDATA = data_index;
                len = len - 1;
            }
            i = (unsigned char)(i + 4);
        }
        else
        {
            len = cmd;
            data_index = (unsigned char)(i + 3);
            while (len != 0)
            {
                PPUDATA = nes_vram_queue_data[data_index];
                data_index = data_index + 1;
                len = len - 1;
            }
            i = data_index;
        }

        latch = latch;
    }

    nes_vram_queue_used = 0;
}
nes_vram_queue_try_fill

C 런타임 큐에 4바이트 채우기 레코드를 추가합니다. 실행 시 value를 PPUDATA에 len번 씁니다. 값은 즉시 레코드에 저장되므로 호출 측 변수를 나중에 바꿔도 채우기 결과는 달라지지 않습니다. 반복하는 것은 한 바이트이며 타일 패턴이 아닙니다.

사용 형식

c
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len);

매개변수

ppu_addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

len

PPU에 쓸 바이트 수로, 범위는 0–127입니다. 길이 바이트의 비트 7을 채우기 구분에 사용하므로 128–255는 거부됩니다. 길이가 0이어도 write는 큐의 3바이트, fill은 4바이트를 사용합니다. 두 형식 모두 실행 시 PPU 주소는 설정하지만 PPUDATA에는 쓰지 않습니다.

반환값

레코드 전체가 추가되면 1, 길이가 잘못되었거나 공간이 부족하면 0을 반환합니다. 거부 시 nes_vram_queue_overflow를 1로 설정하며 기존 레코드와 사용 길이는 변경하지 않습니다. 오류 플래그는 해제할 때까지 남지만 이후 공간에 맞는 추가를 막지는 않습니다. 매 호출의 반환값을 확인하세요.

사용 조건과 제한

runtime.c가 CPU 주소 0x0300–0x03BF에 두는 별도의 192바이트 큐입니다. 실제 데이터 레코드는 3+len바이트, 채우기는 4바이트를 사용합니다. nes_vram_queue_used는 인코딩된 레코드 바이트 수이며, PPU 쓰기 횟수나 남은 VRAM 용량이 아닙니다. __vramq_* 내장 큐는 형식이 다르며 이 함수들로 비우거나 조회하거나 실행할 수 없습니다.

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

레코드 생성과 실행 단계를 분리하세요. 잠금이나 더블 버퍼링은 제공되지 않습니다. 확실하게 준비하려면 추가 중에는 NMI를 끄고, 렌더링을 끈 상태에서 실행하거나 VBlank 안에 처리할 수 있는 분량을 준비한 후 NMI를 켜세요. runtime.c는 커밋 호출 없이 __nes_nmi에서 비어 있지 않은 큐를 자동 실행합니다. 실행 시간 제한, 분할 처리, 스크롤 복원은 없습니다. 큐에 들어간다고 해서 VBlank 시간 안에 전송할 수 있는 것은 아닙니다.

사용 예

c
if(nes_vram_queue_try_fill(0x2100,fill_value,127)!=1)failures++;
fill_value=3; // The queued fill remains the solid tile 1.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 먼저 큐의 192바이트를 모두 채우고 추가 레코드를 의도적으로 거부시킵니다. 그 큐를 비운 뒤 길이 128이 거부되는지 확인하고, 145바이트를 사용하는 레코드 다섯 개로 PPU 쓰기 255회를 예약합니다. CAPACITY 192, ENCODED BYTES 145, AFTER FLUSH 000, REJECTED LENGTH 001, OVERFLOW SEEN 001, FAILED CHECKS 000이 표시되어야 합니다. 거부 관련 표시는 프로그램 도중 관찰한 결과이며 최종 플래그 값은 아닙니다.

파란 복사 타일은 (0,16), 초록 채우기 타일은 (0,64)에서 시작합니다. 각 영역은 세 줄 전체와 네 번째 줄의 31개, 총 127타일이며 마지막 8×8 칸은 검은색입니다. 파란 영역은 꽉 찬 타일, 반폭 타일, 삼각 타일을 반복하며 원본이 바뀌어도 첫 타일은 꽉 찬 상태를 유지합니다. y=112에서 x=16의 빨간 사각형은 길이 0 레코드 이후에도 남고, x=224의 빨간 반폭 타일은 NMI 자동 실행을 입증하며, 취소된 x=248은 검은색으로 남습니다. 검증은 18,432픽셀을 비교하고 화면 렌더링 중 VRAM 쓰기가 0회인지 확인합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_vram_queue_try_fill: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:47

선언 주석 원문
Queue a four-byte fill record: address high/low, length with bit 7 set, value.
Reject lengths above 127 and latch overflow on any capacity/format failure.

kitaqfc/lib/runtime.c:90

c
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len)
{
    unsigned char used;
    unsigned char need;

    used = nes_vram_queue_used;
    need = 4;

    if ((unsigned char)(used + need) < used)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }
    if ((unsigned char)(used + need) > 192)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }
    if ((len & 0x80) != 0)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }

    nes_vram_queue_data[used] = (unsigned char)(ppu_addr >> 8);
    nes_vram_queue_data[(unsigned char)(used + 1)] = (unsigned char)ppu_addr;
    nes_vram_queue_data[(unsigned char)(used + 2)] = (unsigned char)(len | 0x80);
    nes_vram_queue_data[(unsigned char)(used + 3)] = value;
    nes_vram_queue_used = (unsigned char)(used + need);
    return 1;
}
nes_vram_queue_try_write

C 런타임 큐에 실제 데이터가 들어 있는 레코드를 추가합니다. 호출 중에 원본의 len바이트를 큐에 복사한 뒤 사용 길이를 갱신합니다. PPU 전송은 큐를 비울 때 실행됩니다. 호출이 성공한 후 원본을 변경하거나 재사용해도 큐의 데이터는 바뀌지 않습니다.

사용 형식

c
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len);

매개변수

ppu_addr

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

src

현재 매핑된 메모리에서 최소 len바이트를 읽을 수 있는 영역의 포인터입니다. 원본은 이 호출이 끝날 때까지만 유효하면 됩니다. 데이터는 복사되며 원본에는 쓰지 않습니다. 길이가 0이면 원본 바이트를 읽지 않습니다.

len

PPU에 쓸 바이트 수로, 범위는 0–127입니다. 길이 바이트의 비트 7을 채우기 구분에 사용하므로 128–255는 거부됩니다. 길이가 0이어도 write는 큐의 3바이트, fill은 4바이트를 사용합니다. 두 형식 모두 실행 시 PPU 주소는 설정하지만 PPUDATA에는 쓰지 않습니다.

반환값

레코드 전체가 추가되면 1, 길이가 잘못되었거나 공간이 부족하면 0을 반환합니다. 거부 시 nes_vram_queue_overflow를 1로 설정하며 기존 레코드와 사용 길이는 변경하지 않습니다. 오류 플래그는 해제할 때까지 남지만 이후 공간에 맞는 추가를 막지는 않습니다. 매 호출의 반환값을 확인하세요.

사용 조건과 제한

runtime.c가 CPU 주소 0x0300–0x03BF에 두는 별도의 192바이트 큐입니다. 실제 데이터 레코드는 3+len바이트, 채우기는 4바이트를 사용합니다. nes_vram_queue_used는 인코딩된 레코드 바이트 수이며, PPU 쓰기 횟수나 남은 VRAM 용량이 아닙니다. __vramq_* 내장 큐는 형식이 다르며 이 함수들로 비우거나 조회하거나 실행할 수 없습니다.

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

레코드 생성과 실행 단계를 분리하세요. 잠금이나 더블 버퍼링은 제공되지 않습니다. 확실하게 준비하려면 추가 중에는 NMI를 끄고, 렌더링을 끈 상태에서 실행하거나 VBlank 안에 처리할 수 있는 분량을 준비한 후 NMI를 켜세요. runtime.c는 커밋 호출 없이 __nes_nmi에서 비어 있지 않은 큐를 자동 실행합니다. 실행 시간 제한, 분할 처리, 스크롤 복원은 없습니다. 큐에 들어간다고 해서 VBlank 시간 안에 전송할 수 있는 것은 아닙니다.

사용 예

c
if(nes_vram_queue_try_write(0x2040,source_bytes,127)!=1)failures++;
source_bytes[0]=3; // The queued first tile stays 1: payload was copied immediately.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 먼저 큐의 192바이트를 모두 채우고 추가 레코드를 의도적으로 거부시킵니다. 그 큐를 비운 뒤 길이 128이 거부되는지 확인하고, 145바이트를 사용하는 레코드 다섯 개로 PPU 쓰기 255회를 예약합니다. CAPACITY 192, ENCODED BYTES 145, AFTER FLUSH 000, REJECTED LENGTH 001, OVERFLOW SEEN 001, FAILED CHECKS 000이 표시되어야 합니다. 거부 관련 표시는 프로그램 도중 관찰한 결과이며 최종 플래그 값은 아닙니다.

파란 복사 타일은 (0,16), 초록 채우기 타일은 (0,64)에서 시작합니다. 각 영역은 세 줄 전체와 네 번째 줄의 31개, 총 127타일이며 마지막 8×8 칸은 검은색입니다. 파란 영역은 꽉 찬 타일, 반폭 타일, 삼각 타일을 반복하며 원본이 바뀌어도 첫 타일은 꽉 찬 상태를 유지합니다. y=112에서 x=16의 빨간 사각형은 길이 0 레코드 이후에도 남고, x=224의 빨간 반폭 타일은 NMI 자동 실행을 입증하며, 취소된 x=248은 검은색으로 남습니다. 검증은 18,432픽셀을 비교하고 화면 렌더링 중 VRAM 쓰기가 0회인지 확인합니다. 실제 하드웨어에서는 검증하지 않았습니다.

nes_vram_queue_try_write: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_queue.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_queue.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:44

선언 주석 원문
Copy a literal payload into the queue and publish its new used length last.
Accept lengths 0..127; reject larger lengths and latch overflow without changing the queue.
The caller must ensure queue production cannot race its NMI consumer.

kitaqfc/lib/runtime.c:40

c
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len)
{
    unsigned char used;
    unsigned char need;
    unsigned char i;
    unsigned char dst_index;

    // Bit 7 selects a fill record; accepting it here would misdecode a literal
    // and lengths 253..255 would also wrap the byte-sized allocation cost.
    if ((len & 0x80) != 0)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }

    used = nes_vram_queue_used;
    // Each literal record costs three header bytes plus len payload bytes.
    need = (unsigned char)(len + 3);

    if ((unsigned char)(used + need) < used)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }
    if ((unsigned char)(used + need) > 192)
    {
        nes_vram_queue_overflow = 1;
        return 0;
    }

    nes_vram_queue_data[used] = (unsigned char)(ppu_addr >> 8);
    nes_vram_queue_data[(unsigned char)(used + 1)] = (unsigned char)ppu_addr;
    nes_vram_queue_data[(unsigned char)(used + 2)] = len;

    i = 0;
    dst_index = (unsigned char)(used + 3);
    while (i != len)
    {
        nes_vram_queue_data[dst_index] = src[i];
        dst_index = dst_index + 1;
        i = i + 1;
    }

    nes_vram_queue_used = (unsigned char)(used + need);
    return 1;
}
nes_wait_nmi

진입 시 저장한 값과 nes_nmi_counter가 달라질 때까지 기다립니다. runtime.c의 __nes_nmi는 대기 중인 큐를 처리한 뒤 이 8비트 카운터를 증가시킵니다. 값의 불일치로 판단하므로 255에서 0으로 돌아가는 경우도 처리합니다. NMI를 활성화하고 이 핸들러 또는 동등한 카운터 갱신을 사용하세요. 시간 제한이 없으며 NMI 활성화, 스크롤 복원, 정확히 한 프레임의 경과 시간 보장은 하지 않습니다.

사용 형식

c
void nes_wait_nmi(void);

반환값

없음(void).

사용 조건과 제한

선언은 runtime.h를 사용하고 runtime.c는 정확히 한 번 포함하거나 컴파일하세요. 이 파일은 큐 외에도 __nes_nmi, 런타임 프레임 카운터, 직접 PPU 연산, 페이지 02의 OAM 저장 영역을 제공합니다. 프로그램의 NMI 처리와 조율해야 하며, 함수 선언만으로 구현이 연결되지는 않습니다.

사용 예

c
__scroll_set(0,0);start_counter=nes_nmi_counter;
__ppu_ctrl_set(0x80); // Enable runtime.c's NMI handler before waiting.
nes_wait_nmi(); // Returns after the runtime completion counter changes.
nmi_seen=(u8)(nes_nmi_counter!=start_counter);
if(nmi_seen!=1)failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

런타임 예제에서는 직접 전송과 두 가지 대기 방식을 배웁니다. (0,16)에는 파란색의 꽉 찬 타일·왼쪽 절반 막대·삼각형이 반복되고, (0,96)에는 녹색으로 채운 영역이 표시됩니다. 각 영역은 256개의 타일 바이트로 만든 256×64픽셀입니다. 원본을 바꿔도 첫 파란 타일은 꽉 찬 상태를 유지합니다. y=208의 x=16, 224, 240에는 주소 설정·길이 0 쓰기·길이 0 채우기를 나타내는 빨간 8×8 정사각형, 왼쪽 4×8 막대, 8행 삼각형이 표시됩니다. 두 대기 표시값은 1, FAILED CHECKS는 0입니다.

nes_wait_nmi: 런타임
런타임

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\runtime_ppu.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\runtime_ppu.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/runtime.h:23

선언 주석 원문
Wait until the NMI handler advances its counter. NMI must already be enabled
and able to execute; this polling loop has no timeout.

kitaqfc/lib/runtime.c:185

c
void nes_wait_nmi(void)
{
    unsigned char start;
    start = nes_nmi_counter;
    while (nes_nmi_counter == start)
    {
    }
}

scene.h — 장면 등록, 전환, 갱신

공유 전송 상태 하나를 통해 장면 그래픽을 VRAM 큐로 스트리밍합니다. step 완료와 큐 소비 측의 PPU 쓰기 완료를 구별하세요.

장면 테이블의 enter, update, draw, exit 처리 함수를 호출하여 타이틀 화면과 게임 화면 등의 장면을 전환합니다.

nes_scene_stream_begin

전송 대상, 원본, 길이, 청크 크기를 저장하고 공유 스트림을 활성 상태로 표시합니다.

사용 형식

c
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk);

매개변수

ppu_addr

CPU 주소가 아닌 전송 대상 PPU 주소입니다.

src

len바이트를 담은 원본입니다. 모든 바이트가 큐에 들어갈 때까지 읽기 가능한 상태를 유지하세요.

len

바이트 수로, 범위는 0~65535입니다. 전송 대상 영역의 경계도 호출자가 확인해야 합니다.

chunk

큐 레코드당 최대 데이터 크기입니다. 0은 32를 뜻하며, 127보다 크면 127로 제한합니다. 한 번의 step에서 전송하는 총량 제한은 아닙니다.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

스트림은 하나만 관리합니다. begin 호출은 이를 교체하지만 이미 큐에 복사된 데이터는 취소하지 않습니다. 원본 저장 공간과 ROM 매핑을 유효하게 유지하고, step 호출과 큐 소비는 호출자가 진행해야 합니다.

사용 예

c
nes_scene_stream_begin(0x2100,small,4,2);result[0]=nes_scene_stream_state.remaining;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

스트림을 큐 청크로 복사하고 큐를 소비하여 PPU에 전송하는 방법을 익힙니다. 타일 960바이트, 속성 64바이트, 팔레트 32바이트를 전송하여 (16,32)에 빨간 64×32 사각형을 표시합니다. begin만 호출해서는 화면이 갱신되지 않습니다.

nes_scene_stream_begin: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/scene.h:47

선언 주석 원문
Replace the single active transfer and retain its source pointer. Keep the
source storage and any required ROM bank readable until all bytes are queued.

kitaqfc/lib/scene.c:86

c
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk)
{
    nes_scene_stream_state.ppu_addr = ppu_addr;
    nes_scene_stream_state.src = src;
    nes_scene_stream_state.remaining = len;
    nes_scene_stream_state.chunk = chunk;
    nes_scene_stream_state.active = 1;
}
nes_scene_stream_begin_attr

16바이트 큐 레코드로 네임테이블 시작 주소+0x03C0에 보낼 속성 64바이트를 준비합니다.

사용 형식

c
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64);

매개변수

nt_base

네임테이블의 PPU 시작 주소이며, 예를 들면 0x2000입니다.

src64

속성 64바이트입니다. 전부 큐에 들어갈 때까지 유지하세요.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

스트림은 하나만 관리합니다. begin 호출은 이를 교체하지만 이미 큐에 복사된 데이터는 취소하지 않습니다. 원본 저장 공간과 ROM 매핑을 유효하게 유지하고, step 호출과 큐 소비는 호출자가 진행해야 합니다.

사용 예

c
nes_scene_stream_begin_attr(0x2000,attrs);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

스트림을 큐 청크로 복사하고 큐를 소비하여 PPU에 전송하는 방법을 익힙니다. 타일 960바이트, 속성 64바이트, 팔레트 32바이트를 전송하여 (16,32)에 빨간 64×32 사각형을 표시합니다. begin만 호출해서는 화면이 갱신되지 않습니다.

nes_scene_stream_begin_attr: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/scene.h:51

선언 주석 원문
Prepare the 64-byte attribute table at nametable base + 0x03C0.

kitaqfc/lib/scene.c:102

c
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64)
{
    nes_scene_stream_begin((unsigned short)(nt_base + 0x03C0), src64, 64, 16);
}
nes_scene_stream_begin_nametable

32바이트 큐 레코드로 960바이트 네임테이블 타일 전송을 준비합니다.

사용 형식

c
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960);

매개변수

nt_base

네임테이블의 PPU 시작 주소이며, 예를 들면 0x2000입니다.

src960

네임테이블의 타일 960바이트입니다. 속성 64바이트는 포함하지 않습니다.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

스트림은 하나만 관리합니다. begin 호출은 이를 교체하지만 이미 큐에 복사된 데이터는 취소하지 않습니다. 원본 저장 공간과 ROM 매핑을 유효하게 유지하고, step 호출과 큐 소비는 호출자가 진행해야 합니다.

사용 예

c
nes_scene_stream_begin_nametable(0x2000,tiles960);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();result[4]=nes_scene_stream_state.remaining;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

스트림을 큐 청크로 복사하고 큐를 소비하여 PPU에 전송하는 방법을 익힙니다. 타일 960바이트, 속성 64바이트, 팔레트 32바이트를 전송하여 (16,32)에 빨간 64×32 사각형을 표시합니다. begin만 호출해서는 화면이 갱신되지 않습니다.

nes_scene_stream_begin_nametable: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/scene.h:49

선언 주석 원문
Prepare the 960-byte tile portion of a nametable using 32-byte queue records.

kitaqfc/lib/scene.c:96

c
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960)
{
    nes_scene_stream_begin(nt_base, src960, 960, 32);
}
nes_scene_stream_begin_palette

16바이트 큐 레코드로 PPU 주소 0x3F00에 보낼 팔레트 32바이트를 준비합니다.

사용 형식

c
void nes_scene_stream_begin_palette(unsigned char* src32);

매개변수

src32

배경색 16개와 스프라이트색 16개로 구성된 팔레트 32바이트입니다. 색상 코드 제한과 PPU 미러링을 고려하세요.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

스트림은 하나만 관리합니다. begin 호출은 이를 교체하지만 이미 큐에 복사된 데이터는 취소하지 않습니다. 원본 저장 공간과 ROM 매핑을 유효하게 유지하고, step 호출과 큐 소비는 호출자가 진행해야 합니다.

사용 예

c
nes_scene_stream_begin_palette(colors);while(nes_scene_stream_step()!=0){nes_vram_queue_nmi_flush();}nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

스트림을 큐 청크로 복사하고 큐를 소비하여 PPU에 전송하는 방법을 익힙니다. 타일 960바이트, 속성 64바이트, 팔레트 32바이트를 전송하여 (16,32)에 빨간 64×32 사각형을 표시합니다. begin만 호출해서는 화면이 갱신되지 않습니다.

nes_scene_stream_begin_palette: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/scene.h:53

선언 주석 원문
Prepare a 32-byte palette transfer to PPU address 0x3F00.

kitaqfc/lib/scene.c:108

c
void nes_scene_stream_begin_palette(unsigned char* src32)
{
    nes_scene_stream_begin(0x3F00, src32, 32, 16);
}
nes_scene_stream_step

남은 데이터를 런타임 VRAM 큐가 받아들일 수 있는 만큼 여러 청크로 나누어 복사합니다.

사용 형식

c
unsigned char nes_scene_stream_step(void);

반환값

큐 용량 때문에 데이터가 남아 있으면 1, 전부 큐에 들어갔거나 스트림이 비활성 상태이면 0입니다. 청크 추가에 실패하면 원본과 대상 위치를 전진시키지 않아 재시도할 수 있습니다. 0을 반환해도 PPU 전송이 남아 있을 수 있습니다.

사용 조건과 제한

스트림은 하나만 관리합니다. begin 호출은 이를 교체하지만 이미 큐에 복사된 데이터는 취소하지 않습니다. 원본 저장 공간과 ROM 매핑을 유효하게 유지하고, step 호출과 큐 소비는 호출자가 진행해야 합니다.

사용 예

c
result[1]=nes_scene_stream_step();result[2]=nes_scene_stream_state.active;result[3]=nes_vram_queue_used;nes_vram_queue_nmi_flush();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

스트림을 큐 청크로 복사하고 큐를 소비하여 PPU에 전송하는 방법을 익힙니다. 타일 960바이트, 속성 64바이트, 팔레트 32바이트를 전송하여 (16,32)에 빨간 64×32 사각형을 표시합니다. begin만 호출해서는 화면이 갱신되지 않습니다.

nes_scene_stream_step: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch2_stream.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_stream.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr
구현과 소스

kitaqfc/lib/scene.h:57

선언 주석 원문
Queue as many chunks as available capacity allows, capped at 127 bytes each.
Return one when queue exhaustion requires a later retry, zero when all data has
been queued. Zero does not mean the NMI consumer has already written the PPU.

kitaqfc/lib/scene.c:116

c
unsigned char nes_scene_stream_step(void)
{
    unsigned char chunk;

    while (nes_scene_stream_state.active != 0)
    {
        if (nes_scene_stream_state.remaining == 0)
        {
            nes_scene_stream_state.active = 0;
            return 0;
        }

        // A step may enqueue several chunks; chunk limits record size, not the total bytes or time per frame.
        chunk = nes_scene_stream_state.chunk;
        if (chunk == 0)
        {
            chunk = 32;
        }
        if ((unsigned short)chunk > nes_scene_stream_state.remaining)
        {
            chunk = (unsigned char)nes_scene_stream_state.remaining;
        }
        if (chunk > 127)
        {
            chunk = 127;
        }

        // A failed enqueue leaves this chunk pending so a later step can retry after the queue drains.
        if (nes_vram_queue_try_write(nes_scene_stream_state.ppu_addr, nes_scene_stream_state.src, chunk) == 0)
        {
            return 1;
        }

        nes_scene_stream_state.ppu_addr = (unsigned short)(nes_scene_stream_state.ppu_addr + chunk);
        nes_scene_stream_state.src = nes_scene_stream_state.src + chunk;
        nes_scene_stream_state.remaining = (unsigned short)(nes_scene_stream_state.remaining - chunk);
    }

    return 0;
}
scene_change

현재 활성 장면이 있으면 종료하고, 지정한 장면으로 진입합니다.

사용 형식

c
void scene_change(u8 scene_id);

매개변수

scene_id

진입할 장면의 인덱스입니다. 범위는 0부터 count−1까지입니다.

반환값

반환값은 없습니다.

사용 조건과 제한

범위를 벗어난 ID는 무시합니다. 유효한 전환에서는 exit를 실행하고 새 ID와 변경 플래그를 저장한 다음 enter를 실행합니다. 같은 ID를 선택해도 exit와 enter를 실행합니다. 장면 테이블의 소유권은 호출자에게 있습니다. 사용하는 동안 테이블과 일반 함수 포인터가 가리키는 함수를 유효하게 유지하세요. null 콜백은 건너뜁니다. enter/exit 콜백 안에서 재귀적으로 장면을 전환하거나 테이블을 교체하지 마세요.

사용 예

c
scene_change(0);scene_change(9);result[11]=events;result[12]=scene_was_changed();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

장면 0을 활성화하고, 유효하지 않은 ID 9를 무시하며, 0→1 전환과 1→1 재진입을 수행하여 콜백 순서를 확인합니다.

scene_change: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:33

선언 주석 원문
Ignore invalid IDs; otherwise exit the current scene and enter the requested one.
Selecting the current ID still performs exit/enter. Callbacks run synchronously.

kitaqfc/lib/scene.c:28

c
void scene_change(u8 scene_id)
{
    SceneFunc fn;

    if (scene_id >= kq_scene_count) return;

    // Exit/enter handlers must not recursively change scenes or replace this shared table.
    if (kq_scene_has_current != 0) {
        fn = kq_scene_table[(__safe_index u8)kq_scene_current].exit;
        if (fn != 0) fn();
    }

    kq_scene_current = scene_id;
    kq_scene_has_current = 1;
    kq_scene_changed = 1;

    fn = kq_scene_table[(__safe_index u8)kq_scene_current].enter;
    if (fn != 0) fn();
}
scene_draw

활성 장면의 draw 콜백을 한 번 호출합니다.

사용 형식

c
void scene_draw();

반환값

반환값은 없습니다.

사용 조건과 제한

활성 장면이 없거나 draw 콜백이 null이면 아무 동작도 하지 않습니다. 변경 플래그는 지우지 않습니다. 화면 데이터 전송과 프레임 타이밍은 콜백 또는 이를 호출하는 게임 루프에서 관리하세요. 장면 테이블의 소유권은 호출자에게 있습니다. 사용하는 동안 테이블과 일반 함수 포인터가 가리키는 함수를 유효하게 유지하세요. null 콜백은 건너뜁니다. enter/exit 콜백 안에서 재귀적으로 장면을 전환하거나 테이블을 교체하지 마세요.

사용 예

c
scene_draw();result[15]=events;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이벤트 열에는 장면 0의 draw에서 기록한 12와 장면 1의 draw에서 기록한 22가 포함됩니다.

scene_draw: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:38

선언 주석 원문
Call the active scene's draw handler if one exists; no current scene is a no-op.

kitaqfc/lib/scene.c:61

c
void scene_draw()
{
    SceneFunc fn;

    if (kq_scene_has_current == 0) return;
    fn = kq_scene_table[(__safe_index u8)kq_scene_current].draw;
    if (fn != 0) fn();
}
scene_get_current

저장된 현재 장면 ID를 읽습니다.

사용 형식

c
u8 scene_get_current();

반환값

저장된 8비트 ID를 반환합니다. 초기화 직후에도 이 값은 0입니다.

사용 조건과 제한

반환값이 0이라는 사실만으로는 장면 0이 활성 상태인지 알 수 없습니다. 초기화 후 첫 scene_change() 호출이 있었는지 별도로 관리하세요.

사용 예

c
result[16]=scene_get_current();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

장면 0을 활성화한 뒤에는 0, 장면 1로 전환한 뒤에는 1이 읽히는 것을 확인합니다.

scene_get_current: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:40

선언 주석 원문
Return the stored scene ID; zero is also returned before the first transition.

kitaqfc/lib/scene.c:71

c
u8 scene_get_current()
{
    return kq_scene_current;
}
scene_init

활성 장면 없이 장면 테이블을 등록합니다.

사용 형식

c
void scene_init(const SceneDef* scenes, u8 count);

매개변수

scenes

enter, update, draw, exit 콜백을 담은 SceneDef 항목의 배열입니다.

count

항목 수입니다. 0이면 유효한 장면 ID가 없습니다.

반환값

반환값은 없습니다.

사용 조건과 제한

저장된 ID와 변경 플래그를 0으로 초기화합니다. enter는 호출하지 않습니다. 장면을 활성화하려면 scene_change()를 사용하세요. 장면 테이블의 소유권은 호출자에게 있습니다. 사용하는 동안 테이블과 일반 함수 포인터가 가리키는 함수를 유효하게 유지하세요. null 콜백은 건너뜁니다. enter/exit 콜백 안에서 재귀적으로 장면을 전환하거나 테이블을 교체하지 마세요.

사용 예

c
scene_init(scenes,2);scene_update();scene_draw();result[9]=events;result[10]=scene_was_changed();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

두 장면을 등록하고, 장면을 활성화하기 전에 update와 draw를 호출해도 이벤트가 발생하지 않는지 확인하는 샘플입니다.

scene_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:28

선언 주석 원문
Attach the caller-owned scene table and clear current-scene state without invoking callbacks.

kitaqfc/lib/scene.c:11

c
void scene_init(const SceneDef* scenes, u8 count)
{
    kq_scene_table = scenes;
    kq_scene_count = count;
    kq_scene_current = 0;
    kq_scene_changed = 0;
    kq_scene_has_current = 0;
}
scene_set_table

장면 테이블을 교체하고 활성 장면이 없는 상태로 만듭니다.

사용 형식

c
void scene_set_table(const SceneDef* scenes, u8 count);

매개변수

scenes

enter, update, draw, exit 콜백을 담은 SceneDef 항목의 배열입니다.

count

항목 수입니다. 0이면 유효한 장면 ID가 없습니다.

반환값

반환값은 없습니다.

사용 조건과 제한

scene_init()과 같이 상태를 초기화하며, 이전 exit 콜백이나 새 enter 콜백을 호출하지 않습니다. 장면 테이블의 소유권은 호출자에게 있습니다. 사용하는 동안 테이블과 일반 함수 포인터가 가리키는 함수를 유효하게 유지하세요. null 콜백은 건너뜁니다. enter/exit 콜백 안에서 재귀적으로 장면을 전환하거나 테이블을 교체하지 마세요.

사용 예

c
scene_set_table(scenes,2);result[23]=events;result[24]=scene_was_changed();scene_draw();result[25]=events;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

이벤트가 9개 발생한 뒤 테이블을 교체해도 이벤트 수는 9로 유지됩니다. 변경 플래그는 0이 되고, 이후 draw를 호출해도 아무 동작도 하지 않습니다.

scene_set_table: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:30

선언 주석 원문
Replace the table by resetting scene state; this does not call the old scene's exit handler.

kitaqfc/lib/scene.c:21

c
void scene_set_table(const SceneDef* scenes, u8 count)
{
    scene_init(scenes, count);
}
scene_update

활성 장면의 update 콜백을 한 번 호출합니다.

사용 형식

c
void scene_update();

반환값

반환값은 없습니다.

사용 조건과 제한

활성 장면이 없으면 아무 동작도 하지 않습니다. 활성 장면이 있으면 콜백 호출 전에 변경 플래그를 지웁니다. 따라서 update 안에서 발생한 전환은 호출이 끝난 뒤에도 플래그로 확인할 수 있습니다. 장면 테이블의 소유권은 호출자에게 있습니다. 사용하는 동안 테이블과 일반 함수 포인터가 가리키는 함수를 유효하게 유지하세요. null 콜백은 건너뜁니다. enter/exit 콜백 안에서 재귀적으로 장면을 전환하거나 테이블을 교체하지 마세요.

사용 예

c
scene_update();result[13]=events;result[14]=scene_was_changed();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

장면 0의 update는 이벤트 11을 기록합니다. 일반적인 이 갱신을 마치면 변경 플래그는 1에서 0으로 바뀝니다.

scene_update: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:36

선언 주석 원문
Clear the change flag before calling the active scene's update handler, so a
transition made during that handler is visible afterward.

kitaqfc/lib/scene.c:50

c
void scene_update()
{
    SceneFunc fn;

    if (kq_scene_has_current == 0) return;
    kq_scene_changed = 0;
    fn = kq_scene_table[(__safe_index u8)kq_scene_current].update;
    if (fn != 0) fn();
}
scene_was_changed

장면 전환이 현재 기록되어 있는지 읽습니다.

사용 형식

c
u8 scene_was_changed();

반환값

전환 플래그가 설정되어 있으면 1, 아니면 0을 반환합니다.

사용 조건과 제한

읽기만으로 플래그가 지워지지는 않습니다. 초기화, 테이블 교체, 활성 장면의 update 시작 시 플래그를 지웁니다. 유효하지 않은 장면 ID는 플래그를 바꾸지 않습니다.

사용 예

c
result[17]=scene_was_changed();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

전환 직후에는 1, 일반적인 update 후에는 0, 테이블 교체 후에는 0인지 확인합니다.

scene_was_changed: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/scene.h:42

선언 주석 원문
Read the transition flag, which is cleared at the start of an active scene update.

kitaqfc/lib/scene.c:77

c
u8 scene_was_changed()
{
    return kq_scene_changed;
}

scroll.h — 배경·윈도·분할 스크롤

16비트 월드 좌표로 PPUCTRL의 네임테이블 선택과 PPUSCROLL을 구성합니다. Y도 256단위로 나뉘므로 240픽셀 화면 높이를 고려해야 합니다.

nes_camera_follow_center

목표의 월드 위치에서 화면 기준점을 빼고 결과를 카메라 좌표로 저장합니다.

사용 형식

c
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y);

매개변수

target_x

목표의 월드 X 좌표이며, 단위는 픽셀이고 범위는 0~65535입니다.

target_y

목표의 월드 Y 좌표이며, 단위는 픽셀이고 범위는 0~65535입니다.

center_x

목표가 표시될 화면 X 좌표이며, 범위는 0~255입니다.

center_y

목표가 표시될 화면 Y 좌표이며, 범위는 0~255입니다.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

좌표 쌍 하나를 공유합니다. 하드웨어를 갱신하려면 nes_scroll_apply를 호출하세요. 뺄셈 결과가 음수이면 u16에서 래핑되므로 월드 경계 제한은 게임에서 처리하세요.

사용 예

c
nes_camera_follow_center(300,260,128,120);result[4]=nes_scroll_camera_x;result[5]=nes_scroll_camera_y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

저장된 16비트 좌표가 PPUCTRL과 스크롤 값으로 변환되는 과정을 익힙니다. 기본 제어값은 144이고, (264,272)를 적용하면 147이 됩니다. 기준점 (128,120)으로 목표 (300,260)을 따라가면 카메라는 (172,140)이 됩니다. 별도 상태 검사로 실제 PPU 쓰기를 확인합니다.

nes_camera_follow_center: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr

사용 예

c
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NROM의 수직 미러링으로 네임테이블 1을 표시합니다. 월드 (288,32)의 빨간 32×16 사각형이 카메라 (264,16)일 때 화면 (24,16)에 나타나며, 비트 8의 테이블 선택과 하위 바이트 스크롤을 보여 줍니다.

nes_camera_follow_center: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical
구현과 소스

kitaqfc/lib/scroll.h:21

선언 주석 원문
Subtract the screen anchor from the world point into unsigned camera
coordinates. Underflow wraps; world-edge clamping belongs to the game.

kitaqfc/lib/scroll.c:56

c
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y)
{
    nes_scroll_camera_x = target_x - center_x;
    nes_scroll_camera_y = target_y - center_y;
}
nes_scroll_apply

저장한 좌표와 기본 PPUCTRL 값을 합치고 래치를 초기화한 뒤, PPUCTRL과 X/Y 스크롤을 기록합니다.

사용 형식

c
void nes_scroll_apply(void);

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

각 축의 비트 8이 네임테이블을 선택하고 하위 바이트가 스크롤 값을 제공합니다. Y=240~255는 일반적인 세로 스크롤 위치가 아닙니다. PPUCTRL 전체를 교체하므로 NMI와 다른 기본 비트를 의도에 맞게 초기화하세요. VBlank를 기다리지 않으며, PPUSTATUS 읽기는 VBlank 플래그와 공유 래치에도 영향을 줍니다.

사용 예

c
nes_scroll_apply();result[3]=nes_scroll_ctrl_base|((nes_scroll_camera_x>>8)&1)|((nes_scroll_camera_y>>7)&2);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

저장된 16비트 좌표가 PPUCTRL과 스크롤 값으로 변환되는 과정을 익힙니다. 기본 제어값은 144이고, (264,272)를 적용하면 147이 됩니다. 기준점 (128,120)으로 목표 (300,260)을 따라가면 카메라는 (172,140)이 됩니다. 별도 상태 검사로 실제 PPU 쓰기를 확인합니다.

nes_scroll_apply: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr

사용 예

c
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NROM의 수직 미러링으로 네임테이블 1을 표시합니다. 월드 (288,32)의 빨간 32×16 사각형이 카메라 (264,16)일 때 화면 (24,16)에 나타나며, 비트 8의 테이블 선택과 하위 바이트 스크롤을 보여 줍니다.

nes_scroll_apply: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical
구현과 소스

kitaqfc/lib/scroll.h:18

선언 주석 원문
Combine bit 8 of each camera coordinate with base PPUCTRL, reset the latch
by reading PPUSTATUS, and write low-byte X/Y scroll. This uses 256-unit
wrapping on both axes rather than normalizing Y to a 240-pixel nametable height.
Initialize base control to the intended PPUCTRL value, including NMI enable, before applying.
Only coordinate bits 0..8 are used; arrange safe PPU timing in the caller.

kitaqfc/lib/scroll.c:34

c
void nes_scroll_apply(void)
{
    unsigned char ctrl;
    unsigned char x_hi;
    unsigned char y_hi;
    unsigned char latch;

    x_hi = (unsigned char)((nes_scroll_camera_x >> 8) & 1);
    y_hi = (unsigned char)((nes_scroll_camera_y >> 8) & 1);
    ctrl = (unsigned char)(nes_scroll_ctrl_base | x_hi | (unsigned char)(y_hi << 1));

    // Replace the whole control register, so the saved base also determines NMI and increment mode.
    PPUCTRL = ctrl;

    latch = PPUSTATUS;
    PPUSCROLL = (unsigned char)nes_scroll_camera_x;
    PPUSCROLL = (unsigned char)nes_scroll_camera_y;
    latch = latch;
}
nes_scroll_set

PPU에 쓰지 않고 16비트 카메라 좌표를 저장합니다.

사용 형식

c
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y);

매개변수

camera_x

정수 픽셀 단위의 가로 좌표입니다. 반영 시에는 하위 9비트를 사용합니다.

camera_y

세로 좌표입니다. 반영 시 네임테이블 높이 240픽셀에 맞춰 정규화하지 않고 하위 9비트를 사용합니다.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

좌표 쌍 하나를 공유합니다. 하드웨어를 갱신하려면 nes_scroll_apply를 호출하세요. 뺄셈 결과가 음수이면 u16에서 래핑되므로 월드 경계 제한은 게임에서 처리하세요.

사용 예

c
nes_scroll_set(264,272);result[1]=nes_scroll_camera_x;result[2]=nes_scroll_camera_y;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

저장된 16비트 좌표가 PPUCTRL과 스크롤 값으로 변환되는 과정을 익힙니다. 기본 제어값은 144이고, (264,272)를 적용하면 147이 됩니다. 기준점 (128,120)으로 목표 (300,260)을 따라가면 카메라는 (172,140)이 됩니다. 별도 상태 검사로 실제 PPU 쓰기를 확인합니다.

nes_scroll_set: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr

사용 예

c
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NROM의 수직 미러링으로 네임테이블 1을 표시합니다. 월드 (288,32)의 빨간 32×16 사각형이 카메라 (264,16)일 때 화면 (24,16)에 나타나며, 비트 8의 테이블 선택과 하위 바이트 스크롤을 보여 줍니다.

nes_scroll_set: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical
구현과 소스

kitaqfc/lib/scroll.h:12

선언 주석 원문
Store camera coordinates without touching hardware registers.

kitaqfc/lib/scroll.c:25

c
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y)
{
    nes_scroll_camera_x = camera_x;
    nes_scroll_camera_y = camera_y;
}
nes_scroll_set_base_ctrl

네임테이블 선택 비트 두 개를 지운 기본 PPUCTRL 값을 저장합니다.

사용 형식

c
void nes_scroll_set_base_ctrl(unsigned char ctrl);

매개변수

ctrl

NMI, 패턴 테이블, 증가량 설정을 포함한 PPUCTRL 값입니다. 하위 2비트는 좌표에서 결정합니다.

반환값

반환값은 없습니다. 변경되는 상태와 적용 시점은 이 항목에서 설명합니다.

사용 조건과 제한

좌표 쌍 하나를 공유합니다. 하드웨어를 갱신하려면 nes_scroll_apply를 호출하세요. 뺄셈 결과가 음수이면 u16에서 래핑되므로 월드 경계 제한은 게임에서 처리하세요.

사용 예

c
nes_scroll_set_base_ctrl(0x93);result[0]=nes_scroll_ctrl_base;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

저장된 16비트 좌표가 PPUCTRL과 스크롤 값으로 변환되는 과정을 익힙니다. 기본 제어값은 144이고, (264,272)를 적용하면 147이 됩니다. 기준점 (128,120)으로 목표 (300,260)을 따라가면 카메라는 (172,140)이 됩니다. 별도 상태 검사로 실제 PPU 쓰기를 확인합니다.

nes_scroll_set_base_ctrl: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr

사용 예

c
nes_scroll_set_base_ctrl(0);
nes_scroll_set(264,16);
nes_scroll_apply();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NROM의 수직 미러링으로 네임테이블 1을 표시합니다. 월드 (288,32)의 빨간 32×16 사각형이 카메라 (264,16)일 때 화면 (24,16)에 나타나며, 비트 8의 테이블 선택과 하위 바이트 스크롤을 보여 줍니다.

nes_scroll_set_base_ctrl: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_scroll_view.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_view.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --mirroring=vertical
구현과 소스

kitaqfc/lib/scroll.h:10

선언 주석 원문
Store PPUCTRL bits other than the two nametable-selection bits for later application.

kitaqfc/lib/scroll.c:19

c
void nes_scroll_set_base_ctrl(unsigned char ctrl)
{
    nes_scroll_ctrl_base = (unsigned char)(ctrl & 0xFC);
}

sprite.h — OBJ 할당·배치, 메타스프라이트, 애니메이션

스프라이트는 보통 8×8 또는 8×16픽셀 크기의 이동 가능한 하드웨어 오브젝트로, OAM(Object Attribute Memory) 항목으로 지정합니다. 슬롯을 확보하고 섀도 OAM에 위치, 타일, 속성을 설정한 뒤 전송합니다. 여러 슬롯을 하나의 오브젝트로 묶은 것이 메타스프라이트입니다. GB는 40슬롯과 주사선당 10개, FC는 64슬롯과 주사선당 8개를 제공합니다.

FC 속성은 비트 0~1이 네 스프라이트 팔레트 선택, 5가 색 번호 0이 아닌 배경 뒤로 배치, 6/7이 X/Y 반전입니다. 사용하지 않는 2~4는 0으로 두세요. GB와 값이 다르므로 각 기종의 상수를 사용하세요.

anim_update

SpriteAnim 타이머를 진행하고 선택된 타일 번호를 반환합니다. ticks_per_frame번 호출할 때마다 다음 연속 타일로 이동하며 frame_count 프레임 뒤 처음으로 돌아갑니다. 호출한 쪽에서 반환값을 sprite_set_tile에 적용합니다.

사용 형식

c
u8 anim_update(SpriteAnim* anim);

매개변수

anim

first_tile, frame_count, frame, ticks, ticks_per_frame이 있는 쓰기 가능한 SpriteAnim 상태입니다. 해당 개수가 0이 아니면 frame은 frame_count 미만, ticks는 ticks_per_frame 미만으로 유지하세요.

반환값

선택된 8비트 타일 번호이며 상태 포인터가 널이면 0입니다. OAM에는 기록하지 않습니다.

사용 조건과 제한

frame_count가 0이면 진행 없이 first_tile을 반환하고 ticks_per_frame이 0이면 현재 프레임을 유지합니다. 타이머는 영상 프레임이 아니라 호출 횟수로 진행합니다. 타일 번호는 256에서 순환하며 8×16 모드에서도 한 칸씩 증가합니다.

사용 예

c
sprite_example_animation.first_tile=128;sprite_example_animation.frame_count=2;
sprite_example_animation.frame=0;sprite_example_animation.ticks=0;
sprite_example_animation.ticks_per_frame=2;
sprite_example_expect(anim_update(&sprite_example_animation)==128);
tile=anim_update(&sprite_example_animation);
sprite_example_expect(tile==129);sprite_set_tile(5,tile);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

시작 타일 128, 두 프레임, 프레임당 두 호출로 설정하면 첫 갱신은 화살표 타일 128, 두 번째는 정사각형 타일 129를 반환합니다. ANIMATION 옆의 정사각형으로 반환값이 OAM에 적용되었음을 확인합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

anim_update: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:74

선언 주석 원문
Advance animation ticks and wrap the frame index, returning the selected tile.
Zero ticks-per-frame freezes advancement; null state returns zero.

kitaqfc/lib/sprite.c:174

c
u8 anim_update(SpriteAnim* anim)
{
    if (anim == 0) return 0;
    if (anim->frame_count == 0) return anim->first_tile;
    if (anim->ticks_per_frame == 0) return (u8)(anim->first_tile + anim->frame);

    anim->ticks = (u8)(anim->ticks + 1);
    if (anim->ticks >= anim->ticks_per_frame) {
        anim->ticks = 0;
        anim->frame = (u8)(anim->frame + 1);
        if (anim->frame >= anim->frame_count) anim->frame = 0;
    }
    return (u8)(anim->first_tile + anim->frame);
}
metasprite_draw

개수를 지정한 스프라이트 조각 배열을 연속 슬롯에 배치합니다. 각 조각에는 부호 있는 오프셋, 타일, 속성이 들어갑니다. 빈 슬롯을 활성화하고 사용 중인 슬롯은 덮어쓰며 슬롯 한도에서 멈춥니다.

사용 형식

c
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count);

매개변수

first_id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

x

픽셀 단위의 가로 위치를 부호 없는 8비트 값으로 전달합니다. 타일 좌표가 아닙니다.

y

OAM에 저장할 Y 값입니다. 화면 1번 줄 이후에 윗부분을 놓으려면 표시할 윗좌표에서 1을 뺍니다. 239~255는 240줄 표시 영역 밖입니다.

parts

count개 이상의 MetaSpritePart를 읽을 수 있는 배열입니다. 각 4바이트 레코드는 부호 있는 dx/dy, 타일, 속성 순서이며 종료 표시는 없습니다.

count

조각 수를 부호 없는 8비트 값으로 지정합니다. 0이면 쓰지 않습니다. 연속 슬롯이 SPRITE_MAX에 도달하면 그 전까지의 조각만 기록합니다.

반환값

실제로 기록한 조각 수를 0~count로 반환합니다. 슬롯 한도에서 잘렸는지는 이 값으로 확인합니다.

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

부호 있는 8비트 오프셋을 기준 좌표에 더한 뒤 결과를 8비트로 순환시킵니다. 호출 동안 조각 배열 전체를 읽을 수 있어야 합니다. 활성 슬롯도 덮어쓰므로 다른 오브젝트의 슬롯을 보호하지 않습니다.

사용 예

c
sprite_example_expect(metasprite_draw(10,88,SPRITE_EXAMPLE_Y(64),sprite_example_pair,2)==2);
sprite_example_expect(sprite_count_used()==9); // Sparse IDs count actual active slots.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

metasprite_draw: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:71

선언 주석 원문
Place consecutive parts, counting each newly activated slot once. Existing
active slots are overwritten without increasing the allocation count. Return
the number written; an invalid first slot or zero count writes nothing.

kitaqfc/lib/sprite.c:152

c
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count)
{
    u8 i;
    i = 0;
    while (i < count) {
        u8 id;
        id = (u8)(first_id + i);
        if (id >= SPRITE_MAX) return i;
        if (kq_sprite_active[id] == 0) {
            kq_sprite_active[id] = 1;
            kq_sprite_used = (u8)(kq_sprite_used + 1);
        }
        __sprite_set(id, (u8)(x + parts[i].dx), (u8)(y + parts[i].dy), parts[i].tile, parts[i].flags);
        kq_sprite_x[id] = (u8)(x + parts[i].dx);
        kq_sprite_y[id] = (u8)(y + parts[i].dy);
        i = (u8)(i + 1);
    }
    return count;
}
sprite_alloc

첫 번째 빈 스프라이트 슬롯을 확보하고 위치를 지정할 때까지 숨깁니다. 숨겨진 동안에도 할당은 유지됩니다. 표시하기 전에 타일과 속성을 설정하세요.

사용 형식

c
u8 sprite_alloc(void);

반환값

할당된 슬롯 ID를 반환하며, 모든 슬롯이 사용 중이면 0xFF입니다.

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

사용 예

c
for(i=0;i<8;i++) sprite_example_expect(sprite_alloc()==i);
sprite_example_expect(sprite_count_used()==8);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_alloc: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:45

선언 주석 원문
Claim and hide the first free slot, or return 0xFF if all slots are active.

kitaqfc/lib/sprite.c:35

c
u8 sprite_alloc(void)
{
    u8 i;
    i = 0;
    while (i < SPRITE_MAX) {
        if (kq_sprite_active[i] == 0) {
            kq_sprite_active[i] = 1;
            kq_sprite_used = (u8)(kq_sprite_used + 1);
            sprite_hide(i);
            return i;
        }
        i = (u8)(i + 1);
    }
    return 0xFF;
}
sprite_count_used

숨겨진 스프라이트와 metasprite_draw로 활성화한 슬롯을 포함해 할당 수를 반환합니다. 화면에 보이는 수가 아니라 할당된 슬롯 수입니다.

사용 형식

c
u8 sprite_count_used(void);

반환값

0부터 SPRITE_MAX까지의 부호 없는 8비트 값입니다.

사용 예

c
sprite_example_expect(sprite_count_used()==9); // After hiding, freeing and drawing the pair.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_count_used: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:62

선언 주석 원문
Return the number of active slots, including slots activated by metasprite_draw.
Hidden allocated slots remain active until sprite_free releases them.

kitaqfc/lib/sprite.c:108

c
u8 sprite_count_used(void)
{
    return kq_sprite_used;
}
sprite_flush_oam

NMI 프레임 카운터가 갱신될 때까지 기다린 뒤 256바이트 섀도 OAM 페이지를 전송합니다. NMI가 활성화되어 있고 핸들러가 해당 카운터를 갱신해야 합니다.

사용 형식

c
void sprite_flush_oam(void);

반환값

없음(void).

사용 조건과 제한

FC 루틴은 OAMADDR을 0으로 설정하고 CPU 페이지 $02($0200~$02FF)에서 DMA를 시작합니다. VBlank 또는 렌더링 중지 상태에서 사용하고 인터럽트가 같은 섀도 페이지를 사용하는 동안 동시에 수정하지 마세요.

사용 예

c
while(1) { sprite_flush_oam(); } // Transfer prepared sprites at the next frame boundary.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_flush_oam: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:59

선언 주석 원문
Wait for NMI before requesting OAM DMA.

kitaqfc/lib/sprite.c:100

c
void sprite_flush_oam(void)
{
    __nmi_wait();
    __oam_dma();
}
sprite_flush_oam_now

섀도 OAM을 즉시 하드웨어로 전송합니다. 안전한 표시 구간을 기다리지 않으므로 호출하는 쪽에서 실행 시점을 정해야 합니다.

사용 형식

c
void sprite_flush_oam_now(void);

반환값

없음(void).

사용 조건과 제한

FC 루틴은 OAMADDR을 0으로 설정하고 CPU 페이지 $02($0200~$02FF)에서 DMA를 시작합니다. VBlank 또는 렌더링 중지 상태에서 사용하고 인터럽트가 같은 섀도 페이지를 사용하는 동안 동시에 수정하지 마세요.

사용 예

c
// Rendering is disabled here; all shadow entries have been prepared.
sprite_flush_oam_now();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_flush_oam_now: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:57

선언 주석 원문
Transfer intrinsic shadow OAM immediately; this wrapper does not wait for VBlank.

kitaqfc/lib/sprite.c:94

c
void sprite_flush_oam_now(void)
{
    __oam_dma();
}
sprite_free

할당된 슬롯을 해제하고 숨긴 뒤 사용 수를 하나 줄입니다. 범위를 벗어난 ID나 이미 해제된 슬롯에는 아무 작업도 하지 않습니다.

사용 형식

c
void sprite_free(u8 id);

매개변수

id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

반환값

없음(void).

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

사용 예

c
sprite_free(7);sprite_free(7);
sprite_example_expect(sprite_count_used()==7); // Repeated release is ignored.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_free: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:47

선언 주석 원문
Release an active valid slot and hide it; invalid/repeated frees are ignored.

kitaqfc/lib/sprite.c:52

c
void sprite_free(u8 id)
{
    if (sprite_valid(id) == 0) return;
    if (kq_sprite_active[id] == 0) return;
    kq_sprite_active[id] = 0;
    if (kq_sprite_used != 0) kq_sprite_used = (u8)(kq_sprite_used - 1);
    sprite_hide(id);
}
sprite_hide

섀도 위치를 바꿔 스프라이트를 숨깁니다. 할당, 타일, 속성은 유지되므로 보이는 위치로 옮기면 다시 할당하지 않고 표시할 수 있습니다.

사용 형식

c
void sprite_hide(u8 id);

매개변수

id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

반환값

없음(void).

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

사용 예

c
sprite_hide(6);
sprite_example_expect(sprite_count_used()==8); // Hiding retains allocation.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_hide: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:55

선언 주석 원문
Mark the software position off screen and hide its OAM entry without freeing it.

kitaqfc/lib/sprite.c:85

c
void sprite_hide(u8 id)
{
    if (sprite_valid(id) == 0) return;
    kq_sprite_x[id] = 0;
    kq_sprite_y[id] = 0xF0;
    __sprite_hide(id);
}
sprite_init

스프라이트 할당 상태를 초기화하고 섀도 OAM의 모든 슬롯을 숨깁니다. 소프트웨어상의 높이는 8픽셀이 됩니다. 하드웨어 크기 설정과 OAM 전송은 별도로 수행합니다.

사용 형식

c
void sprite_init(void);

반환값

없음(void).

사용 조건과 제한

FC 초기화는 할당과 관리용 좌표를 초기화하지만 OAM을 숨길 때 타일과 속성은 유지합니다. SPRITE_MAX는 1~64로 설정하세요.

사용 예

c
sprite_init();
sprite_example_expect(sprite_count_used()==0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

초기화 직후 할당 수는 0입니다. 여덟 번 할당하면 ID 0~7을 반환합니다. 슬롯 6을 숨겨도 8이고, 슬롯 7을 두 번 해제하면 7이며, 10과 11에 두 조각을 놓으면 9가 됩니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_init: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:43

선언 주석 원문
Reset allocation/position state and hide each OAM slot through the intrinsic.
The software height defaults to eight; hardware sprite size is configured elsewhere.

kitaqfc/lib/sprite.c:19

c
void sprite_init(void)
{
    u8 i;
    kq_sprite_used = 0;
    kq_sprite_height = 8;
    i = 0;
    while (i < SPRITE_MAX) {
        kq_sprite_active[i] = 0;
        kq_sprite_x[i] = 0;
        kq_sprite_y[i] = 0;
        __sprite_hide(i);
        i = (u8)(i + 1);
    }
}
sprite_max_scanline_count

관리 중인 세로 위치와 kq_sprite_height를 바탕으로 표시 영역의 한 주사선에 겹치는 활성 스프라이트의 최대 수를 추정합니다.

사용 형식

c
u8 sprite_max_scanline_count(void);

반환값

0부터 SPRITE_MAX까지의 부호 없는 8비트 값입니다.

사용 조건과 제한

kq_sprite_height를 하드웨어 높이인 8 또는 16으로 맞추세요. 이 추정은 투명 픽셀, 가로 가시성, 하드웨어 우선순위를 고려하지 않습니다. FC는 라이브러리의 좌표를 읽으므로 OAM 내장 함수로 직접 바꾼 위치는 반영하지 않습니다.

사용 예

c
sprite_example_expect(sprite_max_scanline_count()==SPRITE_EXAMPLE_LINE_COUNT);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

sprite_max_scanline_count: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:67

선언 주석 원문
Estimate peak overlap across 240 lines from software Y/height values.
This does not model pixel transparency, OAM priority or every hardware overflow quirk.

kitaqfc/lib/sprite.c:117

c
u8 sprite_max_scanline_count(void)
{
    u8 line;
    u8 best;
    line = 0;
    best = 0;
    while (line < 240) {
        u8 i;
        u8 count;
        i = 0;
        count = 0;
        while (i < SPRITE_MAX) {
            if (kq_sprite_active[i] != 0) {
                if (line > kq_sprite_y[i] && (u8)(line - kq_sprite_y[i]) <= kq_sprite_height) {
                    count = (u8)(count + 1);
                }
            }
            i = (u8)(i + 1);
        }
        if (count > best) best = count;
        line = (u8)(line + 1);
    }
    return best;
}
sprite_set_flags

스프라이트 속성 바이트 전체를 바꿉니다. 해당 기종의 플래그 상수를 조합해 반전, 팔레트, 배경 우선순위를 지정합니다. 지정하지 않은 비트는 0이 됩니다.

사용 형식

c
void sprite_set_flags(u8 id, u8 flags);

매개변수

id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

flags

해당 기종의 SPRITE_FLAG_* 상수로 구성한 전체 속성 바이트입니다. 여러 플래그는 비트 OR(|)로 조합합니다.

반환값

없음(void).

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

FC 속성은 비트 0~1이 네 스프라이트 팔레트 선택, 5가 색 번호 0이 아닌 배경 뒤로 배치, 6/7이 X/Y 반전입니다. 사용하지 않는 2~4는 0으로 두세요. GB와 값이 다르므로 각 기종의 상수를 사용하세요.

사용 예

c
sprite_set_flags(1,SPRITE_FLAG_XFLIP);
sprite_set_flags(2,SPRITE_FLAG_YFLIP);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_set_flags: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:53

선언 주석 원문
Replace the hardware attribute byte of a valid OAM slot.

kitaqfc/lib/sprite.c:78

c
void sprite_set_flags(u8 id, u8 flags)
{
    if (sprite_valid(id) == 0) return;
    __sprite_attr(id, flags);
}
sprite_set_pos

FC 스프라이트의 관리용 좌표와 섀도 OAM 좌표를 갱신합니다. X는 화면 위치이고 Y는 OAM에 기록할 값입니다. 화면에서 윗부분을 32픽셀 위치에 표시하려면 Y=31을 전달합니다.

사용 형식

c
void sprite_set_pos(u8 id, u8 x, u8 y);

매개변수

id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

x

픽셀 단위의 가로 위치를 부호 없는 8비트 값으로 전달합니다. 타일 좌표가 아닙니다.

y

OAM에 저장할 Y 값입니다. 화면 1번 줄 이후에 윗부분을 놓으려면 표시할 윗좌표에서 1을 뺍니다. 239~255는 240줄 표시 영역 밖입니다.

반환값

없음(void).

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

좌표 덧셈은 8비트 연산이며 256을 기준으로 순환합니다. 화면 범위로 자르지 않습니다. 유효한 슬롯은 할당되지 않았어도 수정할 수 있습니다.

사용 예

c
sprite_set_pos(0,88,SPRITE_EXAMPLE_Y(16)); // Visible top-left: (88,16).

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_set_pos: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:49

선언 주석 원문
Update software coordinates and the intrinsic OAM position for a valid slot.

kitaqfc/lib/sprite.c:62

c
void sprite_set_pos(u8 id, u8 x, u8 y)
{
    if (sprite_valid(id) == 0) return;
    kq_sprite_x[id] = x;
    kq_sprite_y[id] = y;
    __sprite_move(id, x, y);
}
sprite_set_tile

섀도 OAM 한 슬롯의 타일 번호를 바꿉니다. 타일 메모리에 있는 그래픽을 선택할 뿐, 그래픽 전송이나 스프라이트 할당은 하지 않습니다.

사용 형식

c
void sprite_set_tile(u8 id, u8 tile);

매개변수

id

0부터 SPRITE_MAX-1까지의 슬롯 ID입니다. 기본값은 GB 0~39, FC 0~63입니다. 범위를 벗어나면 무시합니다.

tile

8비트 타일 번호입니다. 그래픽을 먼저 로드하세요. 8×16 스프라이트의 하드웨어 규칙은 그대로 적용되며 두 타일 묶음에 맞게 번호를 보정하지 않습니다.

반환값

없음(void).

사용 조건과 제한

위치, 타일, 속성, 숨기기는 섀도 데이터만 바꿉니다. 슬롯을 할당하거나 하드웨어 OAM을 즉시 바꾸지 않습니다. 한 프레임의 갱신을 마친 뒤 sprite_flush_oam으로 전송하세요.

사용 예

c
sprite_set_tile(5,129); // Select the green square beside ANIMATION.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

시작 타일 128, 두 프레임, 프레임당 두 호출로 설정하면 첫 갱신은 화살표 타일 128, 두 번째는 정사각형 타일 129를 반환합니다. ANIMATION 옆의 정사각형으로 반환값이 OAM에 적용되었음을 확인합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

sprite_set_tile: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:51

선언 주석 원문
Replace the tile byte of a valid OAM slot without changing allocation state.

kitaqfc/lib/sprite.c:71

c
void sprite_set_tile(u8 id, u8 tile)
{
    if (sprite_valid(id) == 0) return;
    __sprite_tile(id, tile);
}
sprite_warn_scanline_overflow

sprite_max_scanline_count를 GB는 10개, FC는 8개인 주사선당 한도와 비교합니다. 소프트웨어 추정 결과이며 하드웨어 오버플로 플래그를 읽지는 않습니다.

사용 형식

c
u8 sprite_warn_scanline_overflow(void);

반환값

추정값이 주사선당 한도를 넘으면 1, 아니면 0입니다.

사용 조건과 제한

kq_sprite_height를 하드웨어 높이인 8 또는 16으로 맞추세요. 이 추정은 투명 픽셀, 가로 가시성, 하드웨어 우선순위를 고려하지 않습니다. FC는 라이브러리의 좌표를 읽으므로 OAM 내장 함수로 직접 바꾼 위치는 반영하지 않습니다.

사용 예

c
sprite_example_expect(sprite_warn_scanline_overflow()==1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

SCAN LIMIT 아래 줄은 (8,128)에서 시작합니다. GB는 11개, FC는 9개를 같은 줄에 배치해 의도적으로 하나 초과합니다. GB에서는 화살표 10개, FC에서는 8개가 보이고 마지막 것은 보이지 않아야 합니다. 소프트웨어 최대 수와 경고 검사는 각각 11/9와 1이어야 합니다.

스프라이트 할당, 위치, 반전, 복합 오브젝트, 겹침, 애니메이션, 주사선 한도를 배우는 프로그램입니다. 오른쪽 위의 두 숫자는 모든 내부 검사가 통과했음을 뜻하는 00이어야 합니다. 화살표와 정사각형은 각각 8×8 타일입니다.

NORMAL은 (88,16)의 오른쪽 화살표, FLIP X는 (88,32)의 왼쪽 화살표, FLIP Y는 (88,48)의 상하 반전 화살표입니다. META는 (88,64)와 (96,64)에 두 화살표를 놓습니다. OVERLAP에서는 (88,80)의 화살표가 (92,80)의 정사각형 앞에 놓이며 투명한 부분으로 정사각형이 보입니다. ANIMATION은 (88,96)의 정사각형입니다. (120,16)과 (120,32)는 숨기기와 해제 후 비어 있어야 합니다.

CGB와 FC의 화살표는 녹색 몸통, 빨간 윤곽, 파란 끝부분을 사용하며 정사각형은 녹색입니다. 투명한 부분에는 GB의 흰 배경 또는 FC의 검은 배경이 보입니다. DMG는 빨강·초록·파랑 대신 밝은 회색·짙은 회색·검정을 사용합니다.

sprite_warn_scanline_overflow: NTSC
NTSC

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sprite_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\sprite_shapes.nes --nes-chr=.\kitaq-docs\samples\sprite_example.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/sprite.h:64

선언 주석 원문
Flag software overlap estimates above eight sprites on one scanline.

kitaqfc/lib/sprite.c:143

c
u8 sprite_warn_scanline_overflow(void)
{
    return (u8)(sprite_max_scanline_count() > 8);
}

system.h — 시작, 프레임 수, 대기, 인터럽트

프레임 대기 횟수, 대기 후 콜백, 마스킹 가능한 CPU 인터럽트를 관리합니다.

fc_frame_count

fc_frame_count는 system_get_frame의 별칭입니다. 래퍼의 대기가 완료된 횟수를 나타내는 16비트 소프트웨어 카운터를 읽습니다.

프레임 대기 래퍼의 16비트 카운터를 읽습니다.

사용 형식

c
u16 fc_frame_count(void);

반환값

현재 카운터 값인 0~65535를 반환합니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_get_frame

사용 예

c
result[3]=fc_frame_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 세 번 대기한 뒤 3을 읽습니다. 순환 검사를 위해 카운터를 명시적으로 65535로 설정하고, 다음 대기 후 0이 되는지 확인합니다.

fc_frame_count: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:30

c
#define fc_frame_count system_get_frame

kitaqfc/lib/system.c:30

c
u16 system_get_frame(void)
{
    return kq_system_frame;
}
fc_init

fc_init은 system_init의 별칭입니다. 소프트웨어 프레임 수와 콜백 등록을 초기화합니다.

카운터와 등록된 콜백을 지워 프레임 관리를 초기화합니다.

사용 형식

c
void fc_init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_init

사용 예

c
fc_init();result[0]=fc_frame_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 콜백을 등록하기 전에 초기 프레임 번호가 0인지 확인합니다.

fc_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:27

c
#define fc_init system_init

kitaqfc/lib/system.c:7

c
void system_init(void)
{
    kq_system_frame = 0;
    system_vblank_callback = 0;
    // NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
    __nmi_enable();
}
fc_set_vblank_callback

fc_set_vblank_callback은 system_set_vblank_callback의 별칭입니다. 프레임 대기 후 동기적으로 실행되는 콜백을 등록하거나 해제합니다.

프레임 대기 뒤에 호출할 함수를 등록하거나 교체하거나 해제합니다.

사용 형식

c
void fc_set_vblank_callback(SystemCallback callback);

매개변수

callback

호출할 함수입니다. 0을 지정하면 콜백을 해제합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

인수와 반환값이 없는 함수를 가리키는 일반 포인터입니다. 뱅크 번호를 포함하지 않으므로 호출될 수 있는 동안 대상 코드가 매핑되어 있어야 합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_set_vblank_callback

사용 예

c
fc_set_vblank_callback(on_frame);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

콜백을 세 번 호출한 뒤 0을 등록하면 호출이 해제됩니다. 다음 대기에서는 프레임 값이 4가 되지만 콜백 횟수는 3으로 유지됩니다.

fc_set_vblank_callback: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:29

c
#define fc_set_vblank_callback system_set_vblank_callback

kitaqfc/lib/system.c:24

c
void system_set_vblank_callback(SystemCallback callback)
{
    system_vblank_callback = callback;
}
fc_wait_vblank

fc_wait_vblank는 system_wait_vblank의 별칭입니다. 프레임 경계를 기다리고 소프트웨어 프레임 수를 증가시킨 뒤 등록된 콜백을 호출합니다.

다음 프레임을 기다리고 카운터를 증가시킨 뒤 등록된 콜백을 호출합니다.

사용 형식

c
void fc_wait_vblank(void);

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

콜백은 이 함수 안에서 동기적으로 실행됩니다. 콜백을 등록한다고 인터럽트 핸들러가 설치되는 것은 아닙니다. 카운터는 이 래퍼의 대기 횟수를 세며 실제 하드웨어 프레임 경과를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_wait_vblank

사용 예

c
fc_wait_vblank();fc_wait_vblank();fc_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

세 번 대기하면 콜백도 세 번 호출되며, 마지막 콜백 안에서 읽은 카운터 값은 3입니다.

fc_wait_vblank: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:28

c
#define fc_wait_vblank system_wait_vblank

kitaqfc/lib/system.c:16

c
void system_wait_vblank(void)
{
    __nmi_wait();
    kq_system_frame = (u16)(kq_system_frame + 1);
    if (system_vblank_callback != 0) system_vblank_callback();
}
gb_frame_count

gb_frame_count는 system_get_frame의 별칭입니다. 래퍼의 대기가 완료된 횟수를 나타내는 16비트 소프트웨어 카운터를 읽습니다.

프레임 대기 래퍼의 16비트 카운터를 읽습니다.

사용 형식

c
u16 gb_frame_count(void);

반환값

현재 카운터 값인 0~65535를 반환합니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

FC 라이브러리가 제공하는 gb_ 표기입니다. Game Boy 하드웨어가 아니라 FC의 system_* 구현을 호출합니다. 대응하는 fc_ 이름도 같은 FC 구현을 호출합니다.

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_get_frame

사용 예

c
result[3]=gb_frame_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 세 번 대기한 뒤 3을 읽습니다. 순환 검사를 위해 카운터를 명시적으로 65535로 설정하고, 다음 대기 후 0이 되는지 확인합니다.

gb_frame_count: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:34

c
#define gb_frame_count system_get_frame

kitaqfc/lib/system.c:30

c
u16 system_get_frame(void)
{
    return kq_system_frame;
}
gb_init

gb_init은 system_init의 별칭입니다. 소프트웨어 프레임 수와 콜백 등록을 초기화합니다.

카운터와 등록된 콜백을 지워 프레임 관리를 초기화합니다.

사용 형식

c
void gb_init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

FC 라이브러리가 제공하는 gb_ 표기입니다. Game Boy 하드웨어가 아니라 FC의 system_* 구현을 호출합니다. 대응하는 fc_ 이름도 같은 FC 구현을 호출합니다.

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_init

사용 예

c
gb_init();result[0]=gb_frame_count();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 콜백을 등록하기 전에 초기 프레임 번호가 0인지 확인합니다.

gb_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:31

c
#define gb_init system_init

kitaqfc/lib/system.c:7

c
void system_init(void)
{
    kq_system_frame = 0;
    system_vblank_callback = 0;
    // NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
    __nmi_enable();
}
gb_set_vblank_callback

gb_set_vblank_callback은 system_set_vblank_callback의 별칭입니다. 프레임 대기 후 동기적으로 실행되는 콜백을 등록하거나 해제합니다.

프레임 대기 뒤에 호출할 함수를 등록하거나 교체하거나 해제합니다.

사용 형식

c
void gb_set_vblank_callback(SystemCallback callback);

매개변수

callback

호출할 함수입니다. 0을 지정하면 콜백을 해제합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

FC 라이브러리가 제공하는 gb_ 표기입니다. Game Boy 하드웨어가 아니라 FC의 system_* 구현을 호출합니다. 대응하는 fc_ 이름도 같은 FC 구현을 호출합니다.

인수와 반환값이 없는 함수를 가리키는 일반 포인터입니다. 뱅크 번호를 포함하지 않으므로 호출될 수 있는 동안 대상 코드가 매핑되어 있어야 합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_set_vblank_callback

사용 예

c
gb_set_vblank_callback(on_frame);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

콜백을 세 번 호출한 뒤 0을 등록하면 호출이 해제됩니다. 다음 대기에서는 프레임 값이 4가 되지만 콜백 횟수는 3으로 유지됩니다.

gb_set_vblank_callback: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:33

c
#define gb_set_vblank_callback system_set_vblank_callback

kitaqfc/lib/system.c:24

c
void system_set_vblank_callback(SystemCallback callback)
{
    system_vblank_callback = callback;
}
gb_wait_vblank

gb_wait_vblank는 system_wait_vblank의 별칭입니다. 프레임 경계를 기다리고 소프트웨어 프레임 수를 증가시킨 뒤 등록된 콜백을 호출합니다.

다음 프레임을 기다리고 카운터를 증가시킨 뒤 등록된 콜백을 호출합니다.

사용 형식

c
void gb_wait_vblank(void);

반환값

반환값은 없습니다.

사용 조건과 제한

헤더는 구현 이름으로 확장되는 객체형 매크로를 정의합니다. 인수, 반환값, 상태 접근은 대상 함수와 같으며 별칭 때문에 별도의 상태나 처리가 추가되지는 않습니다. 완전한 예제와 빌드 명령처럼 입력에는 input.h/input.c를, 프레임 서비스에는 system.h/system.c를 사용하세요.

FC 라이브러리가 제공하는 gb_ 표기입니다. Game Boy 하드웨어가 아니라 FC의 system_* 구현을 호출합니다. 대응하는 fc_ 이름도 같은 FC 구현을 호출합니다.

콜백은 이 함수 안에서 동기적으로 실행됩니다. 콜백을 등록한다고 인터럽트 핸들러가 설치되는 것은 아닙니다. 카운터는 이 래퍼의 대기 횟수를 세며 실제 하드웨어 프레임 경과를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

system_wait_vblank

사용 예

c
gb_wait_vblank();gb_wait_vblank();gb_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

세 번 대기하면 콜백도 세 번 호출되며, 마지막 콜백 안에서 읽은 카운터 값은 3입니다.

gb_wait_vblank: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\system_gb_aliases.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\system_gb_aliases.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:32

c
#define gb_wait_vblank system_wait_vblank

kitaqfc/lib/system.c:16

c
void system_wait_vblank(void)
{
    __nmi_wait();
    kq_system_frame = (u16)(kq_system_frame + 1);
    if (system_vblank_callback != 0) system_vblank_callback();
}
system_disable_interrupts

마스킹 가능한 CPU 인터럽트를 비활성화합니다.

사용 형식

c
void system_disable_interrupts(void);

반환값

반환값은 없습니다.

사용 조건과 제한

보호된 갱신 구간은 짧게 유지하고, 끝난 뒤 인터럽트를 적절히 복원하세요. 인터럽트 마스킹과 프레임 대기 함수의 실행 조건은 별개의 문제입니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
system_disable_interrupts();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 비활성화와 활성화를 짝지어 사용합니다. 별도의 검증 프로그램에서는 CPU 인터럽트 상태도 확인합니다.

system_disable_interrupts: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:25

선언 주석 원문
Disable maskable CPU IRQs; this does not disable the PPU's NMI source.

kitaqfc/lib/system.c:48

c
void system_disable_interrupts(void)
{
    __irq_disable();
}
system_enable_interrupts

마스킹 가능한 CPU 인터럽트를 활성화합니다.

사용 형식

c
void system_enable_interrupts(void);

반환값

반환값은 없습니다.

사용 조건과 제한

활성화 전에 인터럽트 소스와 핸들러를 준비하세요. FC에서는 NMI의 활성화·비활성화와 별개입니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
system_enable_interrupts();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 인터럽트를 복원합니다. FC 프로그램은 처리 후 해제하지 않은 IRQ가 반복 실행되지 않도록 먼저 APU 프레임 IRQ와 DMC IRQ 소스를 비활성화합니다.

system_enable_interrupts: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:23

선언 주석 원문
Enable maskable CPU IRQs through the intrinsic; NMI is controlled separately.

kitaqfc/lib/system.c:42

c
void system_enable_interrupts(void)
{
    __irq_enable();
}
system_get_frame

프레임 대기 래퍼의 16비트 카운터를 읽습니다.

사용 형식

c
u16 system_get_frame(void);

반환값

현재 카운터 값인 0~65535를 반환합니다.

사용 조건과 제한

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
result[3]=system_get_frame();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 세 번 대기한 뒤 3을 읽습니다. 순환 검사를 위해 카운터를 명시적으로 65535로 설정하고, 다음 대기 후 0이 되는지 확인합니다.

system_get_frame: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:19

선언 주석 원문
Return the wrapping 16-bit count of completed waits through this wrapper.

kitaqfc/lib/system.c:30

c
u16 system_get_frame(void)
{
    return kq_system_frame;
}
system_get_frame8

짧은 주기의 반복에 사용할 수 있도록 프레임 카운터의 하위 8비트를 읽습니다.

사용 형식

c
u8 system_get_frame8(void);

반환값

하위 바이트인 0~255를 반환합니다.

사용 조건과 제한

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
result[4]=system_get_frame8();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 세 번 대기한 뒤 3을 읽고, 16비트 카운터가 순환하면 0을 읽습니다.

system_get_frame8: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:21

선언 주석 원문
Return the low byte of the software frame counter.

kitaqfc/lib/system.c:36

c
u8 system_get_frame8(void)
{
    return (u8)kq_system_frame;
}
system_init

카운터와 등록된 콜백을 지워 프레임 관리를 초기화합니다.

사용 형식

c
void system_init(void);

반환값

반환값은 없습니다.

사용 조건과 제한

카운터는 이 래퍼를 통한 대기 횟수를 셉니다. 실제 하드웨어에서 경과한 프레임 수를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
system_init();result[0]=system_get_frame();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

예제는 콜백을 등록하기 전에 초기 프레임 번호가 0인지 확인합니다.

system_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:13

선언 주석 원문
Reset the software frame counter and callback storage, then enable NMI.

kitaqfc/lib/system.c:7

c
void system_init(void)
{
    kq_system_frame = 0;
    system_vblank_callback = 0;
    // NMI waits require a running PPU and its enabled NMI source, independently of the CPU IRQ mask.
    __nmi_enable();
}
system_set_vblank_callback

프레임 대기 뒤에 호출할 함수를 등록하거나 교체하거나 해제합니다.

사용 형식

c
void system_set_vblank_callback(SystemCallback callback);

매개변수

callback

호출할 함수입니다. 0을 지정하면 콜백을 해제합니다.

반환값

반환값은 없습니다.

사용 조건과 제한

인수와 반환값이 없는 함수를 가리키는 일반 포인터입니다. 뱅크 번호를 포함하지 않으므로 호출될 수 있는 동안 대상 코드가 매핑되어 있어야 합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
system_set_vblank_callback(on_frame);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

콜백을 세 번 호출한 뒤 0을 등록하면 호출이 해제됩니다. 다음 대기에서는 프레임 값이 4가 되지만 콜백 횟수는 3으로 유지됩니다.

system_set_vblank_callback: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:17

선언 주석 원문
Replace the per-wait callback; null disables callback dispatch.

kitaqfc/lib/system.c:24

c
void system_set_vblank_callback(SystemCallback callback)
{
    system_vblank_callback = callback;
}
system_wait_vblank

다음 프레임을 기다리고 카운터를 증가시킨 뒤 등록된 콜백을 호출합니다.

사용 형식

c
void system_wait_vblank(void);

반환값

반환값은 없습니다.

사용 조건과 제한

콜백은 이 함수 안에서 동기적으로 실행됩니다. 콜백을 등록한다고 인터럽트 핸들러가 설치되는 것은 아닙니다. 카운터는 이 래퍼의 대기 횟수를 세며 실제 하드웨어 프레임 경과를 자동 측정하지 않습니다. 16비트 범위를 넘으면 0으로 순환합니다.

FC 초기화는 NMI를 활성화합니다. 대기가 끝나려면 NMI가 정상적으로 전달되어야 합니다. CPU IRQ를 마스킹해도 NMI는 중지되지 않습니다.

사용 예

c
system_wait_vblank();system_wait_vblank();system_wait_vblank();result[1]=frame_calls;result[2]=frame_seen;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

세 번 대기하면 콜백도 세 번 호출되며, 마지막 콜백 안에서 읽은 카운터 값은 3입니다.

system_wait_vblank: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\runtime.c .\kitaq-docs\samples\api-examples\fc\batch_services.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch_services.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/system.h:15

선언 주석 원문
Wait for NMI, count the completed wait, then invoke the registered callback.

kitaqfc/lib/system.c:16

c
void system_wait_vblank(void)
{
    __nmi_wait();
    kq_system_frame = (u16)(kq_system_frame + 1);
    if (system_vblank_callback != 0) system_vblank_callback();
}

tilemap.h — 사각 타일맵 영역 조작

행 우선 타일 배열을 읽고 타일 ID 임계값으로 고체 여부를 검사합니다. 사각형 검사는 내부 전체가 아닌 네 모서리만 확인합니다.

nes_tilemap_get

타일 ID를 읽습니다. 맵 밖의 좌표이면 255를 반환합니다.

사용 형식

c
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty);

매개변수

map

행 우선 타일 배열, 크기, 고체 판정 임계값이 있는 유효한 맵입니다. 호출 중에는 저장 공간을 읽을 수 있어야 합니다.

tx

타일 X 좌표입니다. 1단위는 8픽셀입니다.

ty

타일 Y 좌표입니다. 1단위는 8픽셀입니다.

반환값

타일 바이트 값 또는 255입니다. 저장된 값이 255이면 맵 밖의 결과와 구별할 수 없습니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[1]=nes_tilemap_get(&map,2,1);result[2]=nes_tilemap_get(&map,5,1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_tilemap_get: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:19

선언 주석 원문
Return a tile byte, or 0xFF when coordinates exceed map dimensions. The
map/storage pointers must be valid; 0xFF may also be an actual stored tile.

kitaqfc/lib/tilemap.c:32

c
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
    if (tx >= map->width)
    {
        return 0xFF;
    }
    if (ty >= map->height)
    {
        return 0xFF;
    }
    return map->tiles[nes_tilemap_index(map, tx, ty)];
}
nes_tilemap_index

타일 좌표를 행 우선 바이트 오프셋 ty*width+tx로 변환합니다.

사용 형식

c
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty);

매개변수

map

행 우선 타일 배열, 크기, 고체 판정 임계값이 있는 유효한 맵입니다. 호출 중에는 저장 공간을 읽을 수 있어야 합니다.

tx

타일 X 좌표입니다. 1단위는 8픽셀입니다.

ty

타일 Y 좌표입니다. 1단위는 8픽셀입니다.

반환값

계산된 16비트 오프셋입니다. 좌표의 범위는 검사하지 않습니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[0]=nes_tilemap_index(&map,2,1);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_tilemap_index: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:16

선언 주석 원문
Compute a row-major byte offset by adding the row stride ty times. This
internal arithmetic helper does not validate coordinates.

kitaqfc/lib/tilemap.c:14

c
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
    unsigned short index;
    unsigned char y;

    index = tx;
    y = 0;
    while (y != ty)
    {
        index = (unsigned short)(index + map->width);
        y = y + 1;
    }

    return index;
}
nes_tilemap_point_solid

타일을 solid_from과 비교합니다. 타일 255와 맵 밖의 좌표는 항상 고체로 판정합니다.

사용 형식

c
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty);

매개변수

map

행 우선 타일 배열, 크기, 고체 판정 임계값이 있는 유효한 맵입니다. 호출 중에는 저장 공간을 읽을 수 있어야 합니다.

tx

타일 X 좌표입니다. 1단위는 8픽셀입니다.

ty

타일 Y 좌표입니다. 1단위는 8픽셀입니다.

반환값

고체이면 1, 아니면 0입니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[3]=nes_tilemap_point_solid(&map,2,1);result[4]=nes_tilemap_point_solid(&map,0,0);result[5]=nes_tilemap_point_solid(&map,5,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_tilemap_point_solid: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:22

선언 주석 원문
Treat out-of-bounds/0xFF tiles as solid, then compare other tile IDs with
the map's solid_from threshold.

kitaqfc/lib/tilemap.c:47

c
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty)
{
    unsigned char tile;

    tile = nes_tilemap_get(map, tx, ty);
    if (tile == 0xFF)
    {
        return 1;
    }
    if (tile >= map->solid_from)
    {
        return 1;
    }
    return 0;
}
nes_tilemap_world_box_solid

월드 사각형의 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래 모서리를 검사합니다.

사용 형식

c
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height);

매개변수

map

행 우선 타일 배열, 크기, 고체 판정 임계값이 있는 유효한 맵입니다. 호출 중에는 저장 공간을 읽을 수 있어야 합니다.

world_x

정수 픽셀 단위의 월드 X입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

world_y

정수 픽셀 단위의 월드 Y입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

width

사각형 너비로, 1~255픽셀입니다. 먼 쪽 가장자리 world_x+width-1을 2048 미만으로 유지하세요.

height

사각형 높이로, 1~255픽셀입니다. 먼 쪽 가장자리 world_y+height-1을 2048 미만으로 유지하세요.

반환값

모서리 중 하나라도 고체이면 1, 아니면 0입니다.

사용 조건과 제한

너비와 높이는 0이면 안 됩니다. 모서리 사이의 변과 내부는 검사하지 않으므로 큰 사각형은 장애물을 놓칠 수 있습니다. 샘플에서는 네 모서리가 모두 비어 있으면 중앙의 장애물에 대해 0을 반환합니다.

사용 예

c
result[12]=nes_tilemap_world_box_solid(&map,8,8,16,8);result[13]=nes_tilemap_world_box_solid(&map,0,0,40,24);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_tilemap_world_box_solid: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:33

선언 주석 원문
Test only the four corners of a nonempty world box. Interior/edge tiles
between the corners are not scanned, so large boxes can miss obstacles.
Width/height must be nonzero and endpoint arithmetic must not overflow.

kitaqfc/lib/tilemap.c:86

c
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height)
{
    unsigned short x2;
    unsigned short y2;

    x2 = (unsigned short)(world_x + width - 1);
    y2 = (unsigned short)(world_y + height - 1);

    if (nes_tilemap_world_point_solid(map, world_x, world_y) != 0)
    {
        return 1;
    }
    if (nes_tilemap_world_point_solid(map, x2, world_y) != 0)
    {
        return 1;
    }
    if (nes_tilemap_world_point_solid(map, world_x, y2) != 0)
    {
        return 1;
    }
    if (nes_tilemap_world_point_solid(map, x2, y2) != 0)
    {
        return 1;
    }

    return 0;
}
nes_tilemap_world_point_solid

월드의 점을 8픽셀 단위 타일 좌표로 바꾸고 고체 여부를 검사합니다.

사용 형식

c
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y);

매개변수

map

행 우선 타일 배열, 크기, 고체 판정 임계값이 있는 유효한 맵입니다. 호출 중에는 저장 공간을 읽을 수 있어야 합니다.

world_x

정수 픽셀 단위의 월드 X입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

world_y

정수 픽셀 단위의 월드 Y입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

반환값

해당 타일이 고체이면 1, 아니면 0입니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[10]=nes_tilemap_world_point_solid(&map,16,8);result[11]=nes_tilemap_world_point_solid(&map,0,0);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_tilemap_world_point_solid: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:29

선언 주석 원문
Convert a world point to tile coordinates and apply the tile solidity rule.

kitaqfc/lib/tilemap.c:77

c
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y)
{
    return nes_tilemap_point_solid(map, nes_world_to_tile8_x(world_x), nes_world_to_tile8_y(world_y));
}
nes_world_to_tile8_x

월드 X를 8로 나누고 타일 좌표 바이트로 줄입니다.

사용 형식

c
unsigned char nes_world_to_tile8_x(unsigned short world_x);

매개변수

world_x

정수 픽셀 단위의 월드 X입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

반환값

world_x >> 3의 하위 8비트이며, 2048픽셀마다 래핑됩니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[6]=nes_world_to_tile8_x(23);result[7]=nes_world_to_tile8_x(24);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_world_to_tile8_x: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:25

선언 주석 원문
Convert pixels to eight-pixel tile X and narrow to a byte. Keep world X
below 2048 when wraparound is not intended.

kitaqfc/lib/tilemap.c:65

c
unsigned char nes_world_to_tile8_x(unsigned short world_x)
{
    return (unsigned char)(world_x >> 3);
}
nes_world_to_tile8_y

월드 Y를 8로 나누고 타일 좌표 바이트로 줄입니다.

사용 형식

c
unsigned char nes_world_to_tile8_y(unsigned short world_y);

매개변수

world_y

정수 픽셀 단위의 월드 Y입니다. 래핑을 피하려면 2048 미만으로 유지하세요.

반환값

world_y >> 3의 하위 8비트이며, 2048픽셀마다 래핑됩니다.

사용 조건과 제한

유효한 맵과 읽기 가능한 비어 있지 않은 배열이 필요합니다. 고체 여부는 별도의 충돌 마스크가 아니라 타일 ID 임계값으로 판단합니다.

사용 예

c
result[8]=nes_world_to_tile8_y(15);result[9]=nes_world_to_tile8_y(16);

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

5×3 맵의 (2,1)에 고체 타일 5를 놓습니다. 배열 인덱스는 7이고 맵 밖을 읽으면 255입니다. 장애물에 모서리가 닿으면 1을 반환하지만, 40×24 사각형이 장애물을 내부에만 포함하면 0을 반환하여 모서리만 검사하는 한계를 보여 줍니다.

nes_world_to_tile8_y: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\batch2_tilemap.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\batch2_tilemap.nes --no-cache --no-disasm --mapper=nrom --nes-chr=.\kitaq-docs\samples\font.chr
구현과 소스

kitaqfc/lib/tilemap.h:27

선언 주석 원문
Convert pixels to eight-pixel tile Y with the same byte-coordinate limit.

kitaqfc/lib/tilemap.c:71

c
unsigned char nes_world_to_tile8_y(unsigned short world_y)
{
    return (unsigned char)(world_y >> 3);
}

vram.h — 큐를 통한 VRAM 갱신과 전송

나중에 실행할 VRAM 쓰기 명령 큐를 비우고 오버플로 기록을 지웁니다. 화면을 지우거나 타일 그래픽을 전송하거나 하드웨어 VRAM을 할당하지는 않습니다.

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

__vramq_commit으로 내장 큐를 커밋한 뒤 __nmi_wait로 NMI 처리를 기다립니다. 기본 핸들러가 준비된 큐를 처리합니다. NMI와 핸들러가 정상 동작하도록 유지하세요. 큰 전송을 여러 VBlank로 나누지는 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

vram_clear_queue

대기 중인 VRAM 명령을 실행하지 않고 버리며 넘침 플래그도 지웁니다.

사용 형식

c
void vram_clear_queue(void);

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
vram_clear_queue();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_clear_queue: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:15

선언 주석 원문
Discard queued commands and explicitly reset the overflow latch.

kitaqfc/lib/vram.c:17

c
void vram_clear_queue(void)
{
    __vramq_clear();
    __vramq_clear_overflow();
}
vram_flush

__vramq_commit으로 내장 큐를 커밋한 뒤 __nmi_wait로 NMI 처리를 기다립니다. 기본 핸들러가 준비된 큐를 처리합니다. NMI와 핸들러가 정상 동작하도록 유지하세요. 큰 전송을 여러 VBlank로 나누지는 않습니다.

사용 형식

c
void vram_flush(void);

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

사용 예

c
vram_flush(); // One small write through the VBlank/NMI path.
__scroll_set(0,0);
if (vram_get_queue_used()!=0) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_flush: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:41

선언 주석 원문
Commit queued records for consumption and wait for the NMI path.

kitaqfc/lib/vram.c:115

c
void vram_flush(void)
{
    __vramq_commit();
    __nmi_wait();
}
vram_flush_now

__vramq_exec를 즉시 호출합니다. __vramq_commit으로 준비 플래그를 설정한 경우에만 실행하며 커밋하지 않은 큐는 그대로 둡니다. 실행을 마치면 길이와 준비 플래그를 지우지만 오버플로 기록은 유지합니다. 이 함수는 커밋하거나 안전한 PPU 쓰기 시점을 기다리지 않습니다.

사용 형식

c
void vram_flush_now(void);

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

사용 예

c
// Rendering is stopped here. On FC, commit readiness before direct execution.
__vramq_commit();
vram_flush_now();
after_flush=vram_get_queue_used();
if (after_flush!=0) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_flush_now: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:39

선언 주석 원문
Invoke the queue executor directly. Its ready flag must already be set by
commit, and the caller supplies appropriate PPU timing.

kitaqfc/lib/vram.c:109

c
void vram_flush_now(void)
{
    __vramq_exec();
}
vram_get_overflowed

큐 넘침 플래그를 지우지 않고 읽습니다. 이전 등록 실패가 남아 있을 수 있습니다.

사용 형식

c
u8 vram_get_overflowed(void);

반환값

기록된 오버플로가 없으면 0입니다. 등록이 거부되면 1이 되고 명시적으로 초기화할 때까지 유지됩니다. 큐 실행만으로는 지워지지 않습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
overflow_seen=vram_get_overflowed();
if (overflow_seen!=1) failures++;
// Reading the latch again does not clear it.
if (vram_get_overflowed()!=1) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_get_overflowed: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:52

선언 주석 원문
Read the overflow latch without clearing it.

kitaqfc/lib/vram.c:144

c
u8 vram_get_overflowed(void)
{
    return __vramq_overflow();
}
vram_get_queue_capacity

현재 사용량과 관계없이 명령 버퍼의 전체 용량을 반환합니다.

사용 형식

c
u8 vram_get_queue_capacity(void);

반환값

여기에 설명한 기종별 단위의 부호 없는 8비트 값입니다. 조회만으로 큐를 변경하거나 실행하지 않습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
capacity=vram_get_queue_capacity();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_get_queue_capacity: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:50

선언 주석 원문
Return the queue's total capacity in bytes, independent of its current usage.
This describes the command buffer, not free space in PPU VRAM.

kitaqfc/lib/vram.c:138

c
u8 vram_get_queue_capacity(void)
{
    return __vramq_capacity();
}
vram_get_queue_free

명령 버퍼의 전체 용량에서 현재 사용량을 뺀 여유 용량을 반환합니다. 다음 레코드나 작업 묶음 전체가 들어가는지 등록 전에 확인하세요. 이 조회 함수는 공간을 예약하지 않습니다.

사용 형식

c
u8 vram_get_queue_free(void);

반환값

여기에 설명한 기종별 단위의 부호 없는 8비트 값입니다. 조회만으로 큐를 변경하거나 실행하지 않습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
available=vram_get_queue_free();
if (used+available!=capacity) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_get_queue_free: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:47

선언 주석 원문
Return the remaining command-buffer capacity in bytes. A command requires
space for its complete encoded record, including its address and metadata.
Read before committing: NMI may consume a committed queue asynchronously.

kitaqfc/lib/vram.c:131

c
u8 vram_get_queue_free(void)
{
    return (u8)(__vramq_capacity() - __vramq_len());
}
vram_get_queue_used

대기 중인 명령이 현재 차지하는 명령 버퍼 사용량을 반환합니다. 비디오 메모리에 쓸 데이터의 바이트 수가 아닙니다.

사용 형식

c
u8 vram_get_queue_used(void);

반환값

여기에 설명한 기종별 단위의 부호 없는 8비트 값입니다. 조회만으로 큐를 변경하거나 실행하지 않습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
used=vram_get_queue_used();

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_get_queue_used: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:43

선언 주석 원문
Return the encoded queue length in bytes, not the number of commands.

kitaqfc/lib/vram.c:122

c
u8 vram_get_queue_used(void)
{
    return __vramq_len();
}
vram_init

나중에 실행할 VRAM 쓰기 명령 큐를 비우고 오버플로 기록을 지웁니다. 화면을 지우거나 타일 그래픽을 전송하거나 하드웨어 VRAM을 할당하지는 않습니다.

사용 형식

c
void vram_init(void);

반환값

없음(void).

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
vram_init();
if (vram_get_queue_used()!=0 || vram_get_overflowed()!=0) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_init: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:13

선언 주석 원문
Clear the intrinsic queue and its overflow latch.

kitaqfc/lib/vram.c:10

c
void vram_init(void)
{
    __vramq_clear();
    __vramq_clear_overflow();
}
vram_queue_bg_block

src의 w × h개 타일 번호를 base의 맵에 사각형으로 쓰도록 등록합니다. 원본은 한 행에 w바이트씩 빈틈없이 저장하며 대상 행 간격은 32바이트입니다. 원본 포인터를 보관하므로 실행 전에 데이터를 바꾸면 표시되는 블록에도 반영됩니다.

사용 형식

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

매개변수

base

타일과 사각형 명령은 컴파일 시의 VRAM_NT_BASE를 사용하며 기본값은 0x2000입니다. 블록 전송은 지정한 네임테이블 시작 주소(0x2000, 0x2400, 0x2800, 0x2C00)를 사용하고 미러링도 적용됩니다. 주소는 등록 시 행당 32바이트로 계산합니다. 속성 바이트를 덮어쓰지 않도록 32 × 30타일 영역 안에 배치하세요.

x, y

8×8타일 단위의 왼쪽 위 좌표이며 픽셀 단위가 아닙니다. 클리핑하지 않습니다. 대상 사각형을 x + w <= 32, GB는 y + h <= 32, FC 네임테이블 타일 영역은 y + h <= 30 안에 두세요. 타일 하나는 너비와 높이 1로 계산합니다.

w, h

타일 단위의 너비와 높이를 부호 없는 8비트 값으로 지정합니다. 블록 원본은 행 순서로 w * h바이트입니다. 크기가 0이면 화면에 쓰지 않지만 큐 사용량은 기종별 레코드 규칙을 따릅니다.

src

실행 때까지 보관하는 읽기 가능한 바이트의 근거리 포인터입니다. 실행이 끝날 때까지 데이터의 수명과 메모리·ROM 뱅크 매핑을 유지하세요. 뱅크 번호나 데이터 사본을 저장하지 않습니다. 비어 있지 않은 전송에는 유효한 원본이 필요하며 수명이 끝난 지역 배열을 사용하면 안 됩니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 6 * h바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

타일과 사각형 명령은 컴파일 시의 VRAM_NT_BASE를 사용하며 기본값은 0x2000입니다. 블록 전송은 지정한 네임테이블 시작 주소(0x2000, 0x2400, 0x2800, 0x2C00)를 사용하고 미러링도 적용됩니다. 주소는 등록 시 행당 32바이트로 계산합니다. 속성 바이트를 덮어쓰지 않도록 32 × 30타일 영역 안에 배치하세요.

행마다 레코드 하나를 사용합니다. 높이 0은 추가하지 않지만 너비 0은 행마다 레코드를 추가하고 바이트는 쓰지 않습니다. 행 좌표는 u8로 변환되어 y + row가 256에서 순환합니다. 클리핑이나 부분 등록 취소는 제공하지 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_bg_block(MAP_BASE,10,4,3,2,block);
block[0]=1; // The queued pointer sees this change when the queue executes.
if (check_result!=1) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_bg_block: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:27

선언 주석 원문
Queue each tightly packed source row as a pointer-based copy record. Keep
source storage and its ROM bank accessible until queue execution finishes;
partial queuing remains possible on overflow.

kitaqfc/lib/vram.c:54

c
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src)
{
    u8 row;
    row = 0;
    while (row < h) {
        __vramq_copy(vram_xy_addr(base, x, (u8)(y + row)), src, w);
        src = src + w;
        row = (u8)(row + 1);
    }
    return (u8)(__vramq_overflow() == 0);
}
vram_queue_bg_rect

배경 맵의 사각형을 같은 타일 번호로 채우는 작업을 등록합니다. 왼쪽 위 타일 좌표는 (x, y), 크기는 w × h타일입니다. 단색 타일은 채워진 사각형을 만들고, 무늬 타일은 각 칸에 같은 무늬를 반복합니다.

사용 형식

c
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);

매개변수

x, y

8×8타일 단위의 왼쪽 위 좌표이며 픽셀 단위가 아닙니다. 클리핑하지 않습니다. 대상 사각형을 x + w <= 32, GB는 y + h <= 32, FC 네임테이블 타일 영역은 y + h <= 30 안에 두세요. 타일 하나는 너비와 높이 1로 계산합니다.

w, h

타일 단위의 너비와 높이를 부호 없는 8비트 값으로 지정합니다. 블록 원본은 행 순서로 w * h바이트입니다. 크기가 0이면 화면에 쓰지 않지만 큐 사용량은 기종별 레코드 규칙을 따릅니다.

tile

0~255의 타일 맵 바이트 값입니다. 해당 그래픽을 따로 로드하고 원하는 패턴 테이블·주소 모드를 선택하세요. 타일 번호는 포인터나 색상 값이 아닙니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 5 * h바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

타일과 사각형 명령은 컴파일 시의 VRAM_NT_BASE를 사용하며 기본값은 0x2000입니다. 블록 전송은 지정한 네임테이블 시작 주소(0x2000, 0x2400, 0x2800, 0x2C00)를 사용하고 미러링도 적용됩니다. 주소는 등록 시 행당 32바이트로 계산합니다. 속성 바이트를 덮어쓰지 않도록 32 × 30타일 영역 안에 배치하세요.

행마다 레코드 하나를 사용합니다. 높이 0은 추가하지 않지만 너비 0은 행마다 레코드를 추가하고 바이트는 쓰지 않습니다. 행 좌표는 u8로 변환되어 y + row가 256에서 순환합니다. 클리핑이나 부분 등록 취소는 제공하지 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_bg_rect(2,4,4,3,1); // A filled 32x24 rectangle at (16,32).
if (check_result!=1) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_bg_rect: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:23

선언 주석 원문
Queue one fill per row. This is not transactional: capacity exhaustion can
leave earlier rows queued, and the loop continues attempting later rows.

kitaqfc/lib/vram.c:40

c
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile)
{
    u8 row;
    row = 0;
    while (row < h) {
        __vramq_fill(vram_xy_addr(VRAM_NT_BASE, x, (u8)(y + row)), tile, w);
        row = (u8)(row + 1);
    }
    return (u8)(__vramq_overflow() == 0);
}
vram_queue_bg_tile

배경의 (x, y)에 타일 번호 하나를 쓰는 명령을 등록합니다. 화면의 타일은 큐 실행 시 바뀝니다. 맵 항목을 쓰며, 타일 픽셀 데이터를 전송하거나 팔레트 속성을 바꾸지는 않습니다.

사용 형식

c
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);

매개변수

x, y

8×8타일 단위의 왼쪽 위 좌표이며 픽셀 단위가 아닙니다. 클리핑하지 않습니다. 대상 사각형을 x + w <= 32, GB는 y + h <= 32, FC 네임테이블 타일 영역은 y + h <= 30 안에 두세요. 타일 하나는 너비와 높이 1로 계산합니다.

tile

0~255의 타일 맵 바이트 값입니다. 해당 그래픽을 따로 로드하고 원하는 패턴 테이블·주소 모드를 선택하세요. 타일 번호는 포인터나 색상 값이 아닙니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 4바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

타일과 사각형 명령은 컴파일 시의 VRAM_NT_BASE를 사용하며 기본값은 0x2000입니다. 블록 전송은 지정한 네임테이블 시작 주소(0x2000, 0x2400, 0x2800, 0x2C00)를 사용하고 미러링도 적용됩니다. 주소는 등록 시 행당 32바이트로 계산합니다. 속성 바이트를 덮어쓰지 않도록 32 × 30타일 영역 안에 배치하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_bg_tile(2,2,1); // A solid 8x8 tile at pixel (16,16).
if (check_result!=1) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_bg_tile: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:18

선언 주석 원문
Queue one tile at the default nametable base. Return the current overflow
status, which may include a failure from an earlier operation.

kitaqfc/lib/vram.c:25

c
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile)
{
    __vramq_put(vram_xy_addr(VRAM_NT_BASE, x, y), tile);
    return (u8)(__vramq_overflow() == 0);
}
vram_queue_memcpy

src에서 dst로 연속된 len바이트를 복사하도록 등록합니다. 원본과 대상은 한 바이트씩 진행하며 타일 행 간격을 적용하지 않습니다. 맵 바이트나 쓰기 가능한 타일 패턴 바이트를 전송할 수 있지만 형식을 변환하거나 타일당 바이트 수를 계산하지는 않습니다.

사용 형식

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

매개변수

dst

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

src

실행 때까지 보관하는 읽기 가능한 바이트의 근거리 포인터입니다. 실행이 끝날 때까지 데이터의 수명과 메모리·ROM 뱅크 매핑을 유지하세요. 뱅크 번호나 데이터 사본을 저장하지 않습니다. 비어 있지 않은 전송에는 유효한 원본이 필요하며 수명이 끝난 지역 배열을 사용하면 안 됩니다.

len

연속된 바이트 수로 0~65535를 지정합니다. 타일 수가 아닙니다. 전체 범위가 유효한 원본과 쓰기 가능한 대상에 들어가도록 하세요. 범위 검사나 16비트 주소 순환 방지는 하지 않습니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 6 * ceil(len / 127)바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

len을 최대 127바이트씩 나눕니다. ceil은 올림입니다. len=0이면 레코드를 추가하지 않습니다. 127바이트 분할은 이 라이브러리 래퍼의 처리이며 하위 내장 함수는 부호 없는 8비트 개수를 받습니다. 오버플로 뒤에도 등록을 시도하고 앞선 레코드는 취소하지 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_memcpy(MAP_BASE+8*32+2,strip,3);
if (check_result!=1) failures++; // Copy three map bytes, not three tile patterns.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_memcpy: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:31

선언 주석 원문
Split a byte transfer into copy records of at most 127 bytes. The runtime
retains source pointers rather than copying payloads into the queue. Return
the overflow latch state after all records have been attempted.

kitaqfc/lib/vram.c:70

c
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len)
{
    const u8* p;
    u8 chunk;
    p = (const u8*)src;
    while (len != 0) {
        if (len > 127) chunk = 127;
        else chunk = (u8)len;
        __vramq_copy(dst, p, chunk);
        dst = (u16)(dst + chunk);
        p = p + chunk;
        len = (u16)(len - chunk);
    }
    return (u8)(__vramq_overflow() == 0);
}
vram_queue_memset

dst부터 연속된 len바이트를 한 바이트 값 value로 채우도록 등록합니다. 여러 바이트로 된 타일 패턴이 아닌 한 바이트를 반복하며, 맵의 32바이트 행 간격을 자동으로 적용하지 않습니다.

사용 형식

c
u8 vram_queue_memset(u16 dst, u8 value, u16 len);

매개변수

dst

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

value

0~255의 한 바이트를 반복해서 씁니다. 화면에서의 의미는 대상이 타일 번호인지, 픽셀 비트플레인인지, 다른 바이트 구조인지에 따라 달라집니다.

len

연속된 바이트 수로 0~65535를 지정합니다. 타일 수가 아닙니다. 전체 범위가 유효한 원본과 쓰기 가능한 대상에 들어가도록 하세요. 범위 검사나 16비트 주소 순환 방지는 하지 않습니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 5 * ceil(len / 127)바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

len을 최대 127바이트씩 나눕니다. ceil은 올림입니다. len=0이면 레코드를 추가하지 않습니다. 127바이트 분할은 이 라이브러리 래퍼의 처리이며 하위 내장 함수는 부호 없는 8비트 개수를 받습니다. 오버플로 뒤에도 등록을 시도하고 앞선 레코드는 취소하지 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_memset(MAP_BASE+8*32+10,1,5);
if (check_result!=1) failures++; // Five solid tiles form a 40x8 horizontal bar.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_memset: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:36

선언 주석 원문
Queue fills of at most 127 bytes each. Overflow can leave a partially queued
transfer; no rollback is performed.

kitaqfc/lib/vram.c:94

c
u8 vram_queue_memset(u16 dst, u8 value, u16 len)
{
    u8 chunk;
    while (len != 0) {
        if (len > 127) chunk = 127;
        else chunk = (u8)len;
        __vramq_fill(dst, value, chunk);
        dst = (u16)(dst + chunk);
        len = (u16)(len - chunk);
    }
    return (u8)(__vramq_overflow() == 0);
}
vram_queue_tile

vram_queue_tile은 같은 인수로 vram_queue_bg_tile을 직접 호출하고 같은 값을 반환합니다. 배경 맵 항목 하나를 쓰는 짧은 이름이며, 스프라이트 조작이나 타일 그래픽 전송이 아닙니다.

사용 형식

c
u8 vram_queue_tile(u8 x, u8 y, u8 tile);

매개변수

x, y

8×8타일 단위의 왼쪽 위 좌표이며 픽셀 단위가 아닙니다. 클리핑하지 않습니다. 대상 사각형을 x + w <= 32, GB는 y + h <= 32, FC 네임테이블 타일 영역은 y + h <= 30 안에 두세요. 타일 하나는 너비와 높이 1로 계산합니다.

tile

0~255의 타일 맵 바이트 값입니다. 해당 그래픽을 따로 로드하고 원하는 패턴 테이블·주소 모드를 선택하세요. 타일 번호는 포인터나 색상 값이 아닙니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 4바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

타일과 사각형 명령은 컴파일 시의 VRAM_NT_BASE를 사용하며 기본값은 0x2000입니다. 블록 전송은 지정한 네임테이블 시작 주소(0x2000, 0x2400, 0x2800, 0x2C00)를 사용하고 미러링도 적용됩니다. 주소는 등록 시 행당 32바이트로 계산합니다. 속성 바이트를 덮어쓰지 않도록 32 × 30타일 영역 안에 배치하세요.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_tile(4,2,1); // The short alias draws at pixel (32,16).
if (check_result!=1) failures++;

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_tile: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:20

선언 주석 원문
Alias the queued background-tile operation.

kitaqfc/lib/vram.c:32

c
u8 vram_queue_tile(u8 x, u8 y, u8 tile)
{
    return vram_queue_bg_tile(x, y, tile);
}
vram_queue_tiles

vram_queue_tiles는 vram_queue_memcpy를 직접 호출합니다. len은 타일 수가 아닌 바이트 수입니다. 보관하는 원본 포인터, 큐 레코드, 반환값과 실행 시점도 같습니다.

사용 형식

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

매개변수

dst

CPU 포인터가 아닌 PPU 바이트 주소(0x0000–0x3FFF)입니다. 대상은 쓰기 가능해야 하며 패턴 변경에는 CHR ROM이 아닌 CHR RAM이 필요합니다. PPU 매핑과 미러링이 적용됩니다. 연속 복사와 채우기에는 PPUCTRL 증가량을 1로 설정하세요. 안전한 PPU 쓰기 시점에 실행한 뒤 스크롤 상태를 다시 설정하세요. 실행기는 PPUADDR와 PPUDATA를 사용하고 스크롤 설정을 복원하지 않습니다.

src

실행 때까지 보관하는 읽기 가능한 바이트의 근거리 포인터입니다. 실행이 끝날 때까지 데이터의 수명과 메모리·ROM 뱅크 매핑을 유지하세요. 뱅크 번호나 데이터 사본을 저장하지 않습니다. 비어 있지 않은 전송에는 유효한 원본이 필요하며 수명이 끝난 지역 배열을 사용하면 안 됩니다.

len

연속된 바이트 수로 0~65535를 지정합니다. 타일 수가 아닙니다. 전체 범위가 유효한 원본과 쓰기 가능한 대상에 들어가도록 하세요. 범위 검사나 16비트 주소 순환 방지는 하지 않습니다.

반환값

등록 시도 후 오버플로 기록이 없으면 1, 있으면 0입니다. 현재 레코드가 들어가도 이전 오버플로 때문에 0일 수 있습니다. 여러 레코드 작업은 일부만 등록될 수 있으며 되돌리지 않습니다. 길이가 0이어도 기존 기록 때문에 0을 반환할 수 있습니다.

사용 조건과 제한

FC는 고정 128바이트 내장 버퍼에 저장된 명령 레코드의 바이트 수를 셉니다. 타일 쓰기는 4바이트, 채우기는 5바이트, 포인터 복사는 6바이트이며 메타데이터를 포함합니다. 준비된 큐는 NMI가 소비할 수 있으므로 커밋 전에 확인·등록하고 생산자와 소비자를 조율하세요.

이 호출 전체를 등록하려면 명령 버퍼에 6 * ceil(len / 127)바이트가 필요합니다. 수식은 관리 정보를 포함한 레코드 크기이며 전송 데이터 크기가 아닙니다. 여러 레코드 작업 전에는 여유 용량이 충분한지 확인하세요.

len을 최대 127바이트씩 나눕니다. ceil은 올림입니다. len=0이면 레코드를 추가하지 않습니다. 127바이트 분할은 이 라이브러리 래퍼의 처리이며 하위 내장 함수는 부호 없는 8비트 개수를 받습니다. 오버플로 뒤에도 등록을 시도하고 앞선 레코드는 취소하지 않습니다.

큐 등록과 실행이 겹치지 않도록 조율하세요. 인터럽트가 실행 중일 때 추가하거나 관리 변수를 직접 바꾸거나 보관된 원본 데이터를 의도치 않게 덮어쓰지 마세요. 이 API는 잠금이나 이중 버퍼링을 제공하지 않습니다. 용량은 명령 버퍼의 용량이며 하드웨어 VRAM의 빈 바이트 수가 아닙니다.

사용 예

c
check_result=vram_queue_tiles(MAP_BASE+8*32+6,strip+1,2);
if (check_result!=1) failures++; // This alias also measures length in bytes.

호출 부분을 보여 주는 코드입니다. 완전한 프로그램에 넣을 때는 명시된 초기화 및 버퍼 조건을 충족해야 합니다.

예상 결과

완성 프로그램으로 지연 쓰기, 큐 용량 관리, 오버플로 복구를 익힙니다. 그리기 등록 7회가 GB에서는 전체 32슬롯 중 7슬롯, FC에서는 전체 128바이트 중 52바이트를 사용합니다. CAPACITY/USED/FREE는 각각 032/007/025, 128/052/076입니다. AFTER FLUSH와 FAILED CHECKS는 000, OVERFLOW SEEN은 001이어야 합니다. 가득 찬 큐에 의도적으로 추가해 알림을 검사합니다. 읽거나 실행해도 알림은 남고 큐를 지우면 사라집니다. FC에서는 커밋하지 않은 직접 실행이 큐를 그대로 두는지도 확인합니다.

이미지 좌표는 왼쪽 위를 원점으로 하는 픽셀 단위입니다. (16,16)과 (32,16)에 채워진 8 × 8 정사각형, (16,32)에 채워진 32 × 24 사각형, (80,64)에 40 × 8 가로 막대가 나타나야 합니다. 마지막 VBlank 쓰기로 (144,80)에 8 × 8 정사각형을 추가합니다. 취소한 쓰기 위치 (144,24)는 비어 있어야 합니다. DMG는 흰 바탕에 검은 도형, CGB는 흰 바탕에 빨강·파랑·초록 도형, FC는 검은 바탕에 빨강·파랑·초록 도형입니다. 에뮬레이터 이미지이며 실제 기기 검증은 아닙니다.

복사 처리를 구별하기 위해 자체 제작한 8 × 8 패턴 세 가지를 사용합니다. 타일 1은 전체 채움, 2는 왼쪽 네 열만 채움, 3은 위쪽 한 픽셀에서 아래쪽 여덟 픽셀로 넓어지는 왼쪽 정렬 삼각형입니다. (80,32)의 3 × 2타일 블록은 [1,2,3] / [3,0,2]여야 하며 0은 빈칸입니다. 등록 후 원본 첫 바이트를 바꾸므로 왼쪽 위가 채워지면 원본 포인터를 보관한다는 뜻입니다. (16,64)의 복사는 [1,2,3], (48,64)의 tiles 별칭은 [2,3]을 표시합니다. 크기는 각각 24 × 16, 24 × 8, 16 × 8픽셀입니다.

FC에서는 위쪽 정사각형 두 개, 세 패턴 복사, 마지막 VBlank 정사각형이 빨간색입니다. 32 × 24 사각형과 두 패턴 복사는 파란색이고, 3 × 2타일 블록과 40 × 8 막대는 초록색입니다. 배경은 검은색이며, 그려진 각 픽셀의 위치와 색을 검사합니다.

vram_queue_tiles: NROM
NROM

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaqfc\lib\vram.c .\kitaq-docs\samples\api-examples\fc\vram_queue_shapes.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\vram_queue_shapes.nes --mapper=nrom --nes-chr=.\kitaq-docs\samples\api-examples\fc\vram_shapes.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vram.h:33

선언 주석 원문
Alias the byte-copy queue helper; len remains a byte count, not a tile count.

kitaqfc/lib/vram.c:87

c
u8 vram_queue_tiles(u16 dst, const void* src, u16 len)
{
    return vram_queue_memcpy(dst, src, len);
}

vrc6_sound.h — VRC6 확장 음원

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

VRC6_PULSE_DUTY_12/25/50/75의 값은 0x00/0x10/0x20/0x30입니다. 이름의 숫자는 측정한 듀티 백분율이 아닙니다. KUROSAKI는 코드 d를 (d+1)/16으로 모델링합니다.

nes_vrc6_pulse1_off

VRC6 펄스 1의 타이머 상위/활성 레지스터를 지워 정지합니다.

사용 형식

c
void nes_vrc6_pulse1_off(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_pulse1_off: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:22

선언 주석 원문
Disable pulse 1 by clearing its high timer/enable register.

kitaqfc/lib/vrc6_sound.c:43

c
void nes_vrc6_pulse1_off(void)
{
    VRC6_P1_HI = 0x00;
}
nes_vrc6_pulse1_set

VRC6 펄스 1 제어값과 12비트 타이머를 쓴 뒤 채널을 켭니다.

사용 형식

c
void nes_vrc6_pulse1_set(u8 control, u16 period);

매개변수

control

VRC6 펄스 제어값: 비트 7은 게이트, 비트 6..4는 듀티 코드, 하위 니블은 음량 0..15입니다.

period

12비트 타이머 0..4095이며 상위 비트는 버립니다. 샘플은 펄스 253, 톱니파 289를 사용합니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_pulse1_set: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:18

선언 주석 원문
Write pulse-1 control and the low 12 timer bits, then enable the channel.

kitaqfc/lib/vrc6_sound.c:27

c
void nes_vrc6_pulse1_set(u8 control, u16 period)
{
    VRC6_P1_CTRL = control;
    VRC6_P1_LO = (u8)period;
    VRC6_P1_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}
nes_vrc6_pulse2_off

VRC6 펄스 2의 타이머 상위/활성 레지스터를 지워 정지합니다.

사용 형식

c
void nes_vrc6_pulse2_off(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_pulse2_off: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:24

선언 주석 원문
Disable pulse 2 by clearing its high timer/enable register.

kitaqfc/lib/vrc6_sound.c:49

c
void nes_vrc6_pulse2_off(void)
{
    VRC6_P2_HI = 0x00;
}
nes_vrc6_pulse2_set

VRC6 펄스 2 제어값과 12비트 타이머를 쓴 뒤 채널을 켭니다.

사용 형식

c
void nes_vrc6_pulse2_set(u8 control, u16 period);

매개변수

control

VRC6 펄스 제어값: 비트 7은 게이트, 비트 6..4는 듀티 코드, 하위 니블은 음량 0..15입니다.

period

12비트 타이머 0..4095이며 상위 비트는 버립니다. 샘플은 펄스 253, 톱니파 289를 사용합니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_pulse2_set: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:20

선언 주석 원문
Write pulse-2 control and the low 12 timer bits, then enable the channel.

kitaqfc/lib/vrc6_sound.c:35

c
void nes_vrc6_pulse2_set(u8 control, u16 period)
{
    VRC6_P2_CTRL = control;
    VRC6_P2_LO = (u8)period;
    VRC6_P2_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}
nes_vrc6_saw_off

VRC6 톱니파의 타이머 상위/활성 레지스터를 지워 정지합니다.

사용 형식

c
void nes_vrc6_saw_off(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_saw_off: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:28

선언 주석 원문
Disable the saw channel by clearing its high timer/enable register.

kitaqfc/lib/vrc6_sound.c:63

c
void nes_vrc6_saw_off(void)
{
    VRC6_SAW_HI = 0x00;
}
nes_vrc6_saw_set

VRC6 톱니파의 6비트 누산기 증가율과 12비트 타이머를 쓴 뒤 활성화합니다.

사용 형식

c
void nes_vrc6_saw_set(u8 rate, u16 period);

매개변수

rate

톱니파 누산기 증가량 0..63입니다. 하위 6비트만 사용합니다.

period

12비트 타이머 0..4095이며 상위 비트는 버립니다. 샘플은 펄스 253, 톱니파 289를 사용합니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_saw_set: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:26

선언 주석 원문
Mask the saw accumulation rate to six bits, load a 12-bit timer and enable output.

kitaqfc/lib/vrc6_sound.c:55

c
void nes_vrc6_saw_set(u8 rate, u16 period)
{
    VRC6_SAW_RATE = (u8)(rate & 0x3F);
    VRC6_SAW_LO = (u8)period;
    VRC6_SAW_HI = (u8)(0x80 | ((period >> 8) & 0x0F));
}
nes_vrc6_silence_all

VRC6 세 채널을 모두 끄고 펄스 제어값과 톱니파 증가율을 지웁니다.

사용 형식

c
void nes_vrc6_silence_all(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

이 레지스터 배선에는 --mapper=vrc6(매퍼 24)을 사용하세요. VRC6가 없는 카트리지는 이 소리를 낼 수 없습니다. period의 하위 12비트만 쓰며 주파수 제어 레지스터 $9003은 설정하지 않습니다.

사용 예

c
// Mapper 24: two pulse voices followed by the saw accumulator.
#include "fc_sound_example.h"
#include "vrc6_sound.c"
void main(void) {
    // The background font occupies the first four 1 KiB CHR windows.
    *((u8*)0xD000)=0; *((u8*)0xD001)=1;
    *((u8*)0xD002)=2; *((u8*)0xD003)=3;
    sound_begin("VRC6 PULSE PULSE SAW");
    nes_vrc6_silence_all();
    nes_vrc6_pulse1_set(0x7C,253);
    sound_wait(45); nes_vrc6_pulse1_off(); sound_wait(15);
    nes_vrc6_pulse2_set(0x3C,253);
    sound_wait(45); nes_vrc6_pulse2_off(); sound_wait(15);
    nes_vrc6_saw_set(32,289);
    sound_wait(45); nes_vrc6_saw_off(); sound_wait(15);
    nes_vrc6_silence_all();
    sound_end();
}

예상 결과

VRC6 펄스 1, 다른 듀티의 펄스 2, 톱니파가 각각 45프레임씩, 사이에 15프레임 간격으로 납니다. 세 음높이는 약 440 Hz이며 끝에서 모두 멈춥니다.

nes_vrc6_silence_all: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc6.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc6.nes --mapper=vrc6 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc6_sound.h:16

선언 주석 원문
Clear channel-enable/high-timer registers and output controls for all VRC6 voices.
Requires the VRC6 register layout used by this module. No mapper setup or global frequency-control write is included.

kitaqfc/lib/vrc6_sound.c:16

c
void nes_vrc6_silence_all(void)
{
    VRC6_P1_HI = 0x00;
    VRC6_P2_HI = 0x00;
    VRC6_SAW_HI = 0x00;
    VRC6_P1_CTRL = 0x00;
    VRC6_P2_CTRL = 0x00;
    VRC6_SAW_RATE = 0x00;
}

vrc7_sound.h — VRC7 FM 음원

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

nes_vrc7_channel_set

FM 채널 하나의 주파수, 블록, 키 플래그, 악기, 감쇠를 설정합니다.

사용 형식

c
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags);

매개변수

channel

VRC7 채널 0..5입니다. 더 큰 값은 아무 동작도 하지 않습니다.

instrument

악기 0..15입니다. 0은 사용자 패치, 1..15는 프리셋입니다. 하위 니블만 사용합니다.

volume

VRC7 감쇠 0..15입니다. 0이 가장 크고 15가 최대 감쇠입니다. 하위 니블만 사용합니다.

fnum

주파수 번호 0..511이며 block과 함께 음높이를 정합니다. 9비트만 사용합니다.

block

옥타브 블록 0..7입니다. 하위 3비트만 사용합니다.

key_flags

0x10은 키 온, 0x20은 서스테인 플래그입니다. 다른 비트는 버립니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

악기 0은 모든 채널이 공유합니다. channel_set은 악기/감쇠보다 키/주파수 상위를 먼저 씁니다. key_off 이후 다음 음은 주파수 상위와 블록도 복원해야 합니다.

사용 예

c
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
    // Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
    *((u8*)0xA000)=0; *((u8*)0xA010)=1;
    *((u8*)0xB000)=2; *((u8*)0xB010)=3;
    *((u8*)0xC000)=4; *((u8*)0xC010)=5;
    *((u8*)0xD000)=6; *((u8*)0xD010)=7;
    sound_begin("VRC7 PRESET AND PATCH");
    nes_vrc7_silence_all();
    nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
    sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
    nes_vrc7_set_user_patch(patch);
    nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
    sound_wait(60);
    // Raw register 30 selects instrument zero and maximum attenuation.
    nes_vrc7_write(0x30,0x0F);
    nes_vrc7_key_off(0);
    nes_vrc7_silence_all(); sound_wait(30);
    sound_end();
}

예상 결과

프리셋 악기 1과 독자적인 지속형 사용자 악기 0을 같은 음높이로 각각 60프레임씩 비교합니다. 마지막에는 키 오프와 최대 감쇠로 끝납니다.

nes_vrc7_channel_set: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc7_sound.h:18

선언 주석 원문
Validate channel 0..5, then pack a 9-bit frequency, 3-bit block, key flags,
instrument and volume nibbles into its registers. Volume is the raw hardware
attenuation field, not a linear loudness value.

kitaqfc/lib/vrc7_sound.c:32

c
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags)
{
    u8 reg;
    u8 hi;
    if (channel >= 6) return;
    reg = channel;
    hi = (u8)(((fnum >> 8) & 0x01) | ((block & 0x07) << 1) | (key_flags & 0x30));
    nes_vrc7_write((u8)(0x10 + reg), (u8)fnum);
    // Key/frequency-high is written before instrument/attenuation; this sequence is not an atomic note update.
    nes_vrc7_write((u8)(0x20 + reg), hi);
    nes_vrc7_write((u8)(0x30 + reg), (u8)(((instrument & 0x0F) << 4) | (volume & 0x0F)));
}
nes_vrc7_key_off

선택한 채널의 주파수 상위/블록/키 레지스터 전체를 지웁니다.

사용 형식

c
void nes_vrc7_key_off(u8 channel);

매개변수

channel

VRC7 채널 0..5입니다. 더 큰 값은 아무 동작도 하지 않습니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

악기 0은 모든 채널이 공유합니다. channel_set은 악기/감쇠보다 키/주파수 상위를 먼저 씁니다. key_off 이후 다음 음은 주파수 상위와 블록도 복원해야 합니다.

사용 예

c
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
    // Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
    *((u8*)0xA000)=0; *((u8*)0xA010)=1;
    *((u8*)0xB000)=2; *((u8*)0xB010)=3;
    *((u8*)0xC000)=4; *((u8*)0xC010)=5;
    *((u8*)0xD000)=6; *((u8*)0xD010)=7;
    sound_begin("VRC7 PRESET AND PATCH");
    nes_vrc7_silence_all();
    nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
    sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
    nes_vrc7_set_user_patch(patch);
    nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
    sound_wait(60);
    // Raw register 30 selects instrument zero and maximum attenuation.
    nes_vrc7_write(0x30,0x0F);
    nes_vrc7_key_off(0);
    nes_vrc7_silence_all(); sound_wait(30);
    sound_end();
}

예상 결과

프리셋 악기 1과 독자적인 지속형 사용자 악기 0을 같은 음높이로 각각 60프레임씩 비교합니다. 마지막에는 키 오프와 최대 감쇠로 끝납니다.

nes_vrc7_key_off: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc7_sound.h:21

선언 주석 원문
Clear the channel's entire frequency-high/block/key register, not just the
key-on bit. A later note must restore frequency and block settings.

kitaqfc/lib/vrc7_sound.c:47

c
void nes_vrc7_key_off(u8 channel)
{
    if (channel >= 6) return;
    nes_vrc7_write((u8)(0x20 + channel), 0x00);
}
nes_vrc7_set_user_patch

VRC7 사용자 악기 레지스터에 8바이트를 순서대로 씁니다.

사용 형식

c
void nes_vrc7_set_user_patch(const u8* patch8);

매개변수

patch8

악기 0을 정의하는 8바이트입니다. NULL은 검사하지 않습니다. 호출 중 소스 뱅크의 매핑을 유지하세요.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

악기 0은 모든 채널이 공유합니다. channel_set은 악기/감쇠보다 키/주파수 상위를 먼저 씁니다. key_off 이후 다음 음은 주파수 상위와 블록도 복원해야 합니다.

사용 예

c
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
    // Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
    *((u8*)0xA000)=0; *((u8*)0xA010)=1;
    *((u8*)0xB000)=2; *((u8*)0xB010)=3;
    *((u8*)0xC000)=4; *((u8*)0xC010)=5;
    *((u8*)0xD000)=6; *((u8*)0xD010)=7;
    sound_begin("VRC7 PRESET AND PATCH");
    nes_vrc7_silence_all();
    nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
    sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
    nes_vrc7_set_user_patch(patch);
    nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
    sound_wait(60);
    // Raw register 30 selects instrument zero and maximum attenuation.
    nes_vrc7_write(0x30,0x0F);
    nes_vrc7_key_off(0);
    nes_vrc7_silence_all(); sound_wait(30);
    sound_end();
}

예상 결과

프리셋 악기 1과 독자적인 지속형 사용자 악기 0을 같은 음높이로 각각 60프레임씩 비교합니다. 마지막에는 키 오프와 최대 감쇠로 끝납니다.

nes_vrc7_set_user_patch: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc7_sound.h:14

선언 주석 원문
Copy eight caller-supplied patch bytes into user-instrument registers 0..7.

kitaqfc/lib/vrc7_sound.c:16

c
void nes_vrc7_set_user_patch(const u8* patch8)
{
    const u8* p;
    u8 i;
    p = patch8;
    i = 0;
    while (i < 8) {
        nes_vrc7_write(i, *p);
        p = p + 1;
        i = (u8)(i + 1);
    }
}
nes_vrc7_silence_all

6개 채널을 모두 키 오프하고 최대 감쇠로 악기 0을 선택합니다.

사용 형식

c
void nes_vrc7_silence_all(void);

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

악기 0은 모든 채널이 공유합니다. channel_set은 악기/감쇠보다 키/주파수 상위를 먼저 씁니다. key_off 이후 다음 음은 주파수 상위와 블록도 복원해야 합니다.

사용 예

c
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
    // Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
    *((u8*)0xA000)=0; *((u8*)0xA010)=1;
    *((u8*)0xB000)=2; *((u8*)0xB010)=3;
    *((u8*)0xC000)=4; *((u8*)0xC010)=5;
    *((u8*)0xD000)=6; *((u8*)0xD010)=7;
    sound_begin("VRC7 PRESET AND PATCH");
    nes_vrc7_silence_all();
    nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
    sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
    nes_vrc7_set_user_patch(patch);
    nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
    sound_wait(60);
    // Raw register 30 selects instrument zero and maximum attenuation.
    nes_vrc7_write(0x30,0x0F);
    nes_vrc7_key_off(0);
    nes_vrc7_silence_all(); sound_wait(30);
    sound_end();
}

예상 결과

프리셋 악기 1과 독자적인 지속형 사용자 악기 0을 같은 음높이로 각각 60프레임씩 비교합니다. 마지막에는 키 오프와 최대 감쇠로 끝납니다.

nes_vrc7_silence_all: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc7_sound.h:23

선언 주석 원문
Key off all six voices and set instrument zero with maximum attenuation.

kitaqfc/lib/vrc7_sound.c:54

c
void nes_vrc7_silence_all(void)
{
    u8 i;
    i = 0;
    while (i < 6) {
        nes_vrc7_key_off(i);
        nes_vrc7_write((u8)(0x30 + i), 0x0F);
        i = (u8)(i + 1);
    }
}
nes_vrc7_write

VRC7 선택 포트에 레지스터 번호를 쓴 뒤 데이터 포트에 값을 씁니다.

사용 형식

c
void nes_vrc7_write(u8 reg, u8 value);

매개변수

reg

VRC7 레지스터 번호입니다. 채널 0은 0x10=주파수 하위, 0x20=상위/블록/키, 0x30=악기/감쇠입니다.

value

선택한 VRC7 레지스터에 쓸 바이트입니다.

반환값

반환값은 없습니다. 사운드 레지스터나 드라이버 상태를 변경합니다.

사용 조건과 제한

--mapper=vrc7(매퍼 85)용입니다. 레지스터 선택·데이터 쌍 사이에 인터럽트 쓰기가 끼어들지 않게 하세요. 함수는 장치 안정화 지연을 넣지 않습니다. 녹음은 KUROSAKI의 FM 모델이며 실기의 음색이나 쓰기 타이밍을 검증하지 않습니다.

악기 0은 모든 채널이 공유합니다. channel_set은 악기/감쇠보다 키/주파수 상위를 먼저 씁니다. key_off 이후 다음 음은 주파수 상위와 블록도 복원해야 합니다.

사용 예

c
// Mapper 85: an FM preset followed by a simple original user instrument.
#include "fc_sound_example.h"
#include "vrc7_sound.c"
__prg_rom u8 patch[8]={0x21,0x21,0x3F,0x00,0xF0,0xF0,0x0F,0x0F};
void main(void) {
    // Map the supplied 8 KiB font as eight consecutive 1 KiB CHR windows.
    *((u8*)0xA000)=0; *((u8*)0xA010)=1;
    *((u8*)0xB000)=2; *((u8*)0xB010)=3;
    *((u8*)0xC000)=4; *((u8*)0xC010)=5;
    *((u8*)0xD000)=6; *((u8*)0xD010)=7;
    sound_begin("VRC7 PRESET AND PATCH");
    nes_vrc7_silence_all();
    nes_vrc7_channel_set(0,1,3,290,4,VRC7_KEY_ON);
    sound_wait(60); nes_vrc7_key_off(0); sound_wait(30);
    nes_vrc7_set_user_patch(patch);
    nes_vrc7_channel_set(0,0,3,290,4,VRC7_KEY_ON);
    sound_wait(60);
    // Raw register 30 selects instrument zero and maximum attenuation.
    nes_vrc7_write(0x30,0x0F);
    nes_vrc7_key_off(0);
    nes_vrc7_silence_all(); sound_wait(30);
    sound_end();
}

예상 결과

프리셋 악기 1과 독자적인 지속형 사용자 악기 0을 같은 음높이로 각각 60프레임씩 비교합니다. 마지막에는 키 오프와 최대 감쇠로 끝납니다.

nes_vrc7_write: NTSC
NTSC
NTSC — 녹음된 오디오

사용 예의 전체 프로그램

빌드 명령

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqfc\kitaqfc.exe .\kitaq-docs\samples\api-examples\fc\sound_vrc7.c -I .\kitaqfc\lib -I .\kitaq-docs\samples -o .\out\fc-vrc7.nes --mapper=vrc7 --nes-chr=.\kitaq-docs\samples\font.chr --no-cache --no-disasm
구현과 소스

kitaqfc/lib/vrc7_sound.h:12

선언 주석 원문
Write register selection followed immediately by data. No explicit device
settling delay or cartridge detection is inserted by this wrapper.
The caller must arrange device timing and serialize register/data pairs against interrupt writers.

kitaqfc/lib/vrc7_sound.c:8

c
void nes_vrc7_write(u8 reg, u8 value)
{
    VRC7_ADDR = reg;
    VRC7_DATA = value;
}

구조체, 상수, 전체 헤더

함수 외 매크로, 구조체, 배열 한도, 열거형, 별칭은 이 헤더에서 확인하세요. 원래 주석을 포함하여 수집한 소스를 그대로 실었습니다.

actor.h — 게임 액터 배열
c
/*
 * Optional declarations for the phase 11 actor helper.
 */

// Callers initialize size and metadata and pass non-null actors.
// attr/flags are retained fields; this helper does not interpret or apply them.
struct NesActor {
    unsigned short world_x;
    unsigned short world_y;
    unsigned char width;
    unsigned char height;
    unsigned char screen_x;
    unsigned char screen_y;
    unsigned char attr;
    unsigned char flags;
};

// Set the actor's 16-bit world position without updating cached screen coordinates.
void nes_actor_set_world(struct NesActor* actor, unsigned short x, unsigned short y);
// Subtract the camera and retain only the low coordinate bytes. This performs
// wrapping projection, not clipping; callers must hide actors outside the viewport.
void nes_actor_update_screen(struct NesActor* actor, unsigned short camera_x, unsigned short camera_y);
// Draw the byte-stream metasprite at cached screen coordinates and return its
// next OAM index. Actor attr/flags are not applied by this wrapper.
unsigned char nes_actor_draw_metasprite(struct NesActor* actor, unsigned char oam_index, unsigned char* metasprite);
// Test overlap in world coordinates with exclusive right/bottom edges. Keep
// position-plus-size sums within u16; touching edges are not collisions.
// Require positive widths and heights; zero extents are not rejected before the edge tests.
unsigned char nes_actor_collide(struct NesActor* a, struct NesActor* b);

kitaqfc/lib/actor.h

asset.h — 리소스 ID와 데이터 기술자
c
#ifndef ASSET_H
#define ASSET_H

#include "core.h"
#include "bank.h"

#define ASSET_TYPE_RAW   ((u8)0)
#define ASSET_TYPE_TILES ((u8)1)
#define ASSET_TYPE_MAP   ((u8)2)
#define ASSET_TYPE_SONG  ((u8)3)

// The descriptor table bank and each payload bank are independent.
// ptr is a CPU-visible address and len counts bytes; type is a tag, not a decoder selection.
typedef __packed struct AssetDesc {
    u8 type;
    u8 bank;
    const u8* ptr;
    u16 len;
} AssetDesc;

// Retain a bank-qualified descriptor table; the caller keeps its storage valid.
void asset_set_table(u8 bank, const AssetDesc* table, u8 count);
// Copy a descriptor from its ROM bank into caller storage. Null output or an
// out-of-range ID returns zero without reading the table.
u8 asset_get(u8 asset_id, AssetDesc* out_desc);
// Copy the smaller of the asset length and max_len using a bank-aware transfer.
// Return one for a valid ID even when the payload is truncated.
u8 asset_load_raw(u8 asset_id, void* dst, u16 max_len);
// Switch to the asset bank, upload tile/raw data in chunks of at most 128 bytes,
// then restore the bank recorded by this library. The caller supplies safe PPU timing.
u8 asset_load_tiles(u8 asset_id, u16 vram_dst);
// Return the asset bank or zero on lookup failure; bank zero may also be valid.
u8 asset_get_bank(u8 asset_id);
// Return the stored address without switching banks. Pair it with the descriptor
// bank before reading banked ROM; a failed lookup returns null.
const u8* asset_get_ptr(u8 asset_id);
// Return the descriptor byte length, or zero for an invalid ID.
u16 asset_get_len(u8 asset_id);

#endif

kitaqfc/lib/asset.h

attribute.h — 네임테이블 속성
c
/*
 * KITAQFC phase15 attribute patch helpers.
 */

// Copy 64 already-packed attribute bytes into shadow storage. This does not
// convert one palette ID per tile into NES attribute bitfields.
// The source must expose all 64 packed bytes and stay readable for the duration of this copy.
void nes_attr_shadow_build_from_palette_map(unsigned char* src64);
// Apply a palette to every two-by-two tile quadrant touched by the rectangle.
// Empty or out-of-range rectangles leave the shadow unchanged.
void nes_attr_shadow_fill_rect(unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pal_index);
// Queue the attribute bytes covering a tile rectangle within 32x30 tiles.
// Empty rectangles succeed; invalid bounds fail without queuing.
// Capacity failure may leave earlier rows queued.
unsigned char nes_attr_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height);

kitaqfc/lib/attribute.h

audio.h — 음악, 효과음, 음향 제어
c
#ifndef AUDIO_H
#define AUDIO_H

#include "core.h"

// Channel bits form a complete replacement mask for nes_apu_channel_enable.
#define NES_APU_ENABLE_PULSE1 0x01
#define NES_APU_ENABLE_PULSE2 0x02
#define NES_APU_ENABLE_TRIANGLE 0x04
#define NES_APU_ENABLE_NOISE 0x08
#define NES_APU_ENABLE_DMC 0x10

// Disable frame IRQs, enable the four tonal/noise channels and install quiet
// control defaults. DMC remains disabled.
void nes_apu_init(void);
// Replace all five channel-enable bits in APU_STATUS; this is not an additive enable.
void nes_apu_channel_enable(u8 mask);
// Disable every channel and clear volume/DMC output settings used by this helper.
void nes_apu_silence_all(void);

// Enable only pulse 1, disable its sweep and load control, 11-bit timer and
// length-table index. Starting this effect disables the other APU channels.
void nes_sfx_square1(u8 duty_volume, u16 period, u8 length_index);
// Enable only pulse 2 and load its control/timer/length fields, disabling other channels.
void nes_sfx_square2(u8 duty_volume, u16 period, u8 length_index);
// Enable only triangle and load linear-counter, timer and length fields.
void nes_sfx_triangle(u8 linear, u16 period, u8 length_index);
// Enable only noise and load envelope/control, period/mode and length fields.
void nes_sfx_noise(u8 volume, u8 period_mode, u8 length_index);
// Trigger the predefined pulse-1 tick effect; it inherits the exclusive-channel behavior.
void nes_sfx_tick_blip(void);
// Trigger the predefined pulse-1 movement effect.
void nes_sfx_move_blip(void);

// Convert a CPU sample address and byte length to DMC register units. Supply
// an address in 0xC000-0xFFC0 aligned to 64 bytes and a representable length
// 16*n+1 in 1..4081; inputs are narrowed without validation or bank management.
void nes_dmc_config(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);
// Enable DMC together with all four other channels by replacing the status mask.
void nes_dmc_start(void);
// Disable DMC while leaving all four other channel-enable bits set.
void nes_dmc_stop(void);
// Install sample parameters, then start DMC using the full-channel enable mask.
void nes_dmc_play(u8 flags_rate, u8 output_level, u16 sample_addr, u16 sample_len);

#endif

kitaqfc/lib/audio.h

audio_vblank.h — VBlank IRQ를 통한 음악 재생
c
#ifndef NES_AUDIO_VBLANK_H
#define NES_AUDIO_VBLANK_H
#include "core.h"

// Original MIT-licensed NMI sequencer. Channel order follows the physical APU:
// CH1 pulse 1, CH2 pulse 2, CH3 triangle, CH4 noise (indices 0, 1, 2, 3).
// Every record is five bytes: delay, CH1, CH2, CH3, CH4.
// Delay 1..255 holds the new state for that many NMI ticks. Delay 0 ends playback.
// Tonal notes 0..71 are C2..B7, tuned for NTSC. Noise 0..15 selects a long-mode
// period; 16..31 selects short mode. HOLD preserves a channel; STOP silences it.
#define NES_AUDIO_NOISE_ENVELOPE 128
#define NES_AUDIO_HOLD 255
#define NES_AUDIO_STOP 254
#define NES_AUDIO_RECORD_BYTES 5
#define NES_AUDIO_QUEUE_CAPACITY 7

// Reset the queue, feeder and diagnostic counters; silence and initialize the
// APU. Owns the four basic channels and disables DMC and APU frame IRQs.
void nes_audio_vblank_init(void);
// Copy one complete validated record to RAM and publish it atomically last.
// Return 1 on success, 0 for null/invalid/full. Call from one foreground producer.
u8 nes_audio_vblank_enqueue(const u8* record);
// Return queued records (0..7), excluding the record whose delay is in progress.
u8 nes_audio_vblank_queued(void);
// Return available record slots (0..7), not bytes.
u8 nes_audio_vblank_free(void);
// Enable consumption at the next NMI. Does not enable PPU NMI itself.
void nes_audio_vblank_start(void);
// Disable consumption, silence, discard queued records and clear the feeder.
void nes_audio_vblank_stop(void);
// Stop and attach a readable five-byte-record stream; return 0 for null, zero
// count or a count above 13107. Nonzero loop repeats the stream. Does not start.
u8 nes_audio_vblank_set_music(const u8* records,u16 count,u8 loop);
// Copy at most seven records from the attached stream. Return copies accepted.
// A one-shot feeder appends a delay-zero terminator after the last timed record.
// Keep the stream's ROM bank mapped during this foreground call only.
u8 nes_audio_vblank_refill(void);
// Set the stream, prefill the queue and start. Return 0 for invalid setup/data.
u8 nes_audio_vblank_play_music(const u8* records,u16 count,u8 loop);
// Return 1 while the BGM consumer is enabled, including a recoverable queue underrun.
u8 nes_audio_vblank_is_playing(void);
// Saturating count of starvation episodes, cleared by init; END is not starvation.
u8 nes_audio_vblank_underruns(void);
// Configure the next note: pulse bits 7..6 select duty and 3..0 select volume;
// noise uses volume 3..0, or NES_AUDIO_NOISE_ENVELOPE | decay_period (0..15)
// for a one-shot hardware envelope (larger periods decay more slowly). The
// next noise note starts at volume 15; its length permits a full decay.
// Triangle uses zero for silent, nonzero for full level.
// Return 0 for channel >=4. Hardware has no triangle volume control.
u8 nes_audio_vblank_set_timbre(u8 channel,u8 control);
// Any nonzero argument freezes both timelines and mutes immediately. Zero
// resumes from the stored delays and restores held notes on the next NMI.
// BGM is_playing remains enabled during pause. Refill may still fill the queue.
void nes_audio_vblank_pause(u8 on);
// Copy 1..7 timed records. Bits 0..3 select channels temporarily owned by SFX.
// BGM continues underneath and its current notes return when the effect ends.
// Return 1 after copying; 0 for null, invalid count/data or an empty low mask.
// Ignore mask bits 4..7. All fields are validated, even unselected channels.
// Delay zero is not accepted for SFX. Invalid input preserves the current SFX.
// Replacement restarts the effect; the first HOLD starts from a stopped note.
// The source need only stay mapped/readable until this foreground call returns.
u8 nes_audio_vblank_play_sfx(const u8* records,u8 count,u8 mask);
// Release effect ownership at the next NMI, restoring current BGM notes.
// This does not stop or restart the BGM and does not write the APU immediately.
void nes_audio_vblank_stop_sfx(void);
// NMI hook: invoke exactly once per NMI. With a custom handler or a compiler
// without automatic audio-hook linking, call it there. Never call from main
// or call it again if the compiler already inserts the hook.
// Saves A/X/Y, uses no compiler scratch bytes and reads only RAM/fixed ROM.
void __nes_audio_vblank_tick(void);
#endif

kitaqfc/lib/audio_vblank.h

bank.h — 뱅크를 고려한 코드·데이터 접근
c
#ifndef BANK_H
#define BANK_H

#include "core.h"
#include "intrinsics.h"

// Pair a bank selector with its CPU-window address; a near pointer alone cannot select the bank.
typedef __packed struct BankPtr {
    u8 bank;
    const u8* ptr;
} BankPtr;

extern u8 kq_bank_current;

// Record the bank in the library shadow and invoke the mapper-aware compiler intrinsic.
void bank_switch(u8 bank);
// Read this wrapper's bank shadow; arbitrary mapper writes do not update it.
u8 bank_get_current(void);
// Read one byte from an explicit ROM bank and 16-bit CPU address.
u8 far_data_read8(u8 bank, const void* addr);
// Read a 16-bit value through the bank-aware compiler intrinsic.
u16 far_data_read16(u8 bank, const void* addr);
// Copy len bytes from banked ROM to caller-owned writable storage.
void far_data_read(u8 bank, const void* addr, void* dst, u16 len);
// Store a bank/address pair without reading memory; null output is ignored.
void farptr_make(BankPtr* out, u8 bank, const u8* ptr);
// Read one byte using both the bank and address of the supplied far pointer.
u8 farptr_read8(BankPtr ptr);
// Read a 16-bit value using both components of the far pointer.
u16 farptr_read16(BankPtr ptr);
// Copy len bytes from a far pointer; the caller provides a sufficiently large destination.
void farptr_read(BankPtr ptr, void* dst, u16 len);

// Route a declared zero-argument function through the far-call intrinsic; this is not a general
// callback-pointer dispatcher. Mapper support and fixed-bank call-site rules still apply.
#define far_call(bank, func) __farcall((bank), func)

#endif

kitaqfc/lib/bank.h

chain.h — 위치 이력용 링 버퍼
c
#ifndef CHAIN_H
#define CHAIN_H

#include "core.h"

typedef __packed struct ChainPoint {
    s16 x;
    s16 y;
} ChainPoint;

// head identifies the newest point; count grows to capacity, then old points are overwritten.
// Capacity at most 128 keeps every head + age sum within a byte for this implementation.
typedef __packed struct Chain {
    ChainPoint* points;
    u8 capacity;
    u8 count;
    u8 head;
} Chain;

// Attach caller-owned ring storage and start with no recorded points.
// The storage must remain valid for the lifetime of the chain.
void chain_init(Chain* chain, ChainPoint* storage, u8 capacity);
// Forget the recorded points without clearing or freeing the backing storage.
void chain_clear(Chain* chain);
// Move the ring head backward and store the newest point. Once full, each push
// replaces the oldest point; zero capacity is a no-op.
void chain_push_head(Chain* chain, s16 x, s16 y);
// Read a point by age, with index zero denoting the newest point. Return zero
// for an invalid request and clear a non-null output first. Keep head + index
// within the 8-bit range: addition is narrowed before the capacity wrap.
u8 chain_get_segment(const Chain* chain, u8 index, ChainPoint* out);
// Return the number of stored points, or zero for a null chain.
u8 chain_get_count(const Chain* chain);


// Return target minus current along the shortest wrapped route.
// Supply size 1..256 and coordinates in 0..size-1; inputs are not normalized.
// Exactly half a field retains the direct displacement sign.
s16 chain_wrap_delta(u8 target, u8 current, u16 size);

// Articulated body: one current pose per joint, including the head at index 0.
// Caller-owned byte arrays keep the inner loop compact on both GB and FC.
// Heading sectors run clockwise: 0=right, 4=down, 8=left, 12=up.
// Coordinates are pixels; each wrapped dimension accepts 1..256 pixels.
typedef __packed struct {
    u8* x;
    u8* y;
    u8* heading;
    u8 capacity;
    u8 count;
    u16 width;
    u16 height;
} ChainBody;

// Attach three non-overlapping capacity-byte arrays. Return 1 on success.
// Invalid storage, zero capacity or dimensions outside 1..256 disable the body.
u8 chain_body_init(ChainBody* body, u8* x, u8* y, u8* heading, u8 capacity, u16 width, u16 height);
// Seed count joints in a straight pose behind the head; count includes the head.
// Return 0 without changing the pose if count is zero or exceeds capacity.
// Coordinates wrap to the playfield and headings are masked to 0..15.
u8 chain_body_reset(ChainBody* body, u8 count, u8 head_x, u8 head_y, u8 heading);
// Install the caller's head pose, then pull joints from front to rear.
// Each joint aims four pixels behind its updated predecessor and receives that
// predecessor's OLD heading. Each axis moves 1 pixel, or 2 when error exceeds 4.
// Call once per simulation tick; this does not advance head physics or render.
void chain_body_step(ChainBody* body, u8 head_x, u8 head_y, u8 heading);
// Append a copy of the current tail; later steps separate the new joint.
// Return 1 on growth, or 0 for an empty, disabled or full body.
u8 chain_body_grow(ChainBody* body);
// Remove all joints while retaining caller-owned storage and playfield settings.
void chain_body_clear(ChainBody* body);

#endif

kitaqfc/lib/chain.h

collision.h — 사각형 등 도형의 충돌 검사
c
/*
 * Optional declarations for the phase 11 collision helper.
 */

struct NesBox16 {
    unsigned short x;
    unsigned short y;
    unsigned char w;
    unsigned char h;
};

// Test two axis-aligned world boxes using exclusive right/bottom edges.
// Coordinate-plus-size sums must fit u16; touching edges return false.
// Both boxes must have positive dimensions; the overlap test has no separate empty-box guard.
unsigned char nes_box16_intersects(struct NesBox16* a, struct NesBox16* b);
// Test a half-open box: left/top edges are included and right/bottom edges
// excluded. Callers provide valid pointers and non-overflowing edge coordinates.
unsigned char nes_box16_contains_point(struct NesBox16* box, unsigned short px, unsigned short py);

kitaqfc/lib/collision.h

core.h — 기본 정수 자료형
c
#ifndef CORE_H
#define CORE_H

// Public scalar aliases for the compiler's byte and 16-bit integer types.
typedef unsigned char u8;
typedef unsigned short u16;
typedef signed char s8;
typedef signed short s16;

// Boolean convenience values; API functions returning bit masks need not normalize to TRUE.
#define TRUE 1
#define FALSE 0

#endif

kitaqfc/lib/core.h

danmaku.h — 탄환 풀, 부채꼴 발사, 충돌, 스침
c
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
#ifndef KITAQFC_DANMAKU_H
#define KITAQFC_DANMAKU_H
#include "core.h"
#include "intrinsics.h"

#define DANMAKU_MAX 64
// Reserve cartridge PRG RAM 0x6000..0x627F (640 bytes), enabled and writable.
// Bullet centers remain in [8,248) x [24,232). These margins prevent byte
// wraparound even at the largest signed Q4.4 velocity accepted by spawn.
extern u8 dm_x[64];
extern u8 dm_y[64];
extern u8 dm_active[64];
extern u8 dm_grazed[64];
extern u8 dm_count;
extern u8 dm_peak;
extern u8 dm_hit;
extern u8 dm_graze;
extern u8 dm_player_x;
extern u8 dm_player_y;
extern u8 dm_invulnerable;
extern u16 dm_spawned;
extern u16 dm_rejected;

// Reset the pool, counters and rotating OAM priority. Player settings survive.
void danmaku_reset(void);
// Remove all bullets and step events; preserve lifetime/peak counters.
void danmaku_clear(void);
// Allocate a bullet at pixel-center x/y with signed 1/16-pixel velocities.
// Return one on success, zero outside the playfield or when all 64 slots are full.
u8 danmaku_spawn(u8 x, u8 y, s8 vx, s8 vy);
// Emit up to 64 bullets. Angles wrap in 32 steps: 0 right, 8 down, 16 left,
// 24 up. Speed is in 1/16 pixel, clamped to 64. Each failed allocation counts.
void danmaku_fan(u8 x, u8 y, u8 direction, u8 step, u8 count, u8 speed);
// Move all live bullets once. Remove offscreen/hitting bullets. Hit uses a
// +/-3-pixel box; graze uses +/-11, once per bullet. Events describe this step.
// This updates logic only; it neither writes OAM nor calls a game damage handler.
void danmaku_step(void);
// Write up to slots consecutive shadow-OAM entries starting at first_oam.
// Rotate bullet priority by 13 modulo 64 and hide unused entries in that range.
// tile is an 8x8 sprite centered at (4,4). Return the number emitted. DMA is
// caller-owned and belongs in VBlank. Hardware's eight-per-scanline limit remains.
u8 danmaku_draw(u8 first_oam, u8 slots, u8 tile, u8 attributes);
#endif

kitaqfc/lib/danmaku.h

debug.h — RAM 기록과 단언
c
#ifndef DEBUG_H
#define DEBUG_H

#include "core.h"

// Choose 1..255 consistently across units; the valid-entry count is a byte.
#ifndef DEBUG_TRACE_MAX
#define DEBUG_TRACE_MAX 32
#endif

// Names are retained pointers, not copied strings; keep their storage and ROM mapping readable.
typedef __packed struct DebugTraceEntry {
    const u8* name;
    u16 value;
    u16 frame;
} DebugTraceEntry;

extern DebugTraceEntry kq_debug_trace_log[DEBUG_TRACE_MAX];
extern u8 kq_debug_trace_count;
extern u16 kq_debug_frame;
extern u16 kq_debug_last_assert;

// Build the RAM-resident ASSERT label and reset log length, frame tag and last
// assertion code. Old log storage bytes are not erased.
void debug_init(void);
// Choose the frame tag stored in subsequent debug entries.
void debug_set_frame(u16 frame);
// Append a named marker with the reserved marker value 0xFFFF.
void debug_mark_frame(const u8* label);
// Widen a byte value into the trace entry's 16-bit value field.
void debug_trace_u8(const u8* name, u8 value);
// Append a 16-bit value with the current frame tag.
void debug_trace_u16(const u8* name, u16 value);
// Store the assertion code even if the log is full, then attempt an ASSERT
// entry. The helper records failure but does not stop execution.
void debug_assert_fail(u16 code);
// Read the number of valid entries in the fixed trace buffer.
u8 debug_get_trace_count(void);
// Borrow the internal trace array; bound inspection by debug_get_trace_count.
const DebugTraceEntry* debug_get_trace_log(void);
// Read the latest assertion code, initially zero after debug_init.
u16 debug_get_last_assert(void);

#define KITAQGB_TRACE_U8(name, value) debug_trace_u8((const u8*)(name), (u8)(value))
#define KITAQGB_TRACE_U16(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
#define KITAQGB_TRACE(name, value) debug_trace_u16((const u8*)(name), (u16)(value))
#define KITAQGB_MARK_FRAME(label) debug_mark_frame((const u8*)(label))
#define KITAQGB_ASSERT(cond) do { if (!(cond)) debug_assert_fail((u16)1); } while (0)
#define KITAQGB_ASSERT_CODE(cond, code) do { if (!(cond)) debug_assert_fail((u16)(code)); } while (0)

#endif

kitaqfc/lib/debug.h

entity.h — 고정 배열 기반 오브젝트 풀
c
#ifndef ENTITY_H
#define ENTITY_H

#include "core.h"

// Choose capacity 1..255 consistently across units; counters are bytes and FF is the failure sentinel.
#ifndef ENTITY_MAX
#define ENTITY_MAX ((u8)16)
#endif

// Coordinates, velocities, state and timer have game-defined units.
// The sprite byte is bookkeeping only; releasing a slot does not hide or free its sprite.
typedef __packed struct Entity {
    u8 active;
    u8 type;
    s16 x;
    s16 y;
    s16 vx;
    s16 vy;
    u8 state;
    u8 timer;
    u8 sprite;
} Entity;

// An entity callback receives a slot ID, not a persistent object handle.
// Use a callback in common PRG bank 0, or keep its bank mapped throughout dispatch.
typedef void (*EntityFn)(u8 id);

// Reset every slot before the game starts using the pool.
void entity_init(void);
// Returns a reusable slot ID, or 0xFF if no slot is free.
u8 entity_create(u8 type, s16 x, s16 y);
// Release a valid slot without clearing its payload. Reallocation resets the payload;
// a saved pointer or ID must not be treated as a persistent entity identity.
void entity_destroy(u8 id);
// Returns null for an invalid or inactive ID; the returned pointer aliases reusable pool storage.
Entity* entity_get(u8 id);
// Call fn(id) for each active slot in ascending order. A null callback does nothing.
// Changes to later slots take effect during the same traversal. Do not reenter this traversal.
void entity_update_all(EntityFn fn);
// Call the drawing callback for each active slot, using the same traversal rules as update.
// The callback submits drawing data; the pool does not allocate sprites.
void entity_draw_all(EntityFn fn);
// Count occupied slots without changing entity state.
u8 entity_count_active(void);
// Release every entity and restore deterministic defaults, including the no-sprite sentinel.
void entity_clear_all(void);

#endif

kitaqfc/lib/entity.h

fc.h — 라이브러리 통합 헤더
c
#ifndef FC_H
#define FC_H

// Convenience header collecting FC interfaces. Inclusion provides declarations
// and aliases, not every implementation; link only the source modules required
// by the selected runtime, avoiding alternative helpers with conflicting symbols.
#include "core.h"
#include "intrinsics.h"
#include "system.h"
#include "input.h"
#include "runtime.h"
#include "ppu.h"
#include "ppu_direct.h"
#include "vram_queue.h"
#include "vram.h"
#include "oam.h"
#include "sprite.h"
#include "pad.h"
#include "input_repeat.h"
#include "palette.h"
#include "scroll.h"
#include "tilemap.h"
#include "nametable_asset.h"
#include "attribute.h"
#include "scene.h"
#include "metasprite.h"
#include "actor.h"
#include "entity.h"
#include "collision.h"
#include "fixed.h"
#include "physics2d.h"
#include "physics3d.h"
#include "bank.h"
#include "asset.h"
#include "debug.h"
#include "chain.h"
#include "audio.h"
#include "audio_vblank.h"
#include "fds_sound.h"
#include "vrc6_sound.h"
#include "vrc7_sound.h"
#include "mapper.h"
// This final include adds function-like macros, including zero-argument nes_oam_dma.
// Use selective headers when calling the page-argument runtime API with the same name.
#include "nes_game.h"

#endif

kitaqfc/lib/fc.h

fds.h — FDS 디스크 조작
c
#ifndef FDS_H
#define FDS_H

#include "intrinsics.h"

// Return the compile-time FDS mapper selection as 0 or 1; this is not a hardware probe.
u8 __fds_available(void);
// Return the inverted disk-absent bit from 4032 as 0 or 1; this checks presence, not transfer completion.
u8 __fds_disk_ready(void);
// Return raw status mask 4032 & 04, either 0 or 4. This is not a numbered disk-side identifier.
u8 __fds_side(void);
// Read the entire 4030 status register; this is not a stored BIOS error code.
u8 __fds_error(void);
// Poll disk presence until the absent bit clears. There is no timeout or cancellation path.
void __fds_wait_ready(void);
// Alias the same disk-presence wait as __fds_wait_ready; the call can block indefinitely.
void __fds_wait_insert(void);

#endif

kitaqfc/lib/fds.h

fds_file.h — FDS 파일 로딩
c
#ifndef FDS_FILE_H
#define FDS_FILE_H

#include "intrinsics.h"

// Native FDS32: load a non-boot file at its disk-header address, then memmove
// PRG bytes to dst. Null dst keeps the native destination (also for PPU files).
// Both CPU spans must fit $0200-$07FF or $6000-$DFFF and be owned by the caller.
// Return 0, a BIOS error, $40 for a missing transfer, or $FF for invalid input.
// Call from common code with disk-I/O interrupt/display prerequisites satisfied.
u8 __fds_load_file(u8 id, u8* dst);
// Overwrite the final non-boot PRG slot on its side, preserving its ID/name/load
// address. len must equal its packaged size; src must be a valid CPU RAM span.
// The resolved physical ordinal selects the slot, independently of file ID.
// Return 0, a BIOS error, or $FF before any write for an invalid slot/span/size.
u8 __fds_save_file(u8 id, const u8* src, u16 len);
// Query the compiled --fds-meta/--fds-manifest table, not the inserted disk; return 0 or 1.
u8 __fds_file_exists(u8 id);
// Return the compiled metadata size, or zero if id is absent. No disk directory is read.
u16 __fds_file_size(u8 id);

#endif

kitaqfc/lib/fds_file.h

fds_overlay.h — FDS 오버레이 코드
c
#ifndef FDS_OVERLAY_H
#define FDS_OVERLAY_H

#include "intrinsics.h"

// Load disk file id through BIOS, return its error byte and mark bank residency FF (unknown).
u8 __fds_load_overlay(u8 id);
// Load logical bank 1 from its separate boot file, or bank 2+ from overlay-start-id+(bank-2).
// Return zero on success, FF for invalid/unavailable mappings, or the BIOS error.
// A failed BIOS transfer marks residency FF (unknown); a matching valid guard can skip loading.
u8 __fds_load_bank(u8 bank);
// Return zero when the software residency record matches, otherwise call __fds_load_bank.
u8 __fds_require_bank(u8 bank);
// Return the software resident-bank byte, or FF after a failed bank transfer.
// This is not a checksum of overlay memory.
u8 __fds_current_bank(void);
// Compare a valid bank with the software resident-bank byte; FF (unknown) never matches.
u8 __fds_is_bank_resident(u8 bank);
// Return the linker-generated overlay-function count; zero if the table is disabled.
u8 __fds_overlay_function_count(void);
// Evaluate bank once and call the named zero-argument function using its linked bank.
// Restore the caller window before returning; restoration failure stops in common code.
u8 __fds_overlay_farcall(u8 bank, u16 func);
// Same named, zero-argument dispatch as __fds_overlay_farcall; evaluate bank once.
u8 __fds_farcall(u8 bank, u16 func);

#endif

kitaqfc/lib/fds_overlay.h

fds_save.h — FDS 저장 기능 선언
c
#ifndef FDS_SAVE_H
#define FDS_SAVE_H

// Compatibility include for FDS file/save declarations; this header adds no storage or implementation.
#include "fds_file.h"

#endif

kitaqfc/lib/fds_save.h

fds_sound.h — FDS 웨이브테이블 음원
c
#ifndef FDS_SOUND_H
#define FDS_SOUND_H

#include "intrinsics.h"

// Write 83 to 4023 and zero to 4089/408A to initialize disk/sound register gates.
void __fds_sound_enable(void);
// Copy exactly 64 readable bytes to wave RAM with wave writes enabled, then set 4089 to zero.
void __fds_wave_load(const u8* wave64);
// Stream 32 readable bytes to 4088; the caller prepares modulation state and write conditions.
void __fds_mod_load(const u8* mod32);
// Write a 12-bit frequency to 4082/4083; upper control bits in 4083 are cleared.
void __fds_freq_set(u16 freq);
// Write (volume & 3F) | 80 to 4080 for direct volume control; masking wraps, rather than clamps.
void __fds_volume_set(u8 vol);
// Write the three raw arguments to 4080, 4084 and 408A in that order.
// The legacy parameter names do not describe three independently packed envelope fields.
void __fds_env_set(u8 speed, u8 gain, u8 mode);

#endif

kitaqfc/lib/fds_sound.h

fixed.h — Q8.8 고정소수점 연산과 사각형
c
#ifndef FIXED_H
#define FIXED_H

#include "core.h"

// Signed Q8.8 spans -128 to 127 + 255/256; one integer unit is represented by 256.
typedef s16 fix8;

#define FIX_ONE ((fix8)256)
#define FIX_HALF ((fix8)128)

typedef __packed struct Vec2 {
    fix8 x;
    fix8 y;
} Vec2;

typedef __packed struct KQRect {
    s16 x;
    s16 y;
    s16 w;
    s16 h;
} KQRect;

// Convert an integer to signed 8.8 fixed point by moving it into the high byte.
// The caller must keep the result within the representable 16-bit range.
fix8 fix_from_int(s16 n);
// Discard the eight fractional bits using a signed right shift.
s16 fix_to_int(fix8 x);
// Approximate 8.8 multiplication by discarding four low bits from each operand
// before multiplying. This trades precision for a smaller intermediate product.
fix8 fix_mul(fix8 a, fix8 b);
// Scale the reduced-precision quotient back to 8.8 and return zero for b == 0.
// Callers must also ensure (b >> 4) is nonzero; the explicit check only tests b.
// The intermediate product must fit the compiler's integer arithmetic.
fix8 fix_div(fix8 a, fix8 b);
// Interpolate toward b using a quantized Q0.8 weight. Both shifts discard low
// bits, so small changes may vanish and a weight of 255 need not reach b.
fix8 fix_lerp(fix8 a, fix8 b, u8 t_q8);
// Return the magnitude in s16. The most-negative s16 has no positive s16
// representation and must be excluded by callers needing a nonnegative result.
s16 kq_abs_s16(s16 v);
// Select the smaller signed value.
s16 kq_min_s16(s16 a, s16 b);
// Select the larger signed value.
s16 kq_max_s16(s16 a, s16 b);
// Clamp to an inclusive interval; callers must provide lo <= hi.
s16 kq_clamp_s16(s16 v, s16 lo, s16 hi);
// Test overlap using exclusive right/bottom edges; touching edges do not collide.
// Require positive dimensions and edge sums that fit s16; zero-sized rectangles
// are not explicitly rejected and may be reported as overlapping.
u8 kq_rect_intersect(KQRect a, KQRect b);
// Test a half-open rectangle: left/top edges are included, right/bottom excluded.
// The coordinate-plus-size sums must remain representable as s16.
u8 kq_point_in_rect(s16 x, s16 y, KQRect r);

#endif

kitaqfc/lib/fixed.h

input.h — 버튼 상태, 누름, 놓음, 반복 입력
c
#ifndef INPUT_H
#define INPUT_H

#include "core.h"
#include "intrinsics.h"

// These are shared logical masks, not raw controller-register bit positions.
// input_update translates controller 1 before these masks are applied.
#define BTN_RIGHT  ((u8)0x01)
#define BTN_LEFT   ((u8)0x02)
#define BTN_UP     ((u8)0x04)
#define BTN_DOWN   ((u8)0x08)
#define BTN_A      ((u8)0x10)
#define BTN_B      ((u8)0x20)
#define BTN_SELECT ((u8)0x40)
#define BTN_START  ((u8)0x80)

// A new press pulses immediately; the next pulse occurs DELAY + 1 input updates later.
#ifndef INPUT_REPEAT_DELAY
#define INPUT_REPEAT_DELAY ((u8)18)
#endif

// Later pulses occur RATE + 1 updates apart; zero repeats on each held update.
#ifndef INPUT_REPEAT_RATE
#define INPUT_REPEAT_RATE ((u8)5)
#endif

extern u8 kq_input_prev_keys;
extern u8 kq_input_keys;
extern u8 kq_input_press_keys;
extern u8 kq_input_release_keys;
extern u8 kq_input_repeat_keys;

// Clear held/edge/repeat masks and reset each independent repeat countdown.
void input_init(void);
// Read controller 1 through the safe intrinsic, translate its bit order and
// derive press/release edges. Repeat delays count calls to this function, so
// call it once per intended input tick.
void input_update(void);
// Return true when any masked logical button is held.
u8 input_down(u8 mask);
// Return true when any masked button became pressed in the latest input update.
u8 input_pressed(u8 mask);
// Return true when any masked button became released in the latest update.
u8 input_released(u8 mask);
// Return true for a masked initial press or scheduled repeat pulse.
u8 input_repeat(u8 mask);
// Read the complete translated held-button mask.
u8 input_current(void);
// Read the translated held-button mask from before the latest update.
u8 input_previous(void);

#define btn_down input_down
#define btn_pressed input_pressed
#define btn_released input_released
#define btn_repeat input_repeat

#endif

kitaqfc/lib/input.h

input_repeat.h — 길게 누를 때의 반복 입력
c
/*
 * Optional declarations for the phase 9 NES pad repeat helper.
 *
 * Button bit layout:
 *   bit0=A bit1=B bit2=Select bit3=Start bit4=Up bit5=Down bit6=Left bit7=Right
 */

extern unsigned char nes_pad_repeat_delay;
extern unsigned char nes_pad_repeat_interval;
extern unsigned char nes_pad1_repeat;

// Set initial/repeat countdowns and clear repeat output. Zero countdowns are
// allowed and cause a repeat on the next held-button step.
// A fresh press emits immediately. Held repeats follow after delay+1 and then interval+1 steps.
// These are call counts, not milliseconds; use one fresh pad poll before each step.
void nes_pad_repeat_config(unsigned char delay, unsigned char interval);
// Generate per-button repeat bits from the most recently polled pad state.
// Poll first, then call once per tick; a countdown reaching zero emits on the
// following step, so an interval N leaves N decrement steps between pulses.
void nes_pad_repeat_step(void);
// Return the requested newly-pressed bits, not a normalized Boolean.
unsigned char nes_pad_trigger(unsigned char mask);
// Return the requested repeat-pulse bits from the latest repeat step.
unsigned char nes_pad_repeat(unsigned char mask);
// Return the requested newly-released bits from the latest pad poll.
unsigned char nes_pad_release(unsigned char mask);
// Return the requested currently-held bits in the NES pad layout.
unsigned char nes_pad_held(unsigned char mask);

kitaqfc/lib/input_repeat.h

intrinsics.h — 컴파일러 내장 함수
c
#ifndef INTRINSICS_H
#define INTRINSICS_H

/*
 * FC/NES native intrinsic API.
 * This header intentionally does not expose KITAQGB/CGB compatibility stubs.
 * The compiler recognizes these names and emits 6502-oriented code or
 * calls an internal 6502 helper generated by the backend.
 */

// Direct PPU operations require caller-controlled access timing. Generated helpers share runtime
// argument/scratch storage: do not assume calls are reentrant across NMI or IRQ handlers.
typedef unsigned char u8;
typedef unsigned short u16;

/* memory / bit / math */
// Copy len bytes forward between readable/writable CPU ranges; overlapping copies are not memmove.
// Zero length performs no memory transfer; argument side effects are evaluated before expansion.
void __memcpy(u8* dst, const u8* src, u16 len);
// Fill len CPU bytes with value. Zero length still evaluates argument side effects.
void __memset(u8* dst, u8 value, u16 len);
// Forward copy with an 8-bit length, zero through 255; the dynamic helper promotes it to a word count.
void __memcpy_small(u8* dst, const u8* src, u8 len);
// Fill zero through 255 CPU bytes; this is ordinary RAM access, not a PPU transfer.
void __memset_small(u8* dst, u8 value, u8 len);
// Copy exactly 16 bytes forward; both ranges must be valid and suitably non-overlapping.
void __copy16(u8* dst, const u8* src);
// Copy exactly 32 bytes forward; bank selection and source lifetime belong to the caller.
void __copy32(u8* dst, const u8* src);
// Return 0 or 1 for half-open bounds. Zero extents are empty; coordinates do not wrap across 255.
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);
// Return abs(x1-x2)+abs(y1-y2) modulo 256; large distances are not saturated.
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);
// Return y*width+x as a word without checking coordinates against map dimensions.
u16 __map_index(u8 x, u8 y, u8 width);
// Replace both persistent state bytes. Reusing a seed reproduces the sequence; this is not cryptographic randomness.
void __rng_seed(u16 seed);
// Advance the shared 16-bit LFSR and return the XOR of its two bytes; zero state is reseeded to A55A.
u8 __rng8(void);
// Return base[bit>>3] & (1<<(bit&7)): zero or the bit mask, not a normalized Boolean.
u8 __bit_test(u8* base, u16 bit);
// Set the indexed bit in caller storage without changing the other seven bits in that byte.
void __bit_set(u8* base, u16 bit);
// Clear the indexed bit in caller storage; no bounds check or interrupt exclusion is inserted.
void __bit_clear(u8* base, u16 bit);
// XOR the indexed bit in caller storage; coordinate concurrent writers to avoid lost updates.
void __bit_toggle(u8* base, u16 bit);
// Return the high byte of the unsigned 8-by-8 product, equivalent to floor(a*b/256).
u8 __mul8x8_hi(u8 a, u8 b);
// Return the low word of the unsigned product; overflow wraps without saturation.
u16 __mul16x8(u16 a, u8 b);
// Interpret a and b as signed two's-complement bit patterns and return the low word of their product.
u16 __smul16x8(u16 a, u8 b);
// Return acc+a*b modulo 65536 using unsigned multiplication.
u16 __mac16(u16 acc, u16 a, u8 b);
// Add the signed-pattern product to acc modulo 65536; the API carries the result as u16 bits.
u16 __smac16(u16 acc, u16 a, u8 b);
// FC alias of __smul16x8: signed-pattern raw product, with no Q1.7 scaling shift.
u16 __smul16x8_q1_7(u16 a, u8 b);
// FC alias of __smac16: accumulate a raw signed-pattern product modulo 65536.
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);
// Return a0*b0+a1*b1 modulo 65536. The FC backend inserts no Q8.8 rescaling or rounding.
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three unsigned word-by-byte products modulo 65536 without a final scaling shift.
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// Sum two signed-pattern products modulo 65536; no Q8.8 scaling shift is generated.
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three signed-pattern products modulo 65536; choose operand units explicitly in the caller.
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// FC alias of __sdot2_q8_8: two raw signed-pattern products, summed without rescaling.
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);
// FC alias of __sdot3_q8_8: three raw signed-pattern products, summed without rescaling.
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

/* PRG bank */
// Resolve a symbol or function placement to its byte bank number at compilation time.
u8 __bankof(u16 symbol_or_function);
// Select the mapper-specific PRG bank; the common bank and legal bank range depend on the target board.
void __bankswitch(u8 bank);
// Use the same mapper PRG-switch helper as __bankswitch; retain executable code in a valid mapping.
void __prg_bank_set(u8 bank);
// Call a declared zero-argument function; placement selects its bank and its declared type determines the result.
u8 __farcall(u8 bank, u16 func);
// Switch to the source bank, copy len bytes forward and restore the previous bank.
// Keep destination RAM and helper code accessible throughout; overlapping copies are not memmove.
void __far_memcpy(u8* dst, u8 bank, u16 src, u16 len);
// Temporarily select bank, read one byte at addr and restore the prior bank mapping.
u8 __farpeek8(u8 bank, u16 addr);
// Temporarily select bank and read a little-endian word, then restore the previous mapping.
u16 __farpeek16(u8 bank, u16 addr);

/* PPU / VRAM */
// Invoke the compiler rendering-enable helper; this header contains declarations, not C fallback implementations.
void __ppu_on(void);
// Invoke the compiler rendering-disable helper before bulk direct PPU updates.
void __ppu_off(void);
// Replace PPUMASK with the supplied byte; the caller chooses rendering, clipping and emphasis bits.
void __ppu_mask_set(u8 value);
// Replace PPUCTRL, including NMI enable, pattern-table choices and address increment mode.
void __ppu_ctrl_set(u8 value);
// Read the software PPUCTRL/PPUMASK shadows; use the setters for coherent state.
// Raw writes to $2000/$2001 bypass these shadows. No PPU hardware read occurs.
u8 __ppu_ctrl_get(void);
u8 __ppu_mask_get(void);
// Set the PPU address through the compiler helper; address writes affect the shared scroll/address latch.
void __ppu_addr(u16 ppu_addr);
// Write one PPUDATA byte using the current PPU address and increment mode.
void __ppu_data(u8 value);
// Read PPUSTATUS; the hardware clears VBlank status and resets the shared write latch.
u8 __ppu_read_status(void);
// Read PPUSTATUS to reset the scroll/address write latch; the read also clears VBlank status.
void __scroll_latch_reset(void);
// Save X/Y in runtime shadows, reset the PPU latch and write both scroll bytes immediately.
void __scroll_set(u8 x, u8 y);
// Replace saved X and write an X/Y pair using the retained Y shadow.
void __scroll_x_set(u8 x);
// Replace saved Y and write an X/Y pair using the retained X shadow.
void __scroll_y_set(u8 y);
// Write a byte-counted source range directly; arrange PPU access timing and source-bank visibility.
void __vram_write(u16 ppu_addr, const u8* src, u8 len);
// Write repeated bytes directly; this is not an enqueue operation.
void __vram_fill(u16 ppu_addr, u8 value, u8 len);
// Write one tile directly at 2000+y*32+x; use x<32, y<30 for visible nametable cells.
void __nametable_put(u8 x, u8 y, u8 tile);
// Write one tile in nametable nt&3. Mirroring can alias logical nametables; coordinates are unchecked.
void __nametable_put_nt(u8 nt, u8 x, u8 y, u8 tile);
// Fill rows of nametable zero directly, advancing each row start by 32 bytes.
// Keep the rectangle in bounds and use PPU increment one; this does not enqueue or clip.
void __nametable_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
// Fill a rectangle in nametable nt&3 with the same unchecked, direct-write row loop.
void __nametable_rect_nt(u8 nt, u8 x, u8 y, u8 w, u8 h, u8 tile);
// Write the entire packed attribute byte at 23C0+(y>>2)*8+(x>>2).
// Use tile coordinates below 32; this does not merge a single palette quadrant.
void __attr_set(u8 x, u8 y, u8 attr);
// Write a whole packed attribute byte in nametable nt&3; other quadrant fields are overwritten.
void __attr_set_nt(u8 nt, u8 x, u8 y, u8 attr);
// Stream exactly 16 source bytes to 3F00. Arrange PPU access timing and increment one.
void __palette_bg_load(const u8* pal16);
// Stream exactly 16 source bytes to 3F10; hardware palette aliases also affect background entries.
void __palette_sp_load(const u8* pal16);

/* NMI VRAM transfer queue. Queue entries are consumed by __vramq_exec(),
 * and the default NMI handler calls __vramq_exec() automatically.
 */
// Reset pending intrinsic-queue records; this is separate from runtime.c nes_vram_queue_clear.
void __vramq_clear(void);
// Append one address/value update; use the overflow latch to detect capacity failure.
void __vramq_put(u16 ppu_addr, u8 value);
// Retain the source address for later consumption; keep its storage and bank mapping valid.
void __vramq_copy(u16 ppu_addr, const u8* src, u8 len);
// Append a repeated-byte update; the source value is stored in the record.
void __vramq_fill(u16 ppu_addr, u8 value, u8 len);
// Publish the pending records to the configured consumer. Do not mutate them while consumption can run.
void __vramq_commit(void);
// Execute a ready queue directly; committing and safe PPU timing are caller responsibilities.
void __vramq_exec(void);
// Return encoded bytes currently occupied, not transfer payload bytes or command count.
u8 __vramq_len(void);
// Read the latched capacity failure; reading does not reset it.
u8 __vramq_overflow(void);
// Clear the overflow latch explicitly after handling the failed submission.
void __vramq_clear_overflow(void);
// Return total encoded command-buffer capacity; this is independent of PPU VRAM capacity.
u8 __vramq_capacity(void);

/* NMI / IRQ */
// Spin until the runtime NMI counter changes; the configured handler must advance it. No timeout is emitted.
void __nmi_wait(void);
// Return the current wrapping NMI counter byte, not a Boolean ready flag.
u8 __nmi_ready(void);
// Set bit 7 in the PPUCTRL shadow and write it to hardware, preserving the other shadow bits.
void __nmi_enable(void);
// Clear bit 7 in the PPUCTRL shadow and write it to hardware; CPU IRQ masking is separate.
void __nmi_disable(void);
// Emit SEI to mask CPU IRQs; NMI is unaffected.
void __irq_disable(void);
// Emit CLI to unmask CPU IRQs; the caller must have a valid interrupt handler.
void __irq_enable(void);
// Return the processor-status byte captured before SEI. Pass that byte to __irq_restore.
u8 __irq_save(void);
// Restore processor status with PLP, including arithmetic flags and the saved IRQ mask.
void __irq_restore(u8 state);

/* OAM / input */
// Set OAMADDR to zero and start DMA from CPU page 02; arrange safe PPU timing in the caller.
void __oam_dma(void);
// Set OAMADDR to zero and DMA 256 bytes from page<<8. The source page must remain readable.
void __oam_dma_page(u8 page);
// Hide all 64 page-02 shadow sprites by writing Y=F0; hardware OAM changes only after DMA.
void __oam_clear(void);
// Write Y, tile, attributes and X to one shadow entry. Use indices 0..63; coordinates are raw OAM bytes.
void __sprite_set(u8 index, u8 x, u8 y, u8 tile, u8 attr);
// Replace only shadow X/Y for index 0..63; tile and attribute bytes remain unchanged.
void __sprite_move(u8 index, u8 x, u8 y);
// Replace the selected shadow tile byte; this does not upload tile patterns.
void __sprite_tile(u8 index, u8 tile);
// Replace the selected shadow attribute byte, including palette, priority and flip bits.
void __sprite_attr(u8 index, u8 attr);
// Write F0 to the selected shadow Y byte; issue DMA to make the change visible.
void __sprite_hide(u8 index);
// Expand dx,dy,tile,attr records into page-02 shadow OAM until dx=FF; return the next index.
// Coordinates and the byte OAM cursor wrap; keep the terminated stream within the available slots.
u8 __metasprite_draw(u8 oam_index, u8 base_x, u8 base_y, const u8* metasprite);
// Latch and sample eight D0 bits from port 1; bit order is A,B,Select,Start,Up,Down,Left,Right.
u8 __pad_read1(void);
// Latch and sample eight D0 bits from port 2 in the same NES button order.
u8 __pad_read2(void);
// Repeat pairs of port-1 polls until both bytes match; the retry loop has no fixed limit.
u8 __pad_read1_safe(void);
// Repeat pairs of port-2 polls until both bytes match; this does not populate pad.c globals.
u8 __pad_read2_safe(void);
// Return pad & 0F for A/B/Select/Start; this masks a supplied snapshot and performs no polling.
u8 __pad_buttons(u8 pad);
// Return pad & F0 in the original bit positions; this does not normalize directions to bits 0..3.
u8 __pad_dirs(u8 pad);

/* Famicom/NES expansion input devices.
 * D1 readers are for expansion-port controllers/joysticks that expose a
 * standard 8-bit joypad stream on data line D1 instead of D0.
 * __joypad2p_voice() / __mic_read2p() return the Famicom 2P microphone bit.
 */
// Latch and read an eight-button stream from port-1 data line D1 rather than D0.
u8 __pad_read1_d1(void);
// Latch and read an eight-button stream from port-2 data line D1 rather than D0.
u8 __pad_read2_d1(void);
// Alias the port-1 D1 controller reader; the attached device must use the expected serial protocol.
u8 __exp_pad_read1(void);
// Alias the port-2 D1 controller reader; this is not an expansion-device discovery operation.
u8 __exp_pad_read2(void);
// Return controller-port 4016 bit 2 normalized to 0 or 1; this is a microphone level bit, not PCM audio.
u8 __joypad2p_voice(void);
// Alias the same normalized microphone input bit as __joypad2p_voice.
u8 __mic_read2p(void);

/* Zapper / light gun. The no-suffix names default to the usual port-2/Famicom
 * expansion-port mapping. __zapper_light*() returns 1 when light is detected.
 */
// Return raw 4016 & 18, retaining trigger and active-low light bits.
u8 __zapper_raw1(void);
// Return raw 4017 & 18 for the second port.
u8 __zapper_raw2(void);
// Return port-1 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger1(void);
// Return port-2 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger2(void);
// Invert port-1 bit 3 and normalize: one means light detected.
u8 __zapper_light1(void);
// Invert port-2 bit 3 and normalize: one means light detected.
u8 __zapper_light2(void);
// Default to the port-2 normalized trigger reader.
u8 __zapper_trigger(void);
// Default to the port-2 normalized light reader.
u8 __zapper_light(void);

/* Family BASIC Keyboard. __fkb_scan() writes 18 bytes: 9 rows x 2 columns,
 * each byte containing raw $4017 & 0x1E for that row/column.
 */
// Probe selected-row and disabled-matrix bit patterns and return 0 or 1; this is a heuristic detection.
u8 __fkb_detect(void);
// Write 18 raw masked samples (4017 & 1E), two per row, to caller storage; reset 4016 afterward.
void __fkb_scan(u8* dst18);
// Select a matrix row and the low bit of col, then return raw 4017 & 1E.
// Use rows 0..8 for keys; row 9 is used by detection. No row-range validation is emitted.
u8 __fkb_read_row_col(u8 row, u8 col);

/* R.O.B. / Famicom Robot optical flash primitives. These are timing primitives;
 * game-specific command sequences should be built on top of __rob_pulse() or
 * __rob_send_byte().
 */
// Set PPUMASK and its shadow to 1E or 00, then wait for an NMI-counter change.
// The caller supplies image/palette brightness; rendering enable alone does not create a white screen.
void __rob_flash(u8 bright);
// Run on_frames enabled-mask waits followed by off_frames disabled-mask waits; NMI must advance.
void __rob_pulse(u8 on_frames, u8 off_frames);
// Send eight bits MSB first: one uses 4 on/2 off waits; zero uses 2 on/4 off.
// Requires an advancing runtime NMI counter; 48 waits in total, final PPUMASK=0.
// This raw pattern does not add a robot command preamble or validate reception.
void __rob_send_byte(u8 pattern);

/* Expansion-port TTL serial / MIDI adapter support.
 * The low-level serial layer assumes an external adapter that maps OUT0 to TX
 * and D4 on $4017 to RX. MIDI helpers bit-bang 8N1 bytes at an approximate
 * 31250 bps timing suitable for adapter bring-up and simple music sync.
 */
// Write bit & 1 to 4016, replacing the port output value; coordinate with other port users.
void __serial_tx_bit(u8 bit);
// Read 4017 bit 4 and normalize it to 0 or 1; an external serial adapter supplies the signal.
u8 __serial_rx_bit(void);
// Send start-low, eight least-significant-first bits and stop-high in 57-cycle
// NTSC bit cells (about 31400 bps). Stop DMC DMA and mask IRQ/NMI during transfers;
// PAL timing and electrical adapter compatibility are not established here.
void __midi_out_byte(u8 byte);
// Block for a low start signal and sample eight bits. No timeout, framing check or receive buffer is provided.
u8 __midi_in_byte(void);
// Send 90|(ch&0F), note and velocity. Supply 7-bit data bytes; they are not masked by this helper.
void __midi_note_on(u8 ch, u8 note, u8 velocity);
// Send 80|(ch&0F), note and velocity through the same synchronous serial path.
void __midi_note_off(u8 ch, u8 note, u8 velocity);
// Send B0|(ch&0F), controller and value; both data arguments must be valid 7-bit values.
void __midi_control_change(u8 ch, u8 cc, u8 value);
// Send C0|(ch&0F) followed by the caller-supplied 7-bit program value.
void __midi_program_change(u8 ch, u8 program);
// Transmit the single realtime byte F8 synchronously; this does not schedule future ticks.
void __midi_clock(void);
// Transmit realtime Start (FA) through the serial adapter path.
void __midi_start(void);
// Transmit realtime Continue (FB) through the serial adapter path.
void __midi_continue(void);
// Transmit realtime Stop (FC) through the serial adapter path.
void __midi_stop(void);

/* Mapper */
// Return the low byte of the mapper number selected for this build; no cartridge probe is performed.
u8 __mapper_id(void);
// Select a CNROM bank or MMC3 register 0 bank; unsupported mapper profiles produce a compile error.
void __chr_bank_set(u8 bank);
// Use the same CNROM/MMC3 register 0 selection as __chr_bank_set.
void __chr_bank_set0(u8 bank);
// Select MMC3 register 1, or the same full CNROM bank register; this is mapper-specific banking.
void __chr_bank_set1(u8 bank);
// Emit the selected mapper mirroring operation; available modes and writable control depend on the board.
void __mirroring_set(u8 mode);
// Write the MMC3 IRQ latch and reload registers. This does not enable IRQ delivery.
void __irq_scanline_set(u8 line);
// Alias the MMC3 IRQ latch/reload operation; other mapper profiles are rejected.
void __mapper_irq_set(u8 scanline);
// Write MMC3 E001. The CPU IRQ mask and a valid handler are separate caller responsibilities.
void __mapper_irq_enable(void);
// Write MMC3 E000 to disable and acknowledge mapper IRQs.
void __mapper_irq_disable(void);
// Use the same E000 write as disable; explicitly re-enable when another interrupt is needed.
void __mapper_irq_ack(void);
// Wait for the current hit flag to clear, then for a new hit. No timeout is provided.
void __sprite0_wait_hit(void);
// Wait for a new sprite-zero hit, reset the PPU latch and write X/Y scroll; requires a visible hit setup.
void __split_scroll_sprite0(u8 x, u8 y);

/* FDS. Status and sound primitives are emitted on --mapper=fds.
 * File/overlay APIs use the FDS BIOS direct-pointer routines. __fds_load_file()
 * and __fds_load_overlay() load to the destination encoded in the disk file
 * header. __fds_file_exists() and __fds_file_size() are side-effect-free when
 * a disk-builder manifest is supplied with --fds-meta / --fds-manifest.
 */
// Return the compile-time FDS mapper selection as 0 or 1; this is not a hardware probe.
u8 __fds_available(void);
// Return the inverted disk-absent bit from 4032 as 0 or 1; this checks presence, not transfer completion.
u8 __fds_disk_ready(void);
// Return raw status mask 4032 & 04, either 0 or 4. This is not a numbered disk-side identifier.
u8 __fds_side(void);
// Read the entire 4030 status register; this is not a stored BIOS error code.
u8 __fds_error(void);
// Poll disk presence until the absent bit clears. There is no timeout or cancellation path.
void __fds_wait_ready(void);
// Alias the same disk-presence wait as __fds_wait_ready; the call can block indefinitely.
void __fds_wait_insert(void);
// Native FDS32: load a non-boot file at its disk-header address, then memmove
// PRG bytes to dst. Null dst keeps the native destination (also for PPU files).
// Both CPU spans must fit $0200-$07FF or $6000-$DFFF and be owned by the caller.
// Return 0, a BIOS error, $40 for a missing transfer, or $FF for invalid input.
// Call from common code with disk-I/O interrupt/display prerequisites satisfied.
u8 __fds_load_file(u8 id, u8* dst);
// Overwrite the final non-boot PRG slot on its side, preserving its ID/name/load
// address. len must equal its packaged size; src must be a valid CPU RAM span.
// The resolved physical ordinal selects the slot, independently of file ID.
// Return 0, a BIOS error, or $FF before any write for an invalid slot/span/size.
u8 __fds_save_file(u8 id, const u8* src, u16 len);
// Load disk file id through BIOS, return its error byte and mark bank residency FF (unknown).
u8 __fds_load_overlay(u8 id);
// Load logical bank 1 from its separate boot file, or bank 2+ from overlay-start-id+(bank-2).
// Return zero on success, FF for invalid/unavailable mappings, or the BIOS error.
// A failed BIOS transfer marks residency FF (unknown); a matching valid guard can skip loading.
u8 __fds_load_bank(u8 bank);
// Return zero when the software residency record matches, otherwise call __fds_load_bank.
u8 __fds_require_bank(u8 bank);
// Return the software resident-bank byte, or FF after a failed bank transfer.
// This is not a checksum of overlay memory.
u8 __fds_current_bank(void);
// Compare a valid bank with the software resident-bank byte; FF (unknown) never matches.
u8 __fds_is_bank_resident(u8 bank);
// Return the linker-generated overlay-function count; zero if the table is disabled.
u8 __fds_overlay_function_count(void);
// Evaluate bank once and call the named zero-argument function using its linked bank.
// Restore the caller window before returning; restoration failure stops in common code.
u8 __fds_overlay_farcall(u8 bank, u16 func);
// Same named, zero-argument dispatch as __fds_overlay_farcall; evaluate bank once.
u8 __fds_farcall(u8 bank, u16 func);
// Query the compiled --fds-meta/--fds-manifest table, not the inserted disk; return 0 or 1.
u8 __fds_file_exists(u8 id);
// Return the compiled metadata size, or zero if id is absent. No disk directory is read.
u16 __fds_file_size(u8 id);
// Write 83 to 4023 and zero to 4089/408A to initialize disk/sound register gates.
void __fds_sound_enable(void);
// Copy exactly 64 readable bytes to wave RAM with wave writes enabled, then set 4089 to zero.
void __fds_wave_load(const u8* wave);
// Stream 32 readable bytes to 4088; the caller prepares modulation state and write conditions.
void __fds_mod_load(const u8* mod);
// Write a 12-bit frequency to 4082/4083; upper control bits in 4083 are cleared.
void __fds_freq_set(u16 freq);
// Write (volume & 3F) | 80 to 4080 for direct volume control; masking wraps, rather than clamps.
void __fds_volume_set(u8 volume);
// Write the three raw arguments to 4080, 4084 and 408A in that order.
// The legacy parameter names do not describe three independently packed envelope fields.
void __fds_env_set(u8 a, u8 b, u8 c);

#endif

kitaqfc/lib/intrinsics.h

keyboard.h — 키보드 스캔
c
#ifndef KEYBOARD_H
#define KEYBOARD_H
#include "intrinsics.h"
// Probe selected-row and disabled-matrix bit patterns and return 0 or 1; this is a heuristic detection.
u8 __fkb_detect(void);
// Write 18 raw masked samples (4017 & 1E), two per row, to caller storage; reset 4016 afterward.
void __fkb_scan(u8* dst18);
// Select a matrix row and the low bit of col, then return raw 4017 & 1E.
// Use rows 0..8 for keys; row 9 is used by detection. No row-range validation is emitted.
u8 __fkb_read_row_col(u8 row, u8 col);
#endif

kitaqfc/lib/keyboard.h

mapper.h — 매퍼 뱅크와 IRQ
c
#ifndef MAPPER_H
#define MAPPER_H

#include "intrinsics.h"

// Resolve a symbol or function placement to its byte bank number at compilation time.
u8 __bankof(u16 symbol_or_function);
// Select the mapper-specific PRG bank; the common bank and legal bank range depend on the target board.
void __bankswitch(u8 bank);
// Use the same mapper PRG-switch helper as __bankswitch; retain executable code in a valid mapping.
void __prg_bank_set(u8 bank);
// Invoke the compiler banked-call path; target placement and calling convention must match the selected mapper.
u8 __farcall(u8 bank, u16 func);
// Switch to the source bank, copy len bytes forward and restore the previous bank.
// Keep destination RAM and helper code accessible throughout; overlapping copies are not memmove.
void __far_memcpy(u8* dst, u8 bank, u16 src, u16 len);
// Temporarily select bank, read one byte at addr and restore the prior bank mapping.
u8 __farpeek8(u8 bank, u16 addr);
// Temporarily select bank and read a little-endian word, then restore the previous mapping.
u16 __farpeek16(u8 bank, u16 addr);
// Return the low byte of the mapper number selected for this build; no cartridge probe is performed.
u8 __mapper_id(void);
// Select a CNROM bank or MMC3 register 0 bank; unsupported mapper profiles produce a compile error.
void __chr_bank_set(u8 bank);
// Use the same CNROM/MMC3 register 0 selection as __chr_bank_set.
void __chr_bank_set0(u8 bank);
// Select MMC3 register 1, or the same full CNROM bank register; this is mapper-specific banking.
void __chr_bank_set1(u8 bank);
// Emit the selected mapper mirroring operation; available modes and writable control depend on the board.
void __mirroring_set(u8 mode);
// Write the MMC3 IRQ latch and reload registers. This does not enable IRQ delivery.
void __irq_scanline_set(u8 line);
// Alias the MMC3 IRQ latch/reload operation; other mapper profiles are rejected.
void __mapper_irq_set(u8 scanline);
// Write MMC3 E001. The CPU IRQ mask and a valid handler are separate caller responsibilities.
void __mapper_irq_enable(void);
// Write MMC3 E000 to disable and acknowledge mapper IRQs.
void __mapper_irq_disable(void);
// Use the same E000 write as disable; explicitly re-enable when another interrupt is needed.
void __mapper_irq_ack(void);
// Wait for the current hit flag to clear, then for a new hit. No timeout is provided.
void __sprite0_wait_hit(void);
// Wait for a new sprite-zero hit, reset the PPU latch and write X/Y scroll; requires a visible hit setup.
void __split_scroll_sprite0(u8 x, u8 y);

#endif

kitaqfc/lib/mapper.h

math_fast.h — 고속 정수 연산
c
#ifndef MATH_FAST_H
#define MATH_FAST_H

#include "intrinsics.h"

/*
 * Fast math primitives are compiler intrinsics. These declarations are kept
 * here so game code can include a game-facing math header instead of the full
 * intrinsic surface.
 */
// Return abs(x1-x2)+abs(y1-y2) modulo 256; large distances are not saturated.
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);
// Return 0 or 1 for half-open bounds. Zero extents are empty; coordinates do not wrap across 255.
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);
// Return y*width+x as a word without checking coordinates against map dimensions.
u16 __map_index(u8 x, u8 y, u8 width);
// Advance the shared 16-bit LFSR and return the XOR of its two bytes; zero state is reseeded to A55A.
u8 __rng8(void);
// Replace both persistent state bytes. Reusing a seed reproduces the sequence; this is not cryptographic randomness.
void __rng_seed(u16 seed);

#endif

kitaqfc/lib/math_fast.h

math_fixed.h — 고정소수점 내장 함수 인터페이스
c
#ifndef MATH_FIXED_H
#define MATH_FIXED_H

#include "intrinsics.h"

/* Fixed-point helpers are compiler-recognized intrinsic entry points. */
// Return the high byte of the unsigned 8-by-8 product, equivalent to floor(a*b/256).
u8 __mul8x8_hi(u8 a, u8 b);
// Return the low word of the unsigned product; overflow wraps without saturation.
u16 __mul16x8(u16 a, u8 b);
// Interpret a and b as signed two's-complement bit patterns and return the low word of their product.
u16 __smul16x8(u16 a, u8 b);
// Return acc+a*b modulo 65536 using unsigned multiplication.
u16 __mac16(u16 acc, u16 a, u8 b);
// Add the signed-pattern product to acc modulo 65536; the API carries the result as u16 bits.
u16 __smac16(u16 acc, u16 a, u8 b);
// Return a0*b0+a1*b1 modulo 65536. The FC backend inserts no Q8.8 rescaling or rounding.
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three unsigned word-by-byte products modulo 65536 without a final scaling shift.
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);
// Sum two signed-pattern products modulo 65536; no Q8.8 scaling shift is generated.
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);
// Sum three signed-pattern products modulo 65536; choose operand units explicitly in the caller.
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

#endif

kitaqfc/lib/math_fixed.h

math_lut.h — 계산용 참조 테이블
c
#ifndef MATH_LUT_H
#define MATH_LUT_H

// Uniform 256-phase unsigned sine table in ROM. Range 1..255, centered at 128.
// Link math_lut.c once; indices 0..255 are valid and byte phase wrap is supported.
extern unsigned char MATH_SIN[256];

#endif

kitaqfc/lib/math_lut.h

metasprite.h — 여러 스프라이트로 구성한 이미지
c
/*
 * Optional declarations for the phase 10 NES metasprite helper.
 *
 * Metasprite data layout:
 *   [dx, dy, tile, attr] repeated, terminated by dx=0xFF
 */

// Read four-byte records [dx,dy,tile,attr] until dx=0xFF, writing them to
// shadow OAM. Coordinates and destination offsets wrap as bytes. The caller
// must supply a terminator and enough unused slots; there is no capacity guard.
// Return the next slot index derived from the final wrapped byte offset.
unsigned char nes_metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, unsigned char* metasprite);
// Compiler intrinsic counterpart; this declaration does not route through the C function above.
unsigned char __metasprite_draw(unsigned char oam_index, unsigned char base_x, unsigned char base_y, const unsigned char* metasprite);
// Hide consecutive shadow slots by setting Y to 0xF8. Keep the requested
// range within 64 slots to avoid wrapping and hiding earlier entries.
void nes_metasprite_hide_from(unsigned char oam_index, unsigned char sprite_count);

kitaqfc/lib/metasprite.h

mic.h — 마이크 입력
c
#ifndef MIC_H
#define MIC_H
#define VOICE_H
#include "intrinsics.h"
// Return controller-port 4016 bit 2 normalized to 0 or 1; this is a microphone level bit, not PCM audio.
u8 __joypad2p_voice(void);
// Alias the same normalized microphone input bit as __joypad2p_voice.
u8 __mic_read2p(void);
#endif

kitaqfc/lib/mic.h

midi.h — MIDI 입출력 인터페이스
c
#ifndef MIDI_H
#define MIDI_H
#include "intrinsics.h"
// Write bit & 1 to 4016, replacing the port output value; coordinate with other port users.
void __serial_tx_bit(u8 bit);
// Read 4017 bit 4 and normalize it to 0 or 1; an external serial adapter supplies the signal.
u8 __serial_rx_bit(void);
// Send start-low, eight least-significant-first bits and stop-high using fixed NOP delays.
// Instruction and interrupt overhead is additional; the emitted loop does not establish a verified MIDI baud rate.
void __midi_out_byte(u8 byte);
// Block for a low start signal and sample eight bits. No timeout, framing check or receive buffer is provided.
u8 __midi_in_byte(void);
// Send 90|(ch&0F), note and velocity. Supply 7-bit data bytes; they are not masked by this helper.
void __midi_note_on(u8 ch, u8 note, u8 velocity);
// Send 80|(ch&0F), note and velocity through the same synchronous serial path.
void __midi_note_off(u8 ch, u8 note, u8 velocity);
// Send B0|(ch&0F), controller and value; both data arguments must be valid 7-bit values.
void __midi_control_change(u8 ch, u8 cc, u8 value);
// Send C0|(ch&0F) followed by the caller-supplied 7-bit program value.
void __midi_program_change(u8 ch, u8 program);
// Transmit the single realtime byte F8 synchronously; this does not schedule future ticks.
void __midi_clock(void);
// Transmit realtime Start (FA) through the serial adapter path.
void __midi_start(void);
// Transmit realtime Continue (FB) through the serial adapter path.
void __midi_continue(void);
// Transmit realtime Stop (FC) through the serial adapter path.
void __midi_stop(void);
#endif

kitaqfc/lib/midi.h

nametable_asset.h — 네임테이블 리소스와 배치
c
/*
 * Optional declarations for phase12 nametable / attribute asset helpers.
 */

extern unsigned char nes_attr_shadow[64];

// Mask the index to the four logical nametables and return its PPU base.
// Physical storage still depends on the cartridge's mirroring configuration.
unsigned short nes_nt_base_from_index(unsigned char index);
// Locate the 64-byte attribute table at offset 0x03C0 from a nametable base.
unsigned short nes_attr_base_from_nt(unsigned short nt_base);
// Fill all 64 shadow bytes with an already-packed attribute value.
void nes_attr_shadow_clear(unsigned char value);
// Copy an entire packed attribute table into shadow RAM without writing the PPU.
void nes_attr_shadow_copy(unsigned char* src64);
// Replace only the chosen quadrant's palette bits, preserving its neighbors.
// Palette IDs are masked to two bits; tile coordinates must fit the shadow table.
void nes_attr_shadow_set_quad(unsigned char tile_x, unsigned char tile_y, unsigned char pal_index);
// Upload all attribute shadow bytes immediately; the caller provides safe PPU timing.
void nes_attr_apply_now(unsigned short nt_base);
// Copy all shadow attribute bytes into the runtime queue and return its admission result.
unsigned char nes_attr_queue_all(unsigned short nt_base);
// Write one complete 32-tile row directly. The caller validates the row and PPU timing.
void nes_nt_stream_row(unsigned short nt_base, unsigned char row, unsigned char* src32);
// Copy one complete nametable row into the runtime queue.
unsigned char nes_nt_queue_row(unsigned short nt_base, unsigned char row, unsigned char* src32);
// Write each rectangle row immediately, advancing source by pitch rather than
// width. Caller-provided coordinates, storage and timing are not clipped or checked.
void nes_nt_stream_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);
// Copy rows into the runtime queue until complete or full. Failure leaves
// earlier rows queued; it does not roll back a partially admitted rectangle.
// Widths must respect the queue literal-record limit.
// Pitch is source bytes per row. Each copied literal record costs width+3 queue bytes;
// check total admission needs when partial updates are unacceptable.
unsigned char nes_nt_queue_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char pitch, unsigned char* src);
// Queue one fill record per row, stopping on failure while retaining previously
// queued rows. The runtime fill-length and rectangle bounds requirements apply.
unsigned char nes_nt_queue_fill_rect(unsigned short nt_base, unsigned char tile_x, unsigned char tile_y, unsigned char width, unsigned char height, unsigned char value);
// Upload 960 tile bytes followed by 64 attribute bytes. This bulk operation
// does not disable rendering or wait for VBlank on its own.
void nes_nametable_apply_now(unsigned short nt_base, unsigned char* nam960, unsigned char* attr64);

kitaqfc/lib/nametable_asset.h

nes_game.h — 게임용 연산 매크로
c
#ifndef NES_GAME_H
#define NES_GAME_H

#include "core.h"
#include "intrinsics.h"

/*
 * AI-friendly NES operation aliases.
 *
 * These names intentionally describe gameplay-side operations.  They are thin
 * aliases over KITAQFC intrinsics so the compiler can still record the exact
 * NES action for diagnostics and KUROSAKI metadata.
 */
// These aliases use the compiler intrinsic queue. They do not select the separate C runtime queue.
#define nes_vram_put(addr, value)          __vramq_put((addr), (value))
#define nes_vram_copy(addr, src, len)      __vramq_copy((addr), (src), (len))
#define nes_vram_fill(addr, value, len)    __vramq_fill((addr), (value), (len))
#define nes_vram_commit()                  __vramq_commit()
#define nes_vram_clear_queue()             __vramq_clear()

#define nes_oam_clear()                    __oam_clear()
// This zero-argument macro hides the same-named page-argument C runtime function in included code.
#define nes_oam_dma()                      __oam_dma()
#define nes_sprite_set(i,x,y,t,a)          __sprite_set((i),(x),(y),(t),(a))
#define nes_sprite_move(i,x,y)             __sprite_move((i),(x),(y))
#define nes_sprite_hide(i)                 __sprite_hide((i))
// Select the compiler intrinsic implementation even when the C metasprite header is also included.
#define nes_metasprite_draw(i,x,y,data)    __metasprite_draw((i),(x),(y),(data))

#define nes_pad1()                         __pad_read1_safe()
#define nes_pad2()                         __pad_read2_safe()

#define nes_mapper_irq_set(scanline)       __mapper_irq_set((scanline))
#define nes_mapper_irq_enable()            __mapper_irq_enable()
#define nes_mapper_irq_disable()           __mapper_irq_disable()
#define nes_mapper_irq_ack()               __mapper_irq_ack()
#define nes_split_scroll_sprite0(x,y)      __split_scroll_sprite0((x),(y))

#define nes_fds_wave_load(wave64)          __fds_wave_load((wave64))
#define nes_fds_load_bank(bank)            __fds_load_bank((bank))
#define nes_fds_farcall(bank, func)        __fds_overlay_farcall((bank), (func))

#endif

kitaqfc/lib/nes_game.h

oam.h — OAM 섀도 조작
c
#ifndef OAM_H
#define OAM_H

#include "intrinsics.h"

// Set OAMADDR to zero and start DMA from CPU page 02; arrange safe PPU timing in the caller.
void __oam_dma(void);
// Set OAMADDR to zero and DMA 256 bytes from page<<8. The source page must remain readable.
void __oam_dma_page(u8 page);
// Hide all 64 page-02 shadow sprites by writing Y=F0; hardware OAM changes only after DMA.
void __oam_clear(void);
// Write Y, tile, attributes and X to one shadow entry. Use indices 0..63; coordinates are raw OAM bytes.
void __sprite_set(u8 index, u8 x, u8 y, u8 tile, u8 attr);
// Replace only shadow X/Y for index 0..63; tile and attribute bytes remain unchanged.
void __sprite_move(u8 index, u8 x, u8 y);
// Replace the selected shadow tile byte; this does not upload tile patterns.
void __sprite_tile(u8 index, u8 tile);
// Replace the selected shadow attribute byte, including palette, priority and flip bits.
void __sprite_attr(u8 index, u8 attr);
// Write F0 to the selected shadow Y byte; issue DMA to make the change visible.
void __sprite_hide(u8 index);

#endif

kitaqfc/lib/oam.h

oam_fair.h — 우선순위를 유지하는 스프라이트 표시 순환
c
#ifndef KQ_OAM_FAIR_H
#define KQ_OAM_FAIR_H
#include "core.h"
/* 64-entry software sprite pool. Define the five arrays/cursor declared below
 * and include oam_fair_impl.h in ONE translation unit, in a fixed PRG bank.
 * X/Y are center coordinates, ACTIVE is a byte array. OAM is a 256-byte
 * DMA shadow; USED is its byte cursor. Entries already in OAM stay first.
 * The odd stride selects every starting entry over 64 calls. Hardware still
 * has a 64-sprite / 8-sprite-per-scanline limit: this distributes flicker.
 * Call only from the main loop; commit the completed OAM in VBlank.
 */
extern u8 oam_fair_phase;
extern u8 oam_fair_x[64];
extern u8 oam_fair_y[64];
extern u8 oam_fair_active[64];
extern u8 oam_fair_shadow[256];
extern u8 oam_fair_used;
extern u8 oam_fair_limit;
extern u8 oam_fair_tile;
extern u8 oam_fair_attr;
extern u8 oam_fair_drawn;
// Append active pool entries using the configured common tile/attributes. The emitter stops at byte cursor 252,
// leaving slot 63 unused to avoid cursor wrap. Hide unused trailing OAM entries separately before DMA.
void OAM_FairDraw(void);
#endif

kitaqfc/lib/oam_fair.h

oam_fair_impl.h — 스프라이트 표시 순환 구현
c
#include "oam_fair.h"
u8 oam_fair_phase;
u8 oam_fair_limit;
u8 oam_fair_tile;
u8 oam_fair_attr;
u8 oam_fair_drawn;
u8 oam_fair_scanned;
// Rotate the starting pool entry by 13 modulo 64, then append active sprites
// until the caller's draw limit or OAM byte cursor limit is reached. Coordinates
// describe 8x8 centers: subtract four for X and five for raw OAM Y. Include once
// and invoke it from the main loop with a four-byte-aligned used cursor.
void OAM_FairDraw(void) {
    __asm {
        LDA oam_fair_phase
        CLC
        ADC #13
        AND #63
        STA oam_fair_phase
        TAX
        LDY oam_fair_used
        LDA #0
        STA oam_fair_drawn
        STA oam_fair_scanned
    // Visit each pool entry at most once, preserving already-written OAM entries
    // and sharing the configured tile/attribute values across emitted sprites.
    kq_fair_loop:
        LDA oam_fair_active,X
        BEQ kq_fair_next
        LDA oam_fair_drawn
        CMP oam_fair_limit
        BCS kq_fair_done
        // Reserve the final four-byte slot so advancing this byte cursor cannot wrap to zero.
        CPY #252
        BCS kq_fair_done
        // Convert the 8x8 center to raw OAM coordinates; Y also needs the hardware minus-one bias.
        LDA oam_fair_y,X
        SEC
        SBC #5
        STA oam_fair_shadow,Y
        LDA oam_fair_tile
        STA oam_fair_shadow+1,Y
        LDA oam_fair_attr
        STA oam_fair_shadow+2,Y
        LDA oam_fair_x,X
        SEC
        SBC #4
        STA oam_fair_shadow+3,Y
        INY
        INY
        INY
        INY
        INC oam_fair_drawn
    // Advance cyclically and stop after returning to this frame's starting entry.
    kq_fair_next:
        TXA
        CLC
        ADC #1
        AND #63
        TAX
        CPX oam_fair_phase
        BNE kq_fair_loop
    // Publish the updated byte cursor for later OAM writers and the VBlank DMA path.
    kq_fair_done:
        STY oam_fair_used
    }
}

kitaqfc/lib/oam_fair_impl.h

pad.h — NES 컨트롤러 입력
c
/*
 * Optional declarations for the phase 8 NES pad runtime.
 *
 * Button bit layout in nes_pad1_cur / pressed / released:
 *   bit0=A bit1=B bit2=Select bit3=Start bit4=Up bit5=Down bit6=Left bit7=Right
 */

extern unsigned char nes_pad1_cur;
extern unsigned char nes_pad1_prev;
extern unsigned char nes_pad1_pressed;
extern unsigned char nes_pad1_released;

// Latch controller input with a 1-to-0 strobe and shift eight controller-1 bits
// into held/pressed/released masks. This direct path does not perform repeated
// reads for DMC interference; the final JOY2 read does not populate player-2 state.
void nes_pad_poll(void);

kitaqfc/lib/pad.h

palette.h — NES 팔레트
c
/*
 * Optional declarations for the phase 11 NES palette helper.
 */

extern unsigned char nes_palette_shadow[32];

// Copy exactly 32 palette bytes to the fixed shadow buffer without writing the PPU.
void nes_palette_copy(unsigned char* src32);
// Replace shadow palette data and upload all 32 bytes immediately. The caller
// provides a safe PPU access period; this helper does not wait for VBlank.
void nes_palette_apply_now(unsigned char* src32);
// Upload the current palette shadow directly, with timing controlled by the caller.
void nes_palette_apply_shadow_now(void);
// Update the shadow and copy all colors into the runtime queue. A queue
// failure leaves the new shadow contents in place.
// Return one if the runtime queue accepted the copied payload, zero if it was full.
// Admission does not mean the NMI consumer has already updated the PPU.
unsigned char nes_palette_queue_all(unsigned char* src32);
// Update and queue four background palette bytes. pal_index must be 0..3;
// there is no range check before writing the shadow.
unsigned char nes_palette_queue_bg4(unsigned char pal_index, unsigned char* src4);
// Update and queue four sprite palette bytes at the 0x3F10 region. The
// caller supplies pal_index 0..3; NES palette mirroring still applies.
unsigned char nes_palette_queue_sprite4(unsigned char pal_index, unsigned char* src4);

kitaqfc/lib/palette.h

physics2d.h — 2D 이동과 충돌
c
#pragma once

// Position and velocity use caller-selected integer units with one integration
// step per call; no hidden pixel or time conversion occurs. Keep intermediate
// signed arithmetic in range. World/body storage is owned by the caller.
#include "fixed.h"


// Lightweight deterministic 2D physics for KITAQFC.
// Integer pixels per step are one possible unit convention; inv_mass is Q8.

typedef struct {
    s16 x;
    s16 y;
    s16 vx;
    s16 vy;
    s16 half_x;
    s16 half_y;
    s16 inv_mass_q8;      // 0 means static body.
    s16 restitution_q8;   // 0..255 (reserved for future tuning).
    u8 active;
} KQBody2D;

typedef struct {
    KQBody2D* bodies;
    u8 body_count;
    s16 gravity_x;
    s16 gravity_y;
    u8 solver_iterations;
    s16 max_speed;
} KQWorld2D;

// Retain the caller-owned body array without initializing its elements.
// Set gravity to (0,32), four contact iterations and a per-axis speed limit of 512.
// A null world is ignored; the array must outlive subsequent world operations.
void kq2d_world_init(KQWorld2D* world, KQBody2D* bodies, u8 body_count);
// Advance active dynamic bodies, then visit each active unordered pair for
// the configured contact passes (zero means one). Static bodies still participate
// as obstacles. The world borrows its array and uses non-reentrant helper scratch.
// This is discrete integration; fast bodies can pass through thin obstacles.
void kq2d_step(KQWorld2D* world);
// For one active dynamic body, add gravity, clamp each velocity axis and then
// add velocity to position. Null inputs are ignored; no contacts are solved.
// Use a nonnegative speed limit and keep additions in signed 16-bit range.
void kq2d_integrate_body(KQWorld2D* world, KQBody2D* body);
// Test strict overlap of two centered boxes; touching edges return false.
// Both pointers must be valid. Active/mass flags are ignored, and coordinate
// differences and extent sums must fit the signed 16-bit calculations.
u8 kq2d_overlap_aabb(const KQBody2D* a, const KQBody2D* b);
// Initialize an active unit-inverse-mass body at the supplied center and half
// extents, with zero velocity and reserved restitution set to zero. Null is ignored.
void kq2d_body_init(KQBody2D* body, s16 x, s16 y, s16 half_x, s16 half_y);
// Replace the center without altering velocity, extents or activity. Null is ignored.
void kq2d_body_set_pos(KQBody2D* body, s16 x, s16 y);
// Replace velocity without clamping or checking activity/mass. Null is ignored.
void kq2d_body_set_velocity(KQBody2D* body, s16 vx, s16 vy);
// Add gravity and clamp each velocity axis separately for an active dynamic
// body. Null, inactive and static bodies are ignored. Supply a nonnegative limit
// and avoid overflow in the additions before clamping; position is unchanged.
void kq2d_body_apply_gravity(KQBody2D* body, s16 gravity_x, s16 gravity_y, s16 max_speed);
// Scale both velocities by a signed eight-bit coefficient divided by 256.
// Despite the unsigned parameter, values 128..255 become negative coefficients;
// this is not a full-range unsigned Q8 damping helper. Null is ignored.
void kq2d_body_apply_friction(KQBody2D* body, u8 friction_q8);
// Delegate strict rectangle overlap to the fixed-point utility; edge-only
// contact is false. Coordinate-plus-size sums must remain representable as s16.
u8 kq2d_rect_intersect(KQRect a, KQRect b);
// Delegate the half-open point test: include left/top and exclude right/bottom.
// Coordinate-plus-size sums must remain representable as s16.
u8 kq2d_point_in_rect(s16 x, s16 y, KQRect r);

// Opt-in surface solver. Velocities/threshold/kick share the caller's units.
// Normal must be unit length in Q8; coefficients are 0..255.
typedef struct {
    s16 nx_q8;
    s16 ny_q8;
    s16 vx;
    s16 vy;
    s16 bounce_threshold;
    s16 kick;
    u8 restitution_q8;
    u8 friction_q8;
} KQSurface2D;

// Truncates toward zero, including negative values; coefficient is -256..256.
s16 kq2d_scale_q8(s16 value, s16 coefficient);
// Conservative circular bound, with a shared Q8 scale for both components.
// Quantizes toward zero; very small limits relative to input may yield zero.
// Components and positive max_speed must be <= 16383 in magnitude.
void kq2d_body_limit_speed(KQBody2D* body, s16 max_speed);
// Kinematic surface response; returns incoming normal speed, or zero if separating.
// Caller resolves penetration separately. Relative components must fit +/-8191.
s16 kq2d_body_resolve_surface(KQBody2D* body, const KQSurface2D* surface);
// Linear crossing time in Q8 (0..256); positive separation is outside.
// Returns 0 for initial overlap, 256 for no crossing. Gaps must fit +/-8191.
u16 kq2d_surface_toi_q8(s16 start_gap, s16 end_gap);

// Optional byte-sized eighth-pixel channels for small custom update loops.
#define KQ2D_SUBPIXEL_BITS ((u8)3)
#define KQ2D_SUBPIXEL_ONE  ((u8)8)
#define KQ2D_SUBPIXEL_MASK ((u8)7)
// Optional byte-channel identifiers for application-specific movement.
#define KQ2D_DRAG_BRAKE    ((u8)1)
#define KQ2D_DRAG_STUN     ((u8)2)
#define KQ2D_DRAG_COAST    ((u8)3)
#define KQ2D_DRAG_THRUST   ((u8)4)
#define KQ2D_DIR_NONE      ((u8)0)
#define KQ2D_DIR_NEGATIVE  ((u8)1)
#define KQ2D_DIR_POSITIVE  ((u8)2)

/*
 * Byte-sized Q5.3 channels for KITAQFC.
 *
 * Keep the channels as flat game-state fields in a hot loop.  That avoids
 * aggregate/pointer call traffic while retaining eighth-pixel integration.
 */
// Store integer pixel position separately from the 0..7 fractional remainder.
// Speed is an unsigned eighth-pixel magnitude; direction is a separate channel.
typedef u8 KQ2DPosition;
typedef u8 KQ2DFraction;
typedef u8 KQ2DSpeed;
typedef u8 KQ2DDirection;

kitaqfc/lib/physics2d.h

physics3d.h — 3D AABB 물리
c
#pragma once
#include "core.h"

// Lightweight deterministic 3D physics for KITAQFC.
// AABB contacts, acceleration, mass-weighted bounce, and impact state flags.

// The game owns break decisions: the solver records impact speeds but never
// sets this flag or deactivates a body based on break_speed. Positions, extents,
// velocities and acceleration use consistent caller-selected per-step units.
#define KQ3D_BODY_BROKEN ((u8)0x01)

typedef struct {
    s16 x;
    s16 y;
    s16 z;
    s16 vx;
    s16 vy;
    s16 vz;
    s16 ax;
    s16 ay;
    s16 az;
    s16 half_x;
    s16 half_y;
    s16 half_z;
    s16 mass_q8;        // 256 means mass 1.0.
    s16 inv_mass_q8;    // 0 means static body.
    s16 restitution_q8; // 256 means perfectly elastic.
    s16 break_speed;    // Reserved for game-side impact thresholds.
    s16 last_impact_speed;
    u8 active;
    u8 flags;
} KQBody3D;

typedef struct {
    KQBody3D* bodies;
    u8 body_count;
    s16 gravity_x;
    s16 gravity_y;
    s16 gravity_z;
    u8 solver_iterations;
    s16 max_speed;
} KQWorld3D;

// Borrow the body array without initializing it. Set gravity (0,24,0), four
// contact passes and a per-axis speed limit of 512. Null world is ignored;
// the caller retains ownership and must keep the array alive.
void kq3d_world_init(KQWorld3D* world, KQBody3D* bodies, u8 body_count);
// Initialize an active centered box with zero velocity/acceleration,
// restitution 224, cleared impact/flags and mass set through the weight helper.
// The caller supplies valid half extents. Null body is ignored.
void kq3d_body_init(KQBody3D* body, s16 x, s16 y, s16 z, s16 half_x, s16 half_y, s16 half_z, s16 mass_q8);
// Store the requested mass; nonpositive mass makes the body static.
// For positive mass compute inverse weight as max(1,32767/max(mass,64)).
// The stored mass retains the original input; this is a relative solver weight,
// not an exact Q8 reciprocal. Null body is ignored.
void kq3d_body_set_mass(KQBody3D* body, s16 mass_q8);
// Integrate active dynamic bodies and reset their impact values, then solve
// each active unordered pair for the configured passes (zero means one).
// Static bodies retain previous impact maxima until the game clears them.
// Acceleration persists; flags and break thresholds remain game-owned.
// This discrete solver uses non-reentrant helper scratch and can miss fast crossings.
void kq3d_step(KQWorld3D* world);
// Reset impact for one active dynamic body, add gravity and persistent body
// acceleration, clamp velocity per axis and advance position. Static/inactive
// bodies retain impact state. Null inputs are ignored; no contacts are solved.
// Use a nonnegative speed limit and avoid overflow before clamping.
void kq3d_integrate_body(KQWorld3D* world, KQBody3D* body);
// Test strict overlap on all three axes; face-only contact returns false.
// Valid pointers are required and flags are ignored. Center differences and
// extent sums must fit the signed 16-bit magnitude calculations.
u8 kq3d_overlap_aabb(const KQBody3D* a, const KQBody3D* b);

// Sum three signed products, dividing each by 256 (toward zero) before addition.
// Coefficients are signed bytes scaled by 1/256; the 16-bit sum is not saturated.
s16 kq3d_dot_q8_8(s16 x, s16 y, s16 z, s8 ax, s8 ay, s8 az);

kitaqfc/lib/physics3d.h

ppu.h — PPU 화면 기능 선언
c
#ifndef PPU_H
#define PPU_H

// Link ppu.c. Bulk palette/nametable transfers require rendering to be disabled.
// Disable background and sprite rendering, preserving other PPUMASK bits.
void nes_ppu_screen_off(void);
// Set scroll to (0,0), then apply the requested PPUCTRL and PPUMASK values.
void nes_ppu_screen_on(unsigned char ctrl, unsigned char mask);
// Copy 32 bytes into $3F00..$3F1F with increment one; preserve PPUCTRL.
// Null is ignored. Hardware palette mirrors apply; source bank must stay mapped.
void nes_ppu_load_palette(unsigned char* pal32);
// Fill 960 tiles and 64 packed attributes; preserve PPUCTRL.
// Accept exactly $2000/$2400/$2800/$2C00; invalid bases are ignored.
void nes_ppu_clear_nt(unsigned short nt_base, unsigned char tile, unsigned char attr);

// Address-byte variants reset the latch and coexist with runtime.c's word seek.
void nes_ppu_seek_bytes(unsigned char hi, unsigned char lo);
// Direct transfers follow the current PPUCTRL increment; count is 0..255.
void nes_ppu_write_bytes(unsigned char hi, unsigned char lo, const unsigned char* src, unsigned char count);
void nes_ppu_fill(unsigned char hi, unsigned char lo, unsigned char value, unsigned char count);

#endif

kitaqfc/lib/ppu.h

ppu_direct.h — PPU 직접 조작
c
#ifndef PPU_DIRECT_H
#define PPU_DIRECT_H

#include "intrinsics.h"

/* Direct PPU access. Use only during vblank or when rendering is disabled. */
// Invoke the compiler rendering-enable helper; this header contains declarations, not C fallback implementations.
void __ppu_on(void);
// Invoke the compiler rendering-disable helper before bulk direct PPU updates.
void __ppu_off(void);
// Replace PPUMASK with the supplied byte; the caller chooses rendering, clipping and emphasis bits.
void __ppu_mask_set(u8 value);
// Replace PPUCTRL, including NMI enable, pattern-table choices and address increment mode.
void __ppu_ctrl_set(u8 value);
// Set the PPU address through the compiler helper; address writes affect the shared scroll/address latch.
void __ppu_addr(u16 ppu_addr);
// Write one PPUDATA byte using the current PPU address and increment mode.
void __ppu_data(u8 value);
// Read PPUSTATUS; the hardware clears VBlank status and resets the shared write latch.
u8 __ppu_read_status(void);
// Write a byte-counted source range directly; arrange PPU access timing and source-bank visibility.
void __vram_write(u16 ppu_addr, const u8* src, u8 len);
// Write repeated bytes directly; this is not an enqueue operation.
void __vram_fill(u16 ppu_addr, u8 value, u8 len);

#endif

kitaqfc/lib/ppu_direct.h

rob.h — ROB 제어
c
#ifndef ROB_H
#define ROB_H
#include "intrinsics.h"
// Set PPUMASK and its shadow to 1E or 00, then wait for an NMI-counter change.
// The caller supplies image/palette brightness; rendering enable alone does not create a white screen.
void __rob_flash(u8 bright);
// Run on_frames enabled-mask waits followed by off_frames disabled-mask waits; NMI must advance.
void __rob_pulse(u8 on_frames, u8 off_frames);
// Send eight bits MSB first, using 4/2-frame pulses for one and 2/4 for zero.
// Preserve the outer bit counter and shifted pattern around each pulse helper.
void __rob_send_byte(u8 pattern);
#endif

kitaqfc/lib/rob.h

runtime.h — NMI·OAM·PPU 전송용 C 도우미
c
/*
 * Core NES runtime declarations.
 *
 * Declare frame waits, direct PPU transfers, OAM DMA and the copied-payload VRAM queue.
 */

// The four-byte record follows hardware OAM byte order: Y, tile, attributes, X.
struct NesSprite4 {
    unsigned char y;
    unsigned char tile;
    unsigned char attr;
    unsigned char x;
};

extern unsigned char nes_nmi_counter;
extern unsigned char nes_oam_shadow[256];
// This counter measures the separate 192-byte copied-payload runtime queue, not the compiler intrinsic queue.
extern unsigned char nes_vram_queue_used;
extern unsigned char nes_vram_queue_overflow;

// Wait until the NMI handler advances its counter. NMI must already be enabled
// and able to execute; this polling loop has no timeout.
void nes_wait_nmi(void);
// Wait for the current VBlank to end, then for the next one to begin. Each
// PPUSTATUS read has hardware side effects, including clearing the VBlank flag.
void nes_vblank_wait(void);
// Reset the PPU address latch with a status read, then write the high/low address bytes.
void nes_ppu_seek(unsigned short ppu_addr);
// Write len source bytes directly through PPUDATA. Call only during an appropriate
// PPU access period; the address increment mode comes from the current PPUCTRL.
void nes_ppu_stream_write(unsigned short ppu_addr, unsigned char* src, unsigned short len);
// Write value repeatedly through PPUDATA, using the current PPU address increment
// mode. This routine does not wait for VBlank or disable rendering.
void nes_ppu_stream_fill(unsigned short ppu_addr, unsigned char value, unsigned short len);
// Reset the OAM destination and DMA one 256-byte CPU page. The hardware stalls
// the CPU during transfer; page is the source address high byte.
void nes_oam_dma(unsigned char page);

// Discard pending VRAM commands and clear the overflow latch; no PPU writes occur.
void nes_vram_queue_clear(void);
// Copy a literal payload into the queue and publish its new used length last.
// Accept lengths 0..127; reject larger lengths and latch overflow without changing the queue.
// The caller must ensure queue production cannot race its NMI consumer.
unsigned char nes_vram_queue_try_write(unsigned short ppu_addr, unsigned char* src, unsigned char len);
// Queue a four-byte fill record: address high/low, length with bit 7 set, value.
// Reject lengths above 127 and latch overflow on any capacity/format failure.
unsigned char nes_vram_queue_try_fill(unsigned short ppu_addr, unsigned char value, unsigned char len);
// Consume complete records in order during a safe PPU access period. Reading
// PPUSTATUS resets the shared address latch before each address pair. The caller
// is responsible for ensuring the transfer fits its NMI/VBlank time budget.
void nes_vram_queue_nmi_flush(void);

kitaqfc/lib/runtime.h

scene.h — 장면 등록, 전환, 갱신
c
#ifndef SCENE_H
#define SCENE_H

#include "core.h"

typedef void (*SceneFunc)(void);

typedef __packed struct SceneDef {
    SceneFunc enter;
    SceneFunc update;
    SceneFunc draw;
    SceneFunc exit;
} SceneDef;

// One shared in-flight transfer. remaining counts source bytes not yet copied into the queue.
// An inactive stream may still have commands awaiting the NMI consumer.
struct NesSceneStreamState {
    unsigned char* src;
    unsigned short ppu_addr;
    unsigned short remaining;
    unsigned char chunk;
    unsigned char active;
};

extern struct NesSceneStreamState nes_scene_stream_state;

// Attach the caller-owned scene table and clear current-scene state without invoking callbacks.
void scene_init(const SceneDef* scenes, u8 count);
// Replace the table by resetting scene state; this does not call the old scene's exit handler.
void scene_set_table(const SceneDef* scenes, u8 count);
// Ignore invalid IDs; otherwise exit the current scene and enter the requested one.
// Selecting the current ID still performs exit/enter. Callbacks run synchronously.
void scene_change(u8 scene_id);
// Clear the change flag before calling the active scene's update handler, so a
// transition made during that handler is visible afterward.
void scene_update();
// Call the active scene's draw handler if one exists; no current scene is a no-op.
void scene_draw();
// Return the stored scene ID; zero is also returned before the first transition.
u8 scene_get_current();
// Read the transition flag, which is cleared at the start of an active scene update.
u8 scene_was_changed();


// Replace the single active transfer and retain its source pointer. Keep the
// source storage and any required ROM bank readable until all bytes are queued.
void nes_scene_stream_begin(unsigned short ppu_addr, unsigned char* src, unsigned short len, unsigned char chunk);
// Prepare the 960-byte tile portion of a nametable using 32-byte queue records.
void nes_scene_stream_begin_nametable(unsigned short nt_base, unsigned char* src960);
// Prepare the 64-byte attribute table at nametable base + 0x03C0.
void nes_scene_stream_begin_attr(unsigned short nt_base, unsigned char* src64);
// Prepare a 32-byte palette transfer to PPU address 0x3F00.
void nes_scene_stream_begin_palette(unsigned char* src32);
// Queue as many chunks as available capacity allows, capped at 127 bytes each.
// Return one when queue exhaustion requires a later retry, zero when all data has
// been queued. Zero does not mean the NMI consumer has already written the PPU.
unsigned char nes_scene_stream_step(void);

#endif

kitaqfc/lib/scene.h

scroll.h — 배경·윈도·분할 스크롤
c
/*
 * Optional declarations for the phase 9 NES scroll helper.
 */

extern unsigned char nes_scroll_ctrl_base;
extern unsigned short nes_scroll_camera_x;
extern unsigned short nes_scroll_camera_y;

// Store PPUCTRL bits other than the two nametable-selection bits for later application.
void nes_scroll_set_base_ctrl(unsigned char ctrl);
// Store camera coordinates without touching hardware registers.
void nes_scroll_set(unsigned short camera_x, unsigned short camera_y);
// Combine bit 8 of each camera coordinate with base PPUCTRL, reset the latch
// by reading PPUSTATUS, and write low-byte X/Y scroll. This uses 256-unit
// wrapping on both axes rather than normalizing Y to a 240-pixel nametable height.
// Initialize base control to the intended PPUCTRL value, including NMI enable, before applying.
// Only coordinate bits 0..8 are used; arrange safe PPU timing in the caller.
void nes_scroll_apply(void);
// Subtract the screen anchor from the world point into unsigned camera
// coordinates. Underflow wraps; world-edge clamping belongs to the game.
void nes_camera_follow_center(unsigned short target_x, unsigned short target_y, unsigned char center_x, unsigned char center_y);

kitaqfc/lib/scroll.h

sprite.h — OBJ 할당·배치, 메타스프라이트, 애니메이션
c
#ifndef SPRITE_H
#define SPRITE_H

#include "core.h"
#include "intrinsics.h"

// Keep this value within 1..64; tracking arrays and hardware OAM contain exactly 64 slots.
#ifndef SPRITE_MAX
#define SPRITE_MAX ((u8)64)
#endif

#define SPRITE_FLAG_PAL1     ((u8)0x01)
#define SPRITE_FLAG_PAL2     ((u8)0x02)
#define SPRITE_FLAG_PAL3     ((u8)0x03)
#define SPRITE_FLAG_PRIORITY ((u8)0x20)
#define SPRITE_FLAG_XFLIP    ((u8)0x40)
#define SPRITE_FLAG_YFLIP    ((u8)0x80)

// Signed per-part offsets are added modulo 256. This struct API has an explicit count,
// unlike the FF-terminated byte-stream API in metasprite.h.
typedef struct MetaSpritePart {
    s8 dx;
    s8 dy;
    u8 tile;
    u8 flags;
} MetaSpritePart;

// Initialize frame/ticks to valid values. Returned tile IDs advance by one regardless of hardware sprite size.
typedef struct SpriteAnim {
    u8 first_tile;
    u8 frame_count;
    u8 frame;
    u8 ticks;
    u8 ticks_per_frame;
} SpriteAnim;

extern u8 kq_sprite_active[64];
extern u8 kq_sprite_used;
extern u8 kq_sprite_height;

// Reset allocation/position state and hide each OAM slot through the intrinsic.
// The software height defaults to eight; hardware sprite size is configured elsewhere.
void sprite_init(void);
// Claim and hide the first free slot, or return 0xFF if all slots are active.
u8 sprite_alloc(void);
// Release an active valid slot and hide it; invalid/repeated frees are ignored.
void sprite_free(u8 id);
// Update software coordinates and the intrinsic OAM position for a valid slot.
void sprite_set_pos(u8 id, u8 x, u8 y);
// Replace the tile byte of a valid OAM slot without changing allocation state.
void sprite_set_tile(u8 id, u8 tile);
// Replace the hardware attribute byte of a valid OAM slot.
void sprite_set_flags(u8 id, u8 flags);
// Mark the software position off screen and hide its OAM entry without freeing it.
void sprite_hide(u8 id);
// Transfer intrinsic shadow OAM immediately; this wrapper does not wait for VBlank.
void sprite_flush_oam_now(void);
// Wait for NMI before requesting OAM DMA.
void sprite_flush_oam(void);
// Return the number of active slots, including slots activated by metasprite_draw.
// Hidden allocated slots remain active until sprite_free releases them.
u8 sprite_count_used(void);
// Flag software overlap estimates above eight sprites on one scanline.
u8 sprite_warn_scanline_overflow(void);
// Estimate peak overlap across 240 lines from software Y/height values.
// This does not model pixel transparency, OAM priority or every hardware overflow quirk.
u8 sprite_max_scanline_count(void);
// Place consecutive parts, counting each newly activated slot once. Existing
// active slots are overwritten without increasing the allocation count. Return
// the number written; an invalid first slot or zero count writes nothing.
u8 metasprite_draw(u8 first_id, u8 x, u8 y, const MetaSpritePart* parts, u8 count);
// Advance animation ticks and wrap the frame index, returning the selected tile.
// Zero ticks-per-frame freezes advancement; null state returns zero.
u8 anim_update(SpriteAnim* anim);

#endif

kitaqfc/lib/sprite.h

system.h — 시작, 프레임 수, 대기, 인터럽트
c
#ifndef SYSTEM_H
#define SYSTEM_H

#include "core.h"
#include "intrinsics.h"

// A synchronous callback invoked after the software frame count is incremented.
typedef void (*SystemCallback)(void);

extern u16 kq_system_frame;

// Reset the software frame counter and callback storage, then enable NMI.
void system_init(void);
// Wait for NMI, count the completed wait, then invoke the registered callback.
void system_wait_vblank(void);
// Replace the per-wait callback; null disables callback dispatch.
void system_set_vblank_callback(SystemCallback callback);
// Return the wrapping 16-bit count of completed waits through this wrapper.
u16 system_get_frame(void);
// Return the low byte of the software frame counter.
u8 system_get_frame8(void);
// Enable maskable CPU IRQs through the intrinsic; NMI is controlled separately.
void system_enable_interrupts(void);
// Disable maskable CPU IRQs; this does not disable the PPU's NMI source.
void system_disable_interrupts(void);

#define fc_init system_init
#define fc_wait_vblank system_wait_vblank
#define fc_set_vblank_callback system_set_vblank_callback
#define fc_frame_count system_get_frame
#define gb_init system_init
#define gb_wait_vblank system_wait_vblank
#define gb_set_vblank_callback system_set_vblank_callback
#define gb_frame_count system_get_frame

#endif

kitaqfc/lib/system.h

tilemap.h — 사각 타일맵 영역 조작
c
/*
 * Optional declarations for the phase 11 tilemap helper.
 */

// The map borrows row-major tile bytes; keep width*height bytes readable in the active CPU mapping.
// solid_from is a tile-ID threshold, not an independent collision bitmap.
struct NesTilemap {
    unsigned char* tiles;
    unsigned char width;
    unsigned char height;
    unsigned char solid_from;
};

// Compute a row-major byte offset by adding the row stride ty times. This
// internal arithmetic helper does not validate coordinates.
unsigned short nes_tilemap_index(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Return a tile byte, or 0xFF when coordinates exceed map dimensions. The
// map/storage pointers must be valid; 0xFF may also be an actual stored tile.
unsigned char nes_tilemap_get(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Treat out-of-bounds/0xFF tiles as solid, then compare other tile IDs with
// the map's solid_from threshold.
unsigned char nes_tilemap_point_solid(struct NesTilemap* map, unsigned char tx, unsigned char ty);
// Convert pixels to eight-pixel tile X and narrow to a byte. Keep world X
// below 2048 when wraparound is not intended.
unsigned char nes_world_to_tile8_x(unsigned short world_x);
// Convert pixels to eight-pixel tile Y with the same byte-coordinate limit.
unsigned char nes_world_to_tile8_y(unsigned short world_y);
// Convert a world point to tile coordinates and apply the tile solidity rule.
unsigned char nes_tilemap_world_point_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y);
// Test only the four corners of a nonempty world box. Interior/edge tiles
// between the corners are not scanned, so large boxes can miss obstacles.
// Width/height must be nonzero and endpoint arithmetic must not overflow.
unsigned char nes_tilemap_world_box_solid(struct NesTilemap* map, unsigned short world_x, unsigned short world_y, unsigned char width, unsigned char height);

kitaqfc/lib/tilemap.h

vram.h — 큐를 통한 VRAM 갱신과 전송
c
#ifndef VRAM_H
#define VRAM_H

#include "core.h"
#include "intrinsics.h"

// Coordinates use 32-byte nametable rows. These wrappers do not clip to the 32x30 visible tile area.
#ifndef VRAM_NT_BASE
#define VRAM_NT_BASE ((u16)0x2000)
#endif

// Clear the intrinsic queue and its overflow latch.
void vram_init(void);
// Discard queued commands and explicitly reset the overflow latch.
void vram_clear_queue(void);
// Queue one tile at the default nametable base. Return the current overflow
// status, which may include a failure from an earlier operation.
u8 vram_queue_bg_tile(u8 x, u8 y, u8 tile);
// Alias the queued background-tile operation.
u8 vram_queue_tile(u8 x, u8 y, u8 tile);
// Queue one fill per row. This is not transactional: capacity exhaustion can
// leave earlier rows queued, and the loop continues attempting later rows.
u8 vram_queue_bg_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);
// Queue each tightly packed source row as a pointer-based copy record. Keep
// source storage and its ROM bank accessible until queue execution finishes;
// partial queuing remains possible on overflow.
u8 vram_queue_bg_block(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);
// Split a byte transfer into copy records of at most 127 bytes. The runtime
// retains source pointers rather than copying payloads into the queue. Return
// the overflow latch state after all records have been attempted.
u8 vram_queue_memcpy(u16 dst, const void* src, u16 len);
// Alias the byte-copy queue helper; len remains a byte count, not a tile count.
u8 vram_queue_tiles(u16 dst, const void* src, u16 len);
// Queue fills of at most 127 bytes each. Overflow can leave a partially queued
// transfer; no rollback is performed.
u8 vram_queue_memset(u16 dst, u8 value, u16 len);
// Invoke the queue executor directly. Its ready flag must already be set by
// commit, and the caller supplies appropriate PPU timing.
void vram_flush_now(void);
// Commit queued records for consumption and wait for the NMI path.
void vram_flush(void);
// Return the encoded queue length in bytes, not the number of commands.
u8 vram_get_queue_used(void);
// Return the remaining command-buffer capacity in bytes. A command requires
// space for its complete encoded record, including its address and metadata.
// Read before committing: NMI may consume a committed queue asynchronously.
u8 vram_get_queue_free(void);
// Return the queue's total capacity in bytes, independent of its current usage.
// This describes the command buffer, not free space in PPU VRAM.
u8 vram_get_queue_capacity(void);
// Read the overflow latch without clearing it.
u8 vram_get_overflowed(void);

#endif

kitaqfc/lib/vram.h

vram_queue.h — NMI가 처리하는 VRAM 큐
c
#ifndef VRAM_QUEUE_H
#define VRAM_QUEUE_H

#include "intrinsics.h"

/* Safe frame-time VRAM queue. These names are compiler intrinsic entry points. */
// Reset pending intrinsic-queue records; this is separate from runtime.c nes_vram_queue_clear.
void __vramq_clear(void);
// Append one address/value update; use the overflow latch to detect capacity failure.
void __vramq_put(u16 ppu_addr, u8 value);
// Retain the source address for later consumption; keep its storage and bank mapping valid.
void __vramq_copy(u16 ppu_addr, const u8* src, u8 len);
// Append a repeated-byte update; the source value is stored in the record.
void __vramq_fill(u16 ppu_addr, u8 value, u8 len);
// Publish the pending records to the configured consumer. Do not mutate them while consumption can run.
void __vramq_commit(void);
// Execute a ready queue directly; committing and safe PPU timing are caller responsibilities.
void __vramq_exec(void);
// Return encoded bytes currently occupied, not transfer payload bytes or command count.
u8 __vramq_len(void);
// Read the latched capacity failure; reading does not reset it.
u8 __vramq_overflow(void);
// Clear the overflow latch explicitly after handling the failed submission.
void __vramq_clear_overflow(void);
// Return total encoded command-buffer capacity; this is independent of PPU VRAM capacity.
u8 __vramq_capacity(void);

#endif

kitaqfc/lib/vram_queue.h

vrc6_sound.h — VRC6 확장 음원
c
#ifndef VRC6_SOUND_H
#define VRC6_SOUND_H

#include "core.h"

// Retained names for raw control codes 0..3; their numeric suffixes are not measured duty percentages.
// The KUROSAKI pulse model uses (code+1)/16 for these codes. Combine with a low volume nibble.
#define VRC6_PULSE_DUTY_12 0x00
#define VRC6_PULSE_DUTY_25 0x10
#define VRC6_PULSE_DUTY_50 0x20
#define VRC6_PULSE_DUTY_75 0x30
#define VRC6_PULSE_GATE 0x80

// Clear channel-enable/high-timer registers and output controls for all VRC6 voices.
// Requires the VRC6 register layout used by this module. No mapper setup or global frequency-control write is included.
void nes_vrc6_silence_all(void);
// Write pulse-1 control and the low 12 timer bits, then enable the channel.
void nes_vrc6_pulse1_set(u8 control, u16 period);
// Write pulse-2 control and the low 12 timer bits, then enable the channel.
void nes_vrc6_pulse2_set(u8 control, u16 period);
// Disable pulse 1 by clearing its high timer/enable register.
void nes_vrc6_pulse1_off(void);
// Disable pulse 2 by clearing its high timer/enable register.
void nes_vrc6_pulse2_off(void);
// Mask the saw accumulation rate to six bits, load a 12-bit timer and enable output.
void nes_vrc6_saw_set(u8 rate, u16 period);
// Disable the saw channel by clearing its high timer/enable register.
void nes_vrc6_saw_off(void);

#endif

kitaqfc/lib/vrc6_sound.h

vrc7_sound.h — VRC7 FM 음원
c
#ifndef VRC7_SOUND_H
#define VRC7_SOUND_H

#include "core.h"

#define VRC7_KEY_ON 0x10
#define VRC7_SUSTAIN 0x20

// Write register selection followed immediately by data. No explicit device
// settling delay or cartridge detection is inserted by this wrapper.
// The caller must arrange device timing and serialize register/data pairs against interrupt writers.
void nes_vrc7_write(u8 reg, u8 value);
// Copy eight caller-supplied patch bytes into user-instrument registers 0..7.
void nes_vrc7_set_user_patch(const u8* patch8);
// Validate channel 0..5, then pack a 9-bit frequency, 3-bit block, key flags,
// instrument and volume nibbles into its registers. Volume is the raw hardware
// attenuation field, not a linear loudness value.
void nes_vrc7_channel_set(u8 channel, u8 instrument, u8 volume, u16 fnum, u8 block, u8 key_flags);
// Clear the channel's entire frequency-high/block/key register, not just the
// key-on bit. A later note must restore frequency and block settings.
void nes_vrc7_key_off(u8 channel);
// Key off all six voices and set instrument zero with maximum attenuation.
void nes_vrc7_silence_all(void);

#endif

kitaqfc/lib/vrc7_sound.h

wire3d.h — DMG용 와이어프레임
c
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
#ifndef KITAQFC_WIRE3D_H
#define KITAQFC_WIRE3D_H
#include "core.h"
#include "intrinsics.h"
// Choose a supported viewport before including wire3d.c. Smaller views reduce
// line work and dirty CHR transfers; all use original one-bit line graphics.
#ifndef WIRE3D_FC_WIDTH
#define WIRE3D_FC_WIDTH 64
#endif
#ifndef WIRE3D_FC_HEIGHT
#define WIRE3D_FC_HEIGHT 48
#endif
#if WIRE3D_FC_WIDTH == 64 && WIRE3D_FC_HEIGHT == 48
#define WIRE3D_FC_TILES 48
#elif WIRE3D_FC_WIDTH == 96 && WIRE3D_FC_HEIGHT == 64
#define WIRE3D_FC_TILES 96
#elif WIRE3D_FC_WIDTH == 128 && WIRE3D_FC_HEIGHT == 96
#define WIRE3D_FC_TILES 192
#else
#error Select 64x48, 96x64 or 128x96 for the wireframe viewport
#endif
#define WIRE3D_FC_VERTEX_LIMIT 24
typedef struct Wire3DFC_Vec3 { s8 x; s8 y; s8 z; } Wire3DFC_Vec3;
typedef struct Wire3DFC_Edge { u8 a; u8 b; } Wire3DFC_Edge;
extern u8 wire3d_transfer_frames;
extern u8 wire3d_uploaded_tiles;

// Own both CHR-RAM pattern tables and nametable zero. Requires writable 8 KiB
// CHR RAM and PRG RAM 0x6800..0x70BF; reserves ZP 0x00..0x0A for drawing/transfer.
// Build with --nes-chr-ram and provide cartridge PRG RAM for the stage.
// Center the viewport, clear video memory, select cyan-on-dark colors and turn
// on background rendering with NMI disabled. Do not run another PPU writer.
void Wire3DFC_Init(void);
// Clear the previous staged drawing. The displayed front image remains intact.
void Wire3DFC_BeginFrame(void);
// Draw a connected, inclusive segment. Endpoints must be -512..511. Pixels
// outside the viewport are clipped; completely outside bounding boxes return.
void Wire3DFC_DrawLine2D(s16 ax,s16 ay,s16 bx,s16 by);
// Rotate a small model-space point in Y/X/Z order. Angles wrap in 32 steps.
// Start with components in -63..63; supply three distinct writable pointers.
void Wire3DFC_RotatePoint(s16* x,s16* y,s16* z,u8 rx,u8 ry,u8 rz);
// Project camera-space coordinates using an integer reciprocal table. Require
// x/y within -127..127 and z within 32..255. Return zero without touching outputs
// on invalid input; success returns one and writes unclipped screen coordinates.
u8 Wire3DFC_ProjectPoint(s16 x,s16 y,s16 z,s16* sx,s16* sy);
// Draw when both endpoints pass the projection range. Near-plane crossings
// are rejected, not intersected with the near plane; screen pixels are clipped.
void Wire3DFC_DrawLine3D(s16 ax,s16 ay,s16 az,s16 bx,s16 by,s16 bz);
// Rotate, translate and project up to 24 vertices, then draw indexed edges.
// Invalid edge indices/depth endpoints are skipped. Model data must remain
// readable in the current PRG mapping. This draws all edges, without face culling.
void Wire3DFC_DrawModel(const Wire3DFC_Vec3* vertices,u8 vertex_count,const Wire3DFC_Edge* edges,u8 edge_count,s16 x,s16 y,s16 z,u8 rx,u8 ry,u8 rz);
// Upload changed tiles to the hidden CHR table in bounded VBlank batches and
// swap only after completion. Blocks across as many frames as needed. The two
// counters report this call's upload batches plus swap, and tile count.
void Wire3DFC_EndFrame(void);
#endif

kitaqfc/lib/wire3d.h

wire3d_tables.h —
c
// Copyright (c) 2026 DAISUKE OBA. SPDX-License-Identifier: MIT
// Generated from round(64*sin(2*pi*i/32)) and round(64*width/z).
// Depths below 32 are rejected. Saturating 256 to 255 at the largest
// viewport/nearest depth bounds the projection error without a ninth bit.
__prg_rom const s8 w3dfc_sin[32]={
    0,12,24,36,45,53,59,63,64,63,59,53,45,36,24,12,
    0,-12,-24,-36,-45,-53,-59,-63,-64,-63,-59,-53,-45,-36,-24,-12,
};
#if WIRE3D_FC_WIDTH == 64
__prg_rom const u8 w3dfc_recip[256]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    128,124,120,117,114,111,108,105,102,100,98,95,93,91,89,87,
    85,84,82,80,79,77,76,74,73,72,71,69,68,67,66,65,
    64,63,62,61,60,59,59,58,57,56,55,55,54,53,53,52,
    51,51,50,49,49,48,48,47,47,46,46,45,45,44,44,43,
    43,42,42,41,41,41,40,40,39,39,39,38,38,38,37,37,
    37,36,36,36,35,35,35,34,34,34,34,33,33,33,33,32,
    32,32,32,31,31,31,31,30,30,30,30,29,29,29,29,29,
    28,28,28,28,28,27,27,27,27,27,27,26,26,26,26,26,
    26,25,25,25,25,25,25,25,24,24,24,24,24,24,24,23,
    23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,
    21,21,21,21,21,21,21,21,20,20,20,20,20,20,20,20,
    20,20,20,19,19,19,19,19,19,19,19,19,19,19,18,18,
    18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,
    17,17,17,17,17,17,17,17,17,16,16,16,16,16,16,16,
};
__prg_rom const u8 w3dfc_row_lo[48]={
    0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
    128,129,130,131,132,133,134,135,192,193,194,195,196,197,198,199,
    0,1,2,3,4,5,6,7,64,65,66,67,68,69,70,71,
};
__prg_rom const u8 w3dfc_row_hi[48]={
    104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
    104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
    105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
};
__prg_rom const u8 w3dfc_tile_row[48]={
    0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,
    16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24,
    32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40,
};
#elif WIRE3D_FC_WIDTH == 96
__prg_rom const u8 w3dfc_recip[256]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    192,186,181,176,171,166,162,158,154,150,146,143,140,137,134,131,
    128,125,123,120,118,116,114,112,110,108,106,104,102,101,99,98,
    96,95,93,92,90,89,88,87,85,84,83,82,81,80,79,78,
    77,76,75,74,73,72,71,71,70,69,68,68,67,66,65,65,
    64,63,63,62,61,61,60,60,59,59,58,57,57,56,56,55,
    55,54,54,53,53,53,52,52,51,51,50,50,50,49,49,48,
    48,48,47,47,47,46,46,46,45,45,45,44,44,44,43,43,
    43,42,42,42,42,41,41,41,40,40,40,40,39,39,39,39,
    38,38,38,38,37,37,37,37,37,36,36,36,36,36,35,35,
    35,35,35,34,34,34,34,34,33,33,33,33,33,33,32,32,
    32,32,32,32,31,31,31,31,31,31,30,30,30,30,30,30,
    30,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,
    27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,
    26,25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,
};
__prg_rom const u8 w3dfc_row_lo[64]={
    0,1,2,3,4,5,6,7,96,97,98,99,100,101,102,103,
    192,193,194,195,196,197,198,199,32,33,34,35,36,37,38,39,
    128,129,130,131,132,133,134,135,224,225,226,227,228,229,230,231,
    64,65,66,67,68,69,70,71,160,161,162,163,164,165,166,167,
};
__prg_rom const u8 w3dfc_row_hi[64]={
    104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
    104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,
    105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
    106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,
};
__prg_rom const u8 w3dfc_tile_row[64]={
    0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,
    24,24,24,24,24,24,24,24,36,36,36,36,36,36,36,36,
    48,48,48,48,48,48,48,48,60,60,60,60,60,60,60,60,
    72,72,72,72,72,72,72,72,84,84,84,84,84,84,84,84,
};
#elif WIRE3D_FC_WIDTH == 128
__prg_rom const u8 w3dfc_recip[256]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    255,248,241,234,228,221,216,210,205,200,195,191,186,182,178,174,
    171,167,164,161,158,155,152,149,146,144,141,139,137,134,132,130,
    128,126,124,122,120,119,117,115,114,112,111,109,108,106,105,104,
    102,101,100,99,98,96,95,94,93,92,91,90,89,88,87,86,
    85,84,84,83,82,81,80,80,79,78,77,77,76,75,74,74,
    73,72,72,71,71,70,69,69,68,68,67,67,66,66,65,65,
    64,64,63,63,62,62,61,61,60,60,59,59,59,58,58,57,
    57,56,56,56,55,55,55,54,54,54,53,53,53,52,52,52,
    51,51,51,50,50,50,49,49,49,48,48,48,48,47,47,47,
    47,46,46,46,46,45,45,45,45,44,44,44,44,43,43,43,
    43,42,42,42,42,42,41,41,41,41,41,40,40,40,40,40,
    39,39,39,39,39,38,38,38,38,38,38,37,37,37,37,37,
    37,36,36,36,36,36,36,35,35,35,35,35,35,35,34,34,
    34,34,34,34,34,33,33,33,33,33,33,33,33,32,32,32,
};
__prg_rom const u8 w3dfc_row_lo[96]={
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
    0,1,2,3,4,5,6,7,128,129,130,131,132,133,134,135,
};
__prg_rom const u8 w3dfc_row_hi[96]={
    104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,
    105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
    106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,
    107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
    108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,
    109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,
};
__prg_rom const u8 w3dfc_tile_row[96]={
    0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16,
    32,32,32,32,32,32,32,32,48,48,48,48,48,48,48,48,
    64,64,64,64,64,64,64,64,80,80,80,80,80,80,80,80,
    96,96,96,96,96,96,96,96,112,112,112,112,112,112,112,112,
    128,128,128,128,128,128,128,128,144,144,144,144,144,144,144,144,
    160,160,160,160,160,160,160,160,176,176,176,176,176,176,176,176,
};
#endif

kitaqfc/lib/wire3d_tables.h

zapper.h — 라이트건 입력 인터페이스
c
#ifndef ZAPPER_H
#define ZAPPER_H
#include "intrinsics.h"
// Return raw 4016 & 18, retaining trigger and active-low light bits.
u8 __zapper_raw1(void);
// Return raw 4017 & 18 for the second port.
u8 __zapper_raw2(void);
// Return port-1 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger1(void);
// Return port-2 trigger bit 4 normalized to 0 or 1.
u8 __zapper_trigger2(void);
// Invert port-1 bit 3 and normalize: one means light detected.
u8 __zapper_light1(void);
// Invert port-2 bit 3 and normalize: one means light detected.
u8 __zapper_light2(void);
// Default to the port-2 normalized trigger reader.
u8 __zapper_trigger(void);
// Default to the port-2 normalized light reader.
u8 __zapper_light(void);
#endif

kitaqfc/lib/zapper.h

zx0.h —
c
#pragma once
#include "core.h"
// Copyright (c) 2026 DAISUKE OBA. MIT License.
// ZX0-compatible forward v2 streams; format designed by Einar Saukas.
#define ZX0_OK 0
#define ZX0_BAD_ARGUMENT 1
#define ZX0_TRUNCATED 2
#define ZX0_BAD_STREAM 3
#define ZX0_OUTPUT_FULL 4
#define ZX0_DISPLAY_ACTIVE 5
// Shared last-call status. These foreground routines are not reentrant.
extern u8 zx0_error;
// Return decoded bytes, or zero on error (possibly after partial writes).
// Source and output must not overlap or cross their current CPU bank windows.
// Capacity and packed_size are byte counts. No prefix/backward/v1 streams.
u16 zx0_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);
// Decode a KQA1 raw/RLE/ZX0 asset produced with --format=auto.
// Empty raw assets return zero with zx0_error==ZX0_OK.
u16 asset_decompress(void* dst, u16 capacity, const void* src, u16 packed_size);
// Decode into caller-owned RAM, then transfer to CHR RAM or nametable memory.
// Rendering must be off. Preserve PPUCTRL; set scroll before enabling rendering.
// The workspace must hold the whole output. No palette upload or CHR ROM writes.
u16 zx0_decompress_vram(u16 ppu_addr,void* workspace,u16 capacity,const void* src,u16 packed_size);

kitaqfc/lib/zx0.h