https://github.com/suzukiplan/vgsx
Emulator core for a fixed virtual game console defining a stable MC68030-based hardware model with YM2612 FM sound and a custom VDP.
https://github.com/suzukiplan/vgsx
emulator fixed-hardware-model game-engine game-engine-2d sdk sdl2 vdp virtual-hardware
Last synced: 2 months ago
JSON representation
Emulator core for a fixed virtual game console defining a stable MC68030-based hardware model with YM2612 FM sound and a custom VDP.
- Host: GitHub
- URL: https://github.com/suzukiplan/vgsx
- Owner: suzukiplan
- License: mit
- Created: 2025-09-09T08:45:27.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2026-05-10T07:38:10.000Z (3 months ago)
- Last Synced: 2026-05-10T09:36:46.522Z (3 months ago)
- Topics: emulator, fixed-hardware-model, game-engine, game-engine-2d, sdk, sdl2, vdp, virtual-hardware
- Language: C
- Homepage: https://github.com/suzukiplan/vgsx
- Size: 43.4 MB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README-jp.md
- Changelog: CHANGES.md
- License: LICENSE-Musashi.txt
Awesome Lists containing this project
README
# VGS-X [](https://dl.circleci.com/status-badge/redirect/gh/suzukiplan/vgsx/tree/master)

## VGS Philosophy
VGS(SUZUKI PLAN - Video Game System)は、ひとつの一貫した思想に基づいて設計されています。
それは、**安定した仮想ハードウェアモデルを定義し、それをエミュレーションによって長期にわたり維持すること**です。
VGS は、ゲームエンジンを単なる API やライブラリの集合として捉えるのではなく、
CPU・グラフィックス・サウンド・メモリ構成を含む「ゲームが動作する計算機そのもの」を定義します。
このモデルを固定することで、ゲームは実在のハードウェアや OS、変化し続けるツールチェーンから切り離されます。
VGS の目的は、技術的な新規性や最高性能を追求することではありません。
外部プラットフォームの変化に振り回されることなく、
同じ思考モデルのまま、何十年にもわたってゲームを作り続けられる点にこそ価値があります。
VGS は、長期的なゲーム制作を現実的で持続可能なものとし、
思考の安定性を保つために存在する Video Game System です。
## Relationship to VGS-Zero
VGS-X は [VGS-Zero](https://github.com/suzukiplan/vgszero) の後継システムですが、**単なる性能向上版ではありません**。
両者は共通して、「固定された仮想ハードウェアモデルを定義し、それをエミュレーションによって長期にわたり維持する」
という中核思想を持っています。この設計は、実在のハードウェアや OS に依存しません。
VGS-Zero と VGS-X に共通する重要な設計原則のひとつが、**実機クロック非依存設計**です。
この原則は、現代的なホスト環境の高い性能を使うことを否定するものではありません。
否定しているのは、**ソフトウェアの意味論や挙動を、実機 CPU クロックやホスト性能を前提として定義すること**です。
VGS-X において、ゲームロジック、タイミング、映像および音響表現は、
CPU、VDP、サウンド、DMA、メモリ構成といった仮想ハードウェアの仕様のみによって決定されます。
ホスト環境の性能は、この仮想マシンを安定かつ忠実に実装・維持するための計算資源としてのみ使用され、
ゲームの挙動そのものに影響を与えることはありません。
VGS-Zero は、8bit Z80 を前提とした計算機モデルにより、
ホスト側の時間概念を完全に排除し、ゲームを「特定の時代に固定された計算機定義に属する成果物」
として扱うことを可能にしました。
VGS-X は、この考え方を 32bit MC68030 ベースの計算機モデルへと再定義し、
計算規模や資産容量を大幅に拡張しながらも、
**ソフトウェアの挙動がホスト性能によって変化しない**という原則を厳密に維持しています。
VGS-X は、処理速度や技術的な新規性を競うためのシステムではありません。
ソフトウェアの意味論と実装上の性能を明確に分離することで、
開発者が時間や実行環境を意識することなく、
長期にわたって同じ思考モデルでゲームを作り続けられる
安定した仮想ゲームプラットフォームとして設計されています。
## About VGS-X
VGS-X は MC68030 プロセッサ、YM2612(OPN)FM サウンドチップ、MC68k アーキテクチャ向けに最適化した独自 VDP を搭載する 16 ビットゲーム機です。
Basic Features:
- CPU: MC68030 _(クロック制限なし)_
- [VGS Standard Library](#vgs-standard-library) との完全互換
- VDP: VGS-X Video
- [BGM](#0xe010xxo---background-music-bgm): .vgm 形式(YM2612)
- [SFX](#0xe011xxo---sound-effect-sfx): .wav 形式(44,100Hz, 16bit, 2ch)
- 高速 [DMA (Direct Memory Access)](#0xe00008-0xe00014io---direct-memory-access)
- 高速 [i-math(整数演算)](#0xe00100-0xe00118io---angle) API
- [セーブデータ](#0xe030xxio---savedata) 機能
VDP Features:
- 画面解像度: 320x200 ピクセル (内部的には 640x400)
- 色表現: 24bit カラー(RGB888)
- BG: 4 枚の [ネームテーブル](#name-table) と 2 種類のモード([キャラクタパターン/ビットマップ](#0xd20028-0xd20034-bitmap-mode))
- BG ネームテーブルサイズ: 256x256(2048x2048 ピクセル)
- BG 向け [ハードウェアスクロール](#0xd20008-0xd20024-hardware-scroll) 機能
- [キャラクタパターン](#character-pattern) 数: 65,536
- [OAM](#oam-object-attribute-memory)(スプライト)数: 1,024
- [スプライトサイズ](#size-of-sprite): 8x8~512x512 ピクセル
- 各スプライト単位で [回転](#rotate-of-sprite)・[拡大縮小](#scale-of-sprite)・[アルファブレンド](#alpha-blend-of-sprite)・[マスク](#mask-of-sprite) をサポート
- [ビットマップモード](#0xd20028-0xd20034-bitmap-mode) 向け [ビットマップ描画](#0xd2004c-0xd20068-bitmap-graphic-draw) 機能を搭載
- [ビットマップモード](#0xd20028-0xd20034-bitmap-mode) 向け [プロポーショナルフォント](#0xd2007c-0xd2008c-Proportional-font) 機能を搭載
- JIS-X-0201 / JIS-X-0208 の日本語表示に対応(k8x12 フォント利用)
ゲーム開発には MC68k 用 GCC(_GNU Compiler Collection_)を使用できます。
開発環境として公式にサポートする OS は **Ubuntu Linux** と **macOS** です。_(Windows を開発マシンとして使う場合は WSL2 の利用を推奨します。)_
ランタイム環境は Steam クライアントが対応する PC 用 OS(Windows / macOS / Linux)をすべてサポートします。
将来的には Nintendo Switch 1/2 と PlayStation 4/5 でも動作するランタイムを提供する予定です。NDA の関係で詳細は公開できませんが、[core](./src) モジュールは各ゲーム機の SDK 上でビルド・実行できることを確認済みです。
VGS-X は、一定性能の PC であればどこでも同一のゲーム体験を届けられる開発・配布環境を目指しています。
## VGS-X Runtime Philosophy and Portability
VGS-X は MIT ライセンスで提供されており、仕様およびリファレンス実装の
改変・カスタマイズ・再配布を自由に行うことができます。
本リポジトリに含まれる SDL2 ベースのランタイムは、
PC や Steam 環境における開発・検証・実用を目的とした
**リファレンス実装**です。
これは唯一の、あるいは必須のランタイムであることを意図していません。
VGS-X は、特定の実行環境に依存しない仮想ハードウェア仕様として設計されており、
必要に応じて開発者自身が独自のランタイムを実装できることを前提としています。
家庭用ゲーム機や専用ハードウェア向けの実装も、仕様上は排除されていません。
VGS-X は「すべてのランタイムを公式に提供する」ことを目的とするのではなく、
必要に応じて実装可能な、安定した仮想ハードウェア仕様を提供することを
主眼としています。
# First Step Guide
本編では、**VGS-X 向けアプリケーション開発を始めるための最初のステップ**として、
開発環境の構築、Example(Hello, World)のビルドと実行、
新規プロジェクトの作成方法、そして **将来にわたってゲームの互換性を保つための指針** を解説します。
このセクションを読み終えれば、VGS-X 上で動作するゲームを自分の手でビルドし、
継続的に開発を進められる状態になります。
## Setup Build Environment
VGS-X は MC68030 ELF 形式のモジュールを実行するため、ゲーム開発には `m68k-elf-gcc` のインストールが必須です。
macOS では Homebrew から簡単に導入できますが、Ubuntu Linux には apt パッケージが存在しないため自前でビルドする必要があります。
### Setup Build Environment: macOS
Xcode と Homebrew を導入済みの環境で、`m68k-elf-gcc` と `SDL2` をインストールしてください。
```bash
brew install m68k-elf-gcc
brew install sdl2
```
### Setup Build Environment: Ubuntu Linux
以下は VGS-X 向けゲーム開発に必要なインストール手順です。
```bash
# 依存パッケージのインストール
sudo apt update
sudo apt install build-essential bison flex libgmp-dev libmpc-dev libmpfr-dev texinfo libncurses5-dev
# SDL2 と ALSA のインストール
sudo apt install libsdl2-dev libasound2 libasound2-dev
# m68k-elf ビルド用作業ディレクトリ
mkdir ~/m68k-work
# MC68k 用 binutils のビルドとインストール
cd ~/m68k-work
wget https://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.gz
tar xvf binutils-2.40.tar.gz
cd binutils-2.40
mkdir ../binutils-build
cd ../binutils-build
../binutils-2.40/configure --target=m68k-elf --prefix=/usr/local/m68k-elf --disable-nls --disable-werror
make -j$(nproc)
sudo make install
export PATH=$PATH:/usr/local/m68k-elf/bin
# MC68k 用 GCC のビルドとインストール
cd ~/m68k-work
wget https://ftp.gnu.org/gnu/gcc/gcc-12.2.0/gcc-12.2.0.tar.gz
tar xvf gcc-12.2.0.tar.gz
cd gcc-12.2.0
./contrib/download_prerequisites
mkdir ../gcc-build
cd ../gcc-build
../gcc-12.2.0/configure --target=m68k-elf --prefix=/usr/local/m68k-elf --enable-languages=c --disable-nls --disable-libssp --without-headers
make all-gcc -j$(nproc)
make all-target-libgcc -j$(nproc)
sudo make install-gcc install-target-libgcc
```
ターミナル起動時にパスが通るよう、以下を ~/.zprofile に追記してください。
```.zprofile
export PATH=$PATH:/usr/local/m68k-elf/bin
```
> 参考資料
> [https://computeralgebra.hatenablog.com/entry/2025/02/26/233231](https://computeralgebra.hatenablog.com/entry/2025/02/26/233231)
> 執筆者の方に感謝いたします。
## Build and Execute an Example
`m68k-elf-gcc` のインストールが完了したら、VGS-X 向けゲーム開発を始められます。
以下は `git clone` で本リポジトリを取得し、「HELLO, WORLD!」を描画するサンプルを実行する手順です。
```bash
# ホームディレクトリへ移動
cd ~
# VGS-X リポジトリをクローン
git clone https://github.com/suzukiplan/vgsx
# サンプルディレクトリに移動
cd vgsx/example/01_hello
# ビルドと実行
make
```

## How to Create a New Project
[makeprj](#makeprj) コマンドを利用すると、新しいゲーム開発プロジェクトを作成できます。
```bash
# "My Game" という新規プロジェクトを作成
~/vgsx/tools/makeprj/makeprj ~/projects/MyGame
```
## Compatibility Policy
VGS-X は、将来のバージョンアップにより互換性が維持されなくなる変更(破壊的変更)を行う可能性があります。
[makeprj](#makeprj) コマンドで生成した時点のサブモジュールバージョンを維持している限り、あなたのプロジェクトの互換性には影響しません。ただし、最新バージョンの VGS-X で提供される機能が必要になった場合は、[CHANGES.md](./CHANGES.md) を確認し、**(Disruptive)** のマークが付いた項目の変更内容を入念に確認してください。
# Architecture Reference Manual
本章では、VGS-X の仮想ハードウェア構成や各種仕様について、**実装時に参照するための技術情報**をまとめています。
ここに記載されている内容は、**最初から最後まで通読することを前提としたものではありません**。
本章は、実際にゲームのコードを書きながら
「この挙動はどう定義されているのか」「このレジスタや I/O は何を意味するのか」
といった疑問が生じた際に、**随時参照するリファレンスマニュアル**として利用されることを想定しています。
VGS-X を初めて触る場合は、本章をいったん読み飛ばし、
実装が進んだ段階で必要な箇所に戻ってくる形でも問題ありません。
> 本章で解説するハードウェア機能は、後述の [VGS Standard Library](#vgs-standard-library) で解説する
> **C 言語向けランタイムライブラリを通じて、すべて利用することができます**。
## Screen Specification
- 画面解像度は固定で **320x200** ピクセルです。
- BG は 4 レイヤー、スプライトは 1 レイヤーです。
- 各 BG は [Character Pattern Mode](#character-pattern) と [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の 2 モードを切り替えられます。
- スプライトは最大 1024 枚表示できます。
> _VGS-X の画面解像度 (320x200) は、SteamDeck (1280x800) で全画面表示できるよう設計されています。_
## 4k Display
VGS-X の解像度(座標系)は 320x200 ピクセルですが、内部的な画面バッファは 640x400 ピクセルです。
そして、スプライトやBGは常に 4倍サイズ(縦と横がそれぞれ2倍)で描画されています。
これにより、スプライトの拡大率が 50% 以上ならドット欠けが無く縮小表示することが可能です。
また、回転時のドット欠けも少なくなります。
## Memory Map
VGS-X では MC68030 の 24bit(16MB)アドレス空間のうち、先頭 12MB(0x000000 ~ 0xBFFFFF)がプログラム領域として割り当てられています。
末尾 1MB(0xF00000 ~ 0xFFFFFF)は WRAM(_Work RAM_)です。
プログラム領域と WRAM の間(0xC00000 ~ 0xEFFFFF = 3MB)が VDP および [I/O](#io-map) のメモリマップです。
| Address | Size | Description |
|:-------------------:|--------:|:-------------|
| 0x000000 ~ 0xBFFFFF | 12288KB | [Program (ELF module)](#program) |
| 0xC00000 ~ 0xCFFFFF | 1024KB | [Name Table](#name-table) |
| 0xD00000 ~ 0xD0FFFF | 64KB | [OAM](#oam-object-attribute-memory) |
| 0xD10000 ~ 0xD1FFFF | 64KB | [Palette](#palette) |
| 0xD20000 ~ 0xD2FFFF | 64KB | [VDP Register](#vdp-register) |
| 0xD30000 ~ 0xDFFFFF | 832KB | Reserved |
| 0xE00000 ~ 0xEFFFFF | 1024KB | [I/O](#io-map) |
| 0xF00000 ~ 0xFFFFFF | 1024KB | WRAM |
0xC00000 ~ 0xEFFFFF の mmap 領域へのアクセスは、常に 32 ビット境界に整列させる必要があります。
> このアドレス空間では下位 2 ビットが常にマスクされるため、0xC00000~0xC00003 へのアクセスはすべて 0xC00000 へのアクセスとして扱われます。
[Character patterns](#character-pattern) やサウンドデータは参照専用の ROM に格納され、プログラムから直接参照することはできません。VGS-Zero と同様に、パターン番号で指定します。
## Program
プログラム領域(0x000000 ~ 0xBFFFFF)には [ELF32(Executable and Linkable Format 32bit)](https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.intro.html) のバイナリモジュールを配置します。
VGS-X の電源投入時には、ROM カートリッジから読み込んだプログラムの ELF ヘッダで指定されたエントリポイントから実行が開始されます。
MC68k アセンブリのみでプログラムを記述する場合でも、必ず有効なエントリポイントと実行テキストを含む ELF32 ヘッダとプログラムヘッダを設定する必要があります。
> ただし VGS-X では高性能なコードをアセンブリだけで記述するメリットがほぼ無いため、全面的なアセンブリ実装は推奨しません。
VGS-X で動作するプログラムを生成する際に m68k-elf-gcc へ渡すべき主なオプションは以下の通りです。
```
m68k-elf-gcc
-mc68030
-O2
-I${VGSX_ROOT}/lib
-o program
program.c
-L${VGSX_ROOT}/lib
-T${VGSX_ROOT}/lib/linker.ld
-Wl,-ecrt0
```
VGS-X は C 標準ライブラリを提供しませんが、代わりに [VGS Standard Library](#vgs-standard-library) を提供しています。
> 例外として、GCC が提供する `stdarg.h` は利用できます。
## Character Pattern
VGS-X では最大 65,536 個のキャラクタパターンを利用できます。
1 つのキャラクタパターンは 8x8 ピクセルで構成されています。
データは次のビットレイアウトで 32 バイト分並びます。
| px0 | px1 | px2 | px3 | px4 | px5 | px6 | px7 | Line number |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :---------- |
| H00 | L00 | H01 | L01 | H02 | L02 | H03 | L03 | Line 0 |
| H04 | L04 | H05 | L05 | H06 | L06 | H07 | L07 | Line 1 |
| H08 | L08 | H09 | L09 | H10 | L10 | H11 | L11 | Line 2 |
| H12 | L12 | H13 | L13 | H14 | L14 | H15 | L15 | Line 3 |
| H16 | L16 | H17 | L17 | H18 | L18 | H19 | L19 | Line 4 |
| H20 | L20 | H21 | L21 | H22 | L22 | H23 | L23 | Line 5 |
| H24 | L24 | H25 | L25 | H26 | L26 | H27 | L27 | Line 6 |
| H28 | L28 | H29 | L29 | H30 | L30 | H31 | L31 | Line 7 |
- `Hxx`: 上位 4 ビット(0~15 = カラー番号)
- `Lxx`: 下位 4 ビット(0~15 = カラー番号)
備考:
- ビットレイアウトは VGS-Zero と互換です。
- キャラクタパターンはプログラムから直接参照できません。ネームテーブルまたは OAM でパターン番号を指定して描画します。
- BG とスプライトでパターン番号を共有します。
## Character Pattern RAM
- 起動時(初期状態)には、[makerom](#makerom) で指定した chr ファイルがメモリ(Character Pattern RAM)に展開されます。
- [Copy Character Pattern](#0xd20090-0xd20094-copy-character-pattern) 機能を用いることで、特定のキャラクタパターン番号のキャラクタを別のキャラクタパターン番号へコピーできます。
- [Transfer Character Pattern](#0xd20098-0xd200a0-transfer-character-pattern) 機能を用いることで、プログラム ROM または RAM 上のキャラクタパターンをメモリへ展開できます。
- [Copy Character Pattern](#0xd20090-0xd20094-copy-character-pattern) または [Transfer Character Pattern](#0xd20098-0xd200a0-transfer-character-pattern) によって変更されたメモリ内容は、リセットにより初期状態に戻ります。
## Palette
- 最大 1,024 個のパレットを使用できます。
- 各パレットは RGB888 形式で 16 色を保持します。
- カラー番号 0 は透明色です。
- パレット 0 のカラー番号 0 は背景(バックドロップ)色になります。
| Address | Palette Number | Color Number |
|:-------------------:|:--------------:|:------------:|
| 0xD10000 ~ 0xD10003 | 0 | 0 |
| 0xD10004 ~ 0xD10007 | 0 | 1 |
| ... | ... | ... |
| 0xD103FC ~ 0xD103FF | 15 | 15 |
| ... | ... | ... |
| 0xD1FFFC ~ 0xD1FFFF | 1023 | 15 |
備考:
- ビットレイアウト: `******** rrrrrrrr gggggggg bbbbbbbb`
- パレットテーブルは 0xD10000 ~ 0xD1FFFF の全域(64KB)を使用します。
- パレットテーブルへのアクセスは常に 4 バイト境界で行う必要があります。
## Name Table
- ネームテーブルは [属性](#attribute) を格納する 256x256 x 4 バイトの二次元配列です。
- 表示領域としては 40x25(320x200 ピクセル)を表示します。
- キャラクタパターンや属性データを設定することで、背景レイヤー上に描画できます。
- ネームテーブルは 4 層構造で、BG0 の上に BG1、さらに BG2、BG3 の順に重ねて表示されます。
| Address | Size | Name Table |
|:-------------------:|:----:|:----------:|
| 0xC00000 ~ 0xC3FFFF | 256KB | BG0 |
| 0xC40000 ~ 0xC7FFFF | 256KB | BG1 |
| 0xC80000 ~ 0xCBFFFF | 256KB | BG2 |
| 0xCC0000 ~ 0xCFFFFF | 256KB | BG3 |
[Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) では、これらの領域は 320x200 ピクセルのフレームバッファとして扱われます。
ネームテーブルへのアクセスも常に 4 バイト境界で行ってください。
## Attribute
ネームテーブルおよび OAM の属性ビットは以下の通りです。
| Bit | Mnemonic | Description |
|:---:|:--------:|:------------|
| 0~15 | PTN | [Character Pattern](#character-pattern) 番号 (0~65535) |
| 16~25 | PAL | [Palette](#palette) 番号 (0~1023) |
| 26~29 | reserved | 将来互換性のため 0 を設定してください |
| 30 | F/V | 垂直方向の反転表示 |
| 31 | F/H | 水平方向の反転表示 |
## OAM (Object Attribute Memory)
OAM は以下の構造体を持ちます。
```c
typedef struct {
uint32_t visible; // Visible (0 or not 0)
int32_t y; // Position (Y)
int32_t x; // Position (X)
uint32_t attr; // Attribute
uint32_t size; // Size (0: 8x8, 1: 16x16, 2: 24x24, 3: 32x32 ... 63: 512x512)
int32_t rotate; // Rotate (-360 ~ 360)
uint32_t scale; // Scale (0 ~ 3200 percent)
uint32_t alpha; // Alpha (0: disabled, or 0x000001 ~ 0xFFFFFF)
uint32_t mask; // Mask (0: disabled, or RGB888)
uint32_t sly; // Scale Lock (Y)
uint32_t slx; // Scale Lock (X)
uint32_t pri; // High Priority Flag
uint32_t ram_ptr; // Bitmap Sprite Buffer (RGB888)
uint32_t reserved[3]; // Reserved
} ObjectAttributeMemory;
```
各属性の仕様は次の通りです。
| Name | Valid Range | Description |
|:-----|:-----------:|:------------|
| visible | 0 or 非 0 | 非 0 のときスプライトを描画 |
| y | -32768 ~ 32767 | スプライトの Y 座標 |
| x | -32768 ~ 32767 | スプライトの X 座標 |
| attr | 32bit | [Attribute](#attribute) |
| size | 0 ~ 63 | [Size](#size-of-sprite) |
| rotate | -360 ~ 360 | [Rotate](#rotate-of-sprite) |
| scale | 0 ~ 3200 | [Scale](#scale-of-sprite) |
| alpha | 0 or 0xRRGGBB | [Alpha Blend](#alpha-blend-of-sprite) |
| mask | 0 or 0xRRGGBB | [Mask](#mask-of-sprite) |
| sly | 0 or 1 | Lock [Scale](#scale-of-sprite) (Y) |
| slx | 0 or 1 | Lock [Scale](#scale-of-sprite) (X) |
| pri | 0 or 1 | [High Priority Flag]() |
| ram_ptr | 0 or RAM addr | [Bitmap Sprite](#bitmap-sprite) Buffer (RGB888) |
| reserved | - | 0 以外を設定しないでください |
### (Size of Sprite)
スプライトは `(size + 1) * 8` ピクセルの正方形として描画されます。
例えば size に 3(32x32 ピクセル)を指定すると、`16 = (size + 1) ^ 2` 個のパターンを次のように配置して描画します。
```
Size 3 Pattern Number Layout
+--------+--------+--------+--------+
| | | | |
| ptn+0 | ptn+1 | ptn+2 | ptn+3 |
| | | | |
+--------+--------+--------+--------+
| | | | |
| ptn+4 | ptn+5 | ptn+6 | ptn+7 |
| | | | |
+--------+--------+--------+--------+
| | | | |
| ptn+8 | ptn+9 | ptn+10 | ptn+11 |
| | | | |
+--------+--------+--------+--------+
| | | | |
| ptn+12 | ptn+13 | ptn+14 | ptn+15 |
| | | | |
+--------+--------+--------+--------+
```
### (Rotate of Sprite)
`rotate` に -360~360 の角度を指定すると、スプライトを回転させて描画できます。
ただし回転を有効にすると描画コストが増える点に注意してください。
[Angle](#0xe00100-0xe00118io---angle) 機能と組み合わせると、回転を伴う表現を容易に実装できます。
### (Scale of Sprite)
- `scale` に 0〜3200 の範囲で拡大率(パーセンテージ)を指定できます。
- `slx` または `sly` のいずれかを 0 以外の値に設定すると、X 軸または Y 軸のいずれかのスケーリングが防止されます。
### (Alpha Blend of Sprite)
`alpha` にアルファブレンド値を 0x000000〜0xFFFFFF の範囲で指定できます。
RGB 各成分に異なるアルファ値を設定できます:
- 0xFF0000 を指定すると赤成分のみを描画
- 0x00FF00 を指定すると緑成分のみを描画
- 0x0000FF を指定すると青成分のみを描画
### (Mask of Sprite)
マスク色に RGB888 の非 0 値を指定すると、スプライトを単色で塗りつぶします。
シューティングゲームの自機の影など、[Scale](#scale-of-sprite)、[Alpha Blend](#alpha-blend-of-sprite)、Mask を組み合わせた表現に利用できます。
### (High Priority Flag)
High priority flag `pri` をセットすることで描画優先度を `pri` がセットされていないスプライトよりも優先することができる。
### (Bitmap Sprite)
Bitmap Sprite Buffer (RGB888) `ram_ptr` に RAM アドレス(0以外)をセットすることで [キャラクタパターン](#character-pattern) を用いずに RAM に設定された RGB888 形式(1px = 4bytes)のスプライトを表示することができる。
※RAM バッファサイズ = `(size + 1) * 8` の二乗
## VDP Register
| Address | Name | Mnemonic | Description |
|:-------:|:----:|:--------:|:------------|
|0xD20000 | R0 | SKIP | [Skip Screen Update](#0xd20000-skip-screen-update) |
|0xD20004 | R1 | SPOS | [Sprites Position](#0xd20004-sprite-position) |
|0xD20008 | R2 | SX0 | [Scroll X of BG0](#0xd20008-0xd20024-hardware-scroll) |
|0xD2000C | R3 | SX1 | [Scroll X of BG1](#0xd20008-0xd20024-hardware-scroll) |
|0xD20010 | R4 | SX2 | [Scroll X of BG2](#0xd20008-0xd20024-hardware-scroll) |
|0xD20014 | R5 | SX3 | [Scroll X of BG3](#0xd20008-0xd20024-hardware-scroll) |
|0xD20018 | R6 | SY0 | [Scroll Y of BG0](#0xd20008-0xd20024-hardware-scroll) |
|0xD2001C | R7 | SY1 | [Scroll Y of BG1](#0xd20008-0xd20024-hardware-scroll) |
|0xD20020 | R8 | SY2 | [Scroll Y of BG2](#0xd20008-0xd20024-hardware-scroll) |
|0xD20024 | R9 | SY3 | [Scroll Y of BG3](#0xd20008-0xd20024-hardware-scroll) |
|0xD20028 | R10 | BMP0 | [Bitmap Mode of BG0](#0xd20028-0xd20034-bitmap-mode) |
|0xD2002C | R11 | BMP1 | [Bitmap Mode of BG1](#0xd20028-0xd20034-bitmap-mode) |
|0xD20030 | R12 | BMP2 | [Bitmap Mode of BG2](#0xd20028-0xd20034-bitmap-mode) |
|0xD20034 | R13 | BMP3 | [Bitmap Mode of BG3](#0xd20028-0xd20034-bitmap-mode) |
|0xD20038 | R14 | CLSA | [Clear Screen of All BGs](#0xd20038-0xd20048-clear-screen) |
|0xD2003C | R15 | CLS0 | [Clear Screen of BG0](#0xd20038-0xd20048-clear-screen) |
|0xD20040 | R16 | CLS1 | [Clear Screen of BG1](#0xd20038-0xd20048-clear-screen) |
|0xD20044 | R17 | CLS2 | [Clear Screen of BG2](#0xd20038-0xd20048-clear-screen) |
|0xD20048 | R18 | CLS3 | [Clear Screen of BG3](#0xd20038-0xd20048-clear-screen) |
|0xD2004C | R19 | G_BG | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20050 | R20 | G_X1 | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20054 | R21 | G_Y1 | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20058 | R22 | G_X2 | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD2005C | R23 | G_Y2 | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20060 | R24 | G_COL | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20064 | R25 | G_OPT | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD20068 | R26 | G_EXE | [Bitmap Graphic Draw](#0xd2004c-0xd20068-bitmap-graphic-draw) |
|0xD2006C | R27 | SKIP0 | [Skip Rendering BG0](#0xd2006c-0xd20078-skip-rendering-a-specific-bg) |
|0xD20070 | R28 | SKIP1 | [Skip Rendering BG1](#0xd2006c-0xd20078-skip-rendering-a-specific-bg) |
|0xD20074 | R29 | SKIP2 | [Skip Rendering BG2](#0xd2006c-0xd20078-skip-rendering-a-specific-bg) |
|0xD20078 | R30 | SKIP3 | [Skip Rendering BG3](#0xd2006c-0xd20078-skip-rendering-a-specific-bg) |
|0xD2007C | R31 | PF_INIT | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) の初期化 |
|0xD20080 | R32 | PF_PTN | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) パターン番号 |
|0xD20084 | R33 | PF_DX | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) diff-X |
|0xD20088 | R34 | PF_DY | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) diff-Y |
|0xD2008C | R35 | PF_WIDTH | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) 幅 |
|0xD20090 | R36 | CP_FR | [Copy Character Pattern (From)](#0xd20090-0xd20094-copy-character-pattern) |
|0xD20094 | R37 | CP_TO | [Copy Character Pattern (To)](#0xd20090-0xd20094-copy-character-pattern) |
|0xD20098 | R38 | TR_ADDR | [Transfer Character Pattern (address)](#0xd20098-0xd200a0-transfer-character-pattern) |
|0xD2009C | R39 | TR_SIZE | [Transfer Character Pattern (size)](#0xd20098-0xd200a0-transfer-character-pattern) |
|0xD200A0 | R40 | TR_TO | [Transfer Character Pattern (pattern)](#0xd20098-0xd200a0-transfer-character-pattern) |
VDP レジスタへのアクセスも常に 4 バイト境界で行ってください。
### 0xD20000: Skip Screen Update
このレジスタに非 0 を設定すると、毎フレーム(60fps)の画面更新をスキップします。
### 0xD20004: Sprite Position
スプライトを表示する BG レイヤーを 0~3 の範囲で指定します。
- 0: BG0 の上、BG1~BG3 の下にスプライトを表示
- 1: BG1 の上、BG2~BG3 の下に表示
- 2: BG2 の上、BG3 の下に表示
- 3: BG3 の上に表示
### 0xD20008-0xD20024: Hardware Scroll
ハードウェアスクロールレジスタは Character Pattern Mode と Bitmap Mode で挙動が異なります。
#### (for Character Pattern Mode)
各 BG 面は 2048x2048 ピクセルの仮想表示領域を持ちます。
BG ごとに SX・SY 座標を 0~2047 の範囲で設定し、左上を表示原点として指定します。
#### (for Bitmap Mode)
- `SX` に正の値を書き込むと右方向に指定ピクセル分スクロールします。
- `SX` に負の値を書き込むと左方向にスクロールします。
- `SY` に正の値を書き込むと下方向にスクロールします。
- `SY` に負の値を書き込むと上方向にスクロールします。
### 0xD20028-0xD20034: Bitmap Mode
各 BG ごとに Character Pattern Mode と Bitmap Mode を切り替えられます。1 を書き込むとビットマップモード、0 でキャラクタパターンモードです。
### 0xD20038-0xD20048: Clear Screen
- R14 (CLSA) に値を書き込むと全 BG を同じ値でクリアします。
- R15~R18 (CLS0~CLS3) に値を書き込むと、対応する BG の表示領域を指定した値で埋めます。
### 0xD2004C-0xD20068: Bitmap Graphic Draw
ビットマップモード用の描画コマンドです。以下を設定して R26 (G_EXE) に命令番号を書き込むことで描画を実行します。
- R19 (G_BG): 描画対象の BG
- R20, R21 (G_X1, G_Y1): 描画開始座標
- R22, R23 (G_X2, G_Y2): 描画終了座標
- R24 (G_COL): 描画色
- R25 (G_OPT): オプション
### 0xD2006C-0xD20078: Skip Rendering a Specific BG
対応する BG の描画をスキップしたい場合、SKIP0~SKIP3 に非 0 を設定してください。
### 0xD2007C-0xD2008C: Proportional Font
プロポーショナルフォント描画のための座標初期化や差分移動量、表示幅を設定します。
### 0xD20090-0xD20094: Copy Character Pattern
特定のパターン番号から特定のパターン番号へのコピーをします。
1. `CP_FR` (0xD20090) にコピー元のパターン番号を設定
2. `CP_TO` (0xD20094) にコピー先のパターン番号を指定
Remarks:
- コピーは `CP_TO` の書き込みがされた時即座に完了する。
### 0xD20098-0xD200A0: Transfer Character Pattern
特定のメモリアドレスから特定のパターン番号への転送をします。
1. `TR_ADDR` (0xD20098) にコピー元のメモリアドレスを設定
2. `TR_SIZE` (0xD2009C) にコピーするサイズ(bytes)を指定
3. `TR_TO` (0xD200A0) で指定したパターン番号へ転送します。
Remarks:
- `TR_ADDR` に指定するメモリアドレスには [Character Pattern Table](#character-pattern) 形式のキャラクタパターン raw データが格納されていなければなりません。
- `TR_SIZE` には 32 の倍数を指定しなければなりません。
- 転送は `TR_TO` の書き込みがされた時即座に完了する。
## I/O Map
VGS-X における I/O は 0xE00000~0xEFFFFF のメモリ領域に 32 ビット値でアクセスすることで実行します。
| Address | In | Out | Description |
|:--------:|:---:|:---:|:------------|
| 0xE00000 | o | - | [V-SYNC](#0xe00000in---v-sync) |
| 0xE00000 | - | o | [Console Output](#0xe00000out---console-output) |
| 0xE00004 | o | o | [Random](#0xe00004io---random) |
| 0xE00008 | - | o | [DMA: Destination](#0xe00008-0xe00014io---direct-memory-access) |
| 0xE0000C | - | o | [DMA: Source](#0xe00008-0xe00014io---direct-memory-access) |
| 0xE00010 | - | o | [DMA: Argument](#0xe00008-0xe00014io---direct-memory-access) |
| 0xE00014 | o | o | [DMA: Execute](#0xe00008-0xe00014io---direct-memory-access) |
| 0xE00100 | - | o | [Angle: X1](#0xe00100-0xe00118io---angle) |
| 0xE00104 | - | o | [Angle: Y1](#0xe00100-0xe00118io---angle) |
| 0xE00108 | - | o | [Angle: X2](#0xe00100-0xe00118io---angle) |
| 0xE0010C | - | o | [Angle: Y2](#0xe00100-0xe00118io---angle) |
| 0xE00110 | o | o | [Angle: Degree (0~359)](#0xe00100-0xe00118io---angle) |
| 0xE00114 | o | - | [Angle: int-sin (-256~256)](#0xe00100-0xe00118io---angle) |
| 0xE00118 | o | - | [Angle: int-cos (-256~256)](#0xe00100-0xe00118io---angle) |
| 0xE01000 | - | o | [Play BGM](#0xe010xxo---background-music-bgm) |
| 0xE01004 | - | o | [BGM Control](#0xe010xxo---background-music-bgm) |
| 0xE01008 | o | o | [BGM Master Volume](#0xe010xxo---background-music-bgm) |
| 0xE01100 | - | o | [Play SFX](#0xe011xxo---sound-effect-sfx) |
| 0xE01104 | - | o | [Stop SFX](#0xe011xxo---sound-effect-sfx) |
| 0xE01108 | o | o | [SFX Master Volume](#0xe011xxo---sound-effect-sfx) |
| 0xE02000 | o | - | [Gamepad: D-pad Up](#0xe020xxi---gamepad) |
| 0xE02004 | o | - | [Gamepad: D-pad Down](#0xe020xxi---gamepad) |
| 0xE02008 | o | - | [Gamepad: D-pad Left](#0xe020xxi---gamepad) |
| 0xE0200C | o | - | [Gamepad: D-pad Right](#0xe020xxi---gamepad) |
| 0xE02010 | o | - | [Gamepad: A button](#0xe020xxi---gamepad) |
| 0xE02014 | o | - | [Gamepad: B button](#0xe020xxi---gamepad) |
| 0xE02018 | o | - | [Gamepad: X button](#0xe020xxi---gamepad) |
| 0xE0201C | o | - | [Gamepad: Y button](#0xe020xxi---gamepad) |
| 0xE02020 | o | - | [Gamepad: Start button](#0xe020xxi---gamepad) |
| 0xE02100 | o | o | [Gamepad: Type](#0xe021xxio---gamepad-types) |
| 0xE02104 | o | - | [Gamepad: Button ID (A)](#0xe021xxio---gamepad-types) |
| 0xE02108 | o | - | [Gamepad: Button ID (B)](#0xe021xxio---gamepad-types) |
| 0xE0210C | o | - | [Gamepad: Button ID (X)](#0xe021xxio---gamepad-types) |
| 0xE02110 | o | - | [Gamepad: Button ID (Y)](#0xe021xxio---gamepad-types) |
| 0xE02114 | o | - | [Gamepad: Button ID (Start)](#0xe021xxio---gamepad-types) |
| 0xE02118 | - | o | [Gamepad: Button Name (ID)](#0xe021xxio---gamepad-types) |
| 0xE0211C | - | o | [Gamepad: Button Name (Address)](#0xe021xxio---gamepad-types) |
| 0xE03000 | o | - | [SaveData: Address](#0xe030xxio---savedata) |
| 0xE03004 | o | o | [SaveData: Execute](#0xe030xxio---savedata) |
| 0xE03008 | - | o | [SaveData: Size](#0xe030xxio---savedata) |
| 0xE03100 | o | - | [Sequential: Open Write](#0xe031xxio---large-sequencial-file-io) |
| 0xE03104 | o | - | [Sequential: Write Byte](#0xe031xxio---large-sequencial-file-io) |
| 0xE03108 | o | - | [Sequential: Commit](#0xe031xxio---large-sequencial-file-io) |
| 0xE03110 | o | - | [Sequential: Open Read](#0xe031xxio---large-sequencial-file-io) |
| 0xE03114 | - | o | [Sequential: Read Byte](#0xe031xxio---large-sequencial-file-io) |
| 0xE04000 | o | - | [UTC: Year](#0xe040xxin---calendar)|
| 0xE04004 | o | - | [UTC: Month](#0xe040xxin---calendar)|
| 0xE04008 | o | - | [UTC: Day of Month](#0xe040xxin---calendar)|
| 0xE0400C | o | - | [UTC: Hour](#0xe040xxin---calendar)|
| 0xE04010 | o | - | [UTC: Minute](#0xe040xxin---calendar)|
| 0xE04014 | o | - | [UTC: Second](#0xe040xxin---calendar)|
| 0xE04020 | o | - | [Local: Year](#0xe040xxin---calendar)|
| 0xE04024 | o | - | [Local: Month](#0xe040xxin---calendar)|
| 0xE04028 | o | - | [Local: Day of Month](#0xe040xxin---calendar)|
| 0xE0402C | o | - | [Local: Hour](#0xe040xxin---calendar)|
| 0xE04030 | o | - | [Local: Minute](#0xe040xxin---calendar)|
| 0xE04034 | o | - | [Local: Second](#0xe040xxin---calendar)|
| 0xE05004 | o | o | [Mouse: Hidden](#0xe050xxio---mouse) |
| 0xE05008 | o | - | [Mouse: Moving](#0xe050xxio---mouse) |
| 0xE0500C | o | - | [Mouse: X](#0xe050xxio---mouse) |
| 0xE05010 | o | - | [Mouse: Y](#0xe050xxio---mouse) |
| 0xE05014 | o | o | [Mouse: Cursor Pattern](#0xe050xxio---mouse) |
| 0xE05018 | o | o | [Mouse: Cursor Palette](#0xe050xxio---mouse) |
| 0xE0501C | o | - | [Mouse: Scroll (Vertical)](#0xe050xxio---mouse) |
| 0xE05020 | o | - | [Mouse: Scroll (Horizontal)](#0xe050xxio---mouse) |
| 0xE05100 | o | - | [Mouse: Left Button](#0xe050xxio---mouse) |
| 0xE05104 | o | - | [Mouse: Left Click](#0xe050xxio---mouse) |
| 0xE05108 | o | - | [Mouse: Left Click X](#0xe050xxio---mouse) |
| 0xE0510C | o | - | [Mouse: Left Click Y](#0xe050xxio---mouse) |
| 0xE05200 | o | - | [Mouse: Right Button](#0xe050xxio---mouse) |
| 0xE05204 | o | - | [Mouse: Right Click](#0xe050xxio---mouse) |
| 0xE05208 | o | - | [Mouse: Right Click X](#0xe050xxio---mouse) |
| 0xE0520C | o | - | [Mouse: Right Click Y](#0xe050xxio---mouse) |
| 0xE06000 | o | - | [YM2612: Channel 0 Frequency](#0xe06xxx---ym2612) |
| 0xE06004 | o | - | [YM2612: Channel 1 Frequency](#0xe06xxx---ym2612) |
| 0xE06008 | o | - | [YM2612: Channel 2 Frequency](#0xe06xxx---ym2612) |
| 0xE0600C | o | - | [YM2612: Channel 3 Frequency](#0xe06xxx---ym2612) |
| 0xE06010 | o | - | [YM2612: Channel 4 Frequency](#0xe06xxx---ym2612) |
| 0xE06014 | o | - | [YM2612: Channel 5 Frequency](#0xe06xxx---ym2612) |
| 0xE06100 | o | - | [YM2612: Channel 0 Volume](#0xe06xxx---ym2612) |
| 0xE06104 | o | - | [YM2612: Channel 1 Volume](#0xe06xxx---ym2612) |
| 0xE06108 | o | - | [YM2612: Channel 2 Volume](#0xe06xxx---ym2612) |
| 0xE0610C | o | - | [YM2612: Channel 3 Volume](#0xe06xxx---ym2612) |
| 0xE06110 | o | - | [YM2612: Channel 4 Volume](#0xe06xxx---ym2612) |
| 0xE06114 | o | - | [YM2612: Channel 5 Volume](#0xe06xxx---ym2612) |
| 0xE06200 | o | o | [YM2612: Channel 0 Mute](#0xe06xxx---ym2612) |
| 0xE06204 | o | o | [YM2612: Channel 1 Mute](#0xe06xxx---ym2612) |
| 0xE06208 | o | o | [YM2612: Channel 2 Mute](#0xe06xxx---ym2612) |
| 0xE0620C | o | o | [YM2612: Channel 3 Mute](#0xe06xxx---ym2612) |
| 0xE06210 | o | o | [YM2612: Channel 4 Mute](#0xe06xxx---ym2612) |
| 0xE06214 | o | o | [YM2612: Channel 5 Mute](#0xe06xxx---ym2612) |
| 0xE07000 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07004 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07008 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE0700C | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07010 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07014 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07018 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE0701C | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07020 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE07024 | o | - | [Debug Switch](#0xe070xx---debug-switch) |
| 0xE7FFF4 | o | - | [Abort](#0xe7fff4out---abort) |
| 0xE7FFF8 | - | o | [Reset](#0xe7fff8out---reset) |
| 0xE7FFFC | - | o | [Exit](#0xe7fffcout---exit) |
| 0xE80000 ~ 0xE8FFFC | o | o | [User-Defined I/O](#0xe8xxxxio---user-defined-io) |
### 0xE00000[in] - V-SYNC
MC68030 プログラムが 0xE00000 を読み込むと、VGS-X はその時点の VRAM を参照して BG0~BG3 とスプライトを描画し、60fps で同期を取った後に戻ります。
```c
// VRAM の更新処理
drawProc();
// 垂直同期を待つ(内部的に 0xE00000 へ入力)
vgs_vsync();
// 同期完了後の処理
afterDrawProc();
```
`vgs_vsync` 関数は [vgs.h](./lib/vgs.h) に定義されています。
> __設計方針__: この仕様により VGS-X の MC68030 には動作クロックという概念がありません。実行速度はホスト PC の性能に依存し、必要スペックの周知は開発者に委ねられます。
### 0xE00000[out] - Console Output
0xE00000 に値を書き込むとコンソールに文字列を出力できます。デバッグログなどに利用してください。
```c
vgs_print("Hello, World!\n");
```
`vgs_print` は [log.h](./lib/log.h) に定義されています。
### 0xE00004[i/o] - Random
- 0xE00004 に書き込むと乱数シードを設定できます。
- 0xE00004 を読み出すと 0~65535 の乱数を取得できます。
- 同一のシードであれば結果は決定的で、65,536 回の読み出しで周期的に繰り返します。
### 0xE00008-0xE00014[io] - Direct Memory Access
DMA を用いて高速にメモリ転送を行えます。
#### DMA Copy
- `Source` で転送元アドレスを指定します。
- `Destination` で転送先アドレスを指定します。
- `Argument` で転送バイト数を指定します。
- `Command` に 0 を書き込むと実行します。
備考:
- `Source` と `Destination` はプログラム領域(0x000000~プログラムサイズ)または RAM(0xF00000~0xFFFFFF)である必要があります。
- 転送元・転送先が RAM の場合、範囲が重なっていても安全に転送されます(`memmove` 相当)。
- 無効なアドレス範囲を指定すると DMA は実行されません。
#### DMA Set
`Source` の下位 8 ビットに指定した値で、`Destination` から `Argument` バイト分を埋めます。
備考:
- `Source` の上位 24 ビットは無視されます。
- `Destination` は RAM(0xF00000~0xFFFFFF)である必要があります。
- 無効なアドレス範囲を指定すると DMA は実行されません。
#### DMA UTF8 to SJIS String
`Source` に設定した UTF-8 文字列(終端 0)を SJIS に変換しながら `Destination` にコピーします。
- `Source` はプログラム領域または RAM である必要があります。
- `Destination` は RAM である必要があります。
#### DMA UTF8 to SJIS Character
UTF-8 の 1 文字を SJIS に変換します。`Source` に 1 文字分の UTF-8 データ、`Destination` に 2 バイト以上の RAM を指定し、結果を格納します。
### 0xE00100-0xE00118[io] - Angle
二点 (X1, Y1) と (X2, Y2) の角度(0~359 度)を高速に算出します。
```c
VGS_OUT_ANGLE_X1 = x1;
VGS_OUT_ANGLE_Y1 = y1;
VGS_OUT_ANGLE_X2 = x2;
VGS_OUT_ANGLE_Y2 = y2;
int32_t degree = VGS_IO_ANGLE_DEGREE;
```
`VGS_IO_ANGLE_DEGREE` を読み書きすると、その値を内部レジスタに保持します。続けて 0xE00114 を読むと整数サイン、0xE00118 を読むと整数コサインが取得できます。
```c
int32_t s = VGS_IN_ANGLE_SIN;
int32_t c = VGS_IN_ANGLE_COS;
```
通常の `sin` / `cos` 関数が -1.0~1.0 の倍精度を返すのに対し、VGS の整数版は -256~256 を返します。小数 8 ビットの固定小数点演算として扱うと便利です。
角度とサイン・コサインの関係は以下を参照してください。

[OAM](#oam-object-attribute-memory) の `[rotate](#rotate-of-sprite)` で指定する角度と [Angle](#0xe00100-0xe00118io---angle) の角度を一致させるため、スプライトのキャラクタパターンは右向きに描いておくと扱いやすくなります。

具体的な実装例は [./example/03_rotate/program.c](./example/03_rotate/program.c) を参照してください。

### 0xE010xx[o] - Background Music (BGM)
- 0xE01000 に VGM インデックスを設定すると BGM を再生します。
- 0xE01004 に 0 を書くと一時停止、1 を書くと再開、2 を書くとフェードアウトします。
- 0xE01008 で BGM マスターボリュームを設定・取得できます(0=0%、255=100%、既定値 255)。
VGS-X は YM2612(OPN2) と互換性のある VGM データを再生できます。データ作成には [Furnace Tracker](https://github.com/tildearrow/furnace) の利用を推奨します。
### 0xE011xx[o] - Sound Effect (SFX)
- 0xE01100 に .wav インデックスを書き込むと効果音を再生します。
- 0xE01104 にインデックスを書き込むと該当 SFX を停止します。
- 0xE01108 で SFX マスターボリュームを設定・取得できます(0=0%、255=100%、既定値 255)。
VGS-X の ROM カートリッジには 256 個までの .wav(44.1kHz / 16bit / ステレオ)を格納できます。
> VGS-Zero とほぼ同等の機能ですが、チャンネル数が異なります(VGS-Zero: 1ch、VGS-X: 2ch)。
以下のように `ffmpeg` で変換すると対応フォーマットの .wav を作成できます。
```bash
ffmpeg -i input.mp3 -acodec pcm_s16le -ar 44100 -ac 2 sfx.wav
```
### 0xE020xx[i] - Gamepad
VGS-X は下図のような D-Pad、ABXY、Start の入力を取得できます。

- ボタンが押されると対応する 0xE020xx の値が非 0 になります。
- カーソルキーと左スティックは常に連動します。
- `A` ボタンは決定、`B` ボタンはキャンセル、`X` ボタンは連打用途、`Y` ボタンは補助操作、`Start` ボタンはメニューやスタート用を想定しています。
代表的なゲームパッドとの対応は次の通りです。
| VGS-X and XBOX | PC Keybord | Switch | PlayStation |
|:-:|:-:|:-:|:-:|
| `A` | `Z` | `A` | `Cross` |
| `B` | `X` | `B` | `Circle` |
| `X` | `A` | `X` | `Square` |
| `Y` | `S` | `Y` | `Triangle` |
| `Start` | `Space` | `Plus` | `Options` |
> Switch のゲームパッド(Proコン)を用いる場合、A/B と X/Y の並び順が標準(XBOX)とは逆になる仕様です。
### 0xE021xx[io] - Gamepad Types
PC キーボード、Xbox、PlayStation、Nintendo Switch のいずれかの入力をサポートします。
0xE02100 を読み取ると現在接続されているゲームパッドの種別が取得できます。
```c
uint32_t gamepadType = VGS_KEY_TYPE;
switch (gamepadType) {
case VGS_KEY_ID_KEYBOARD: vgs_putlog("Keyboard connected!"); break;
case VGS_KEY_ID_XBOX: vgs_putlog("XBOX gamepad connected!"); break;
case VGS_KEY_ID_SWITCH: vgs_putlog("Switch gamepad connected!"); break;
case VGS_KEY_ID_PS: vgs_putlog("PlayStation gamepad connected!"); break;
default: vgs_putlog("Unknown gamepad connected!");
}
```
| Key Identifer | `#define` name |
|:-------------:|:---------------|
| 0 | VGS_KEY_ID_UNKNOWN |
| 1 | VGS_KEY_ID_KEYBOARD |
| 2 | VGS_KEY_ID_XBOX |
| 3 | VGS_KEY_ID_SWITCH |
| 4 | VGS_KEY_ID_PS |
> デバッグ目的に限り、0xE02100 にキー識別子を書き込んで接続種別を上書きできます。
0xE02104 ~ 0xE02114 を読むと、接続中のゲームパッドにおける ABXY/Start ボタンの識別子を取得できます。
| Button Identifer | `#define` name | Button Name String |
|:----------------:|:---------------|:-------------------|
| 0 | VGS_BUTTON_ID_UNKNOWN | `"UNKNOWN"` |
| 1 | VGS_BUTTON_ID_A | `"A"` |
| 2 | VGS_BUTTON_ID_B | `"B"` |
| 3 | VGS_BUTTON_ID_X | `"X"` |
| 4 | VGS_BUTTON_ID_Y | `"Y"` |
| 5 | VGS_BUTTON_ID_Z | `"Z"` |
| 6 | VGS_BUTTON_ID_S | `"S"` |
| 7 | VGS_BUTTON_ID_CROSS | `"CROSS"` |
| 8 | VGS_BUTTON_ID_CIRCLE | `"CIRCLE"` |
| 9 | VGS_BUTTON_ID_TRIANGLE | `"TRIANGLE"` |
| 10 | VGS_BUTTON_ID_SQUARE | `"SQUARE"` |
| 11 | VGS_BUTTON_ID_START | `"START"` |
| 12 | VGS_BUTTON_ID_SPACE | `"SPACE"` |
| 13 | VGS_BUTTON_ID_PLUS | `"+"` |
| 14 | VGS_BUTTON_ID_OPTIONS | `"OPTIONS"` |
0xE02108 にボタン ID を設定し、0xE0211C に 12 バイト以上の RAM アドレスを指定すると、ボタン名の文字列を格納できます。
```c
char buf[12];
VGS_OUT_KEY_BUTTON_ID = 14;
VGS_OUT_KEY_BUTTON_NAME = (uint32_t)buf;
// buf には "OPTIONS\\0" が格納されます
```
### 0xE030xx[io] - SaveData
このインタフェースは、プログラムが **RAM 上のデータを永続ストレージ(`save.dat`)へ保存** したり、**過去に保存したデータを RAM に読み戻す** ことを可能にします。
出力(書き込み)としてアクセスするか、入力(読み出し)としてアクセスするかによって、同一の I/O アドレスで保存/読み込みの両方を扱います。
#### Usage
```c
VGS_OUT_SAVE_ADDRESS = (uint32_t)&mydata; // 保存/読み込みの基準となるRAMアドレス
VGS_IO_SAVE_EXECUTE = sizeof(mydata); // 保存: RAM → save.dat へ書き込み
uint32_t size = VGS_IO_SAVE_EXECUTE; // 読み込み: save.dat → RAM へ読み込み
```
- VGS_IO_SAVE_EXECUTE に値を書き込むと、そのバイト数分のデータを RAM から save.dat に保存します。
- VGS_IO_SAVE_EXECUTE を読み出すと、save.dat から RAM にデータを読み込み、実際に読み込まれたバイト数を返します。
#### Remarks
- VGS_OUT_SAVE_ADDRESS は RAM 範囲 0xF00000〜0xFFFFFF(24-bit アドレス空間)内のアドレスを指定しなければなりません。
- 保存(save)時は、指定サイズが 1〜0x100000 bytes の範囲である必要があり、かつ対象のアドレス範囲が RAM の上限を超えてはなりません。
- 読み込み(load)時は、save.dat が存在しない、サイズが不正、または正常に読み込めない場合、読み込み結果は 0 になります。
- 読み込み操作が返す値は、RAM に読み込まれたバイト数を表します。
- このインタフェースはセーブデータ内容の整合性検証(ハッシュ等)は行いません。確認するのはファイルの存在、サイズの妥当性、および読み込み成功可否のみです。
### 0xE031xx[io] - Large Sequencial File I/O
このインタフェースは、**バイト単位のシーケンシャルファイル I/O** を提供します。
主に **入力ログやリプレイデータなどの中規模データ** を記録・再生する用途を想定しています。
すべてのデータは **固定サイズの内部メモリバッファ** に蓄積され、
ファイルへの書き込みおよび読み込みは **一括処理** として行われます。
本インタフェースは **大容量データやストリーミング用途を目的としていません**。
#### File Model
* **最大 256 個**のシーケンシャルファイルを使用できます。
* ファイルは **8bit インデックス(0–255)** で識別されます。
* 各ファイルは保存ディレクトリ内に
`saveNNN.dat`(`save000.dat` ~ `save255.dat`)として保存されます。
#### Write Operation
```c
VGS_IO_SEQ_OPEN_W = index; // 書き込み対象ファイルを選択 (0–255)
VGS_IO_SEQ_WRITE = value; // 内部バッファへ 1 byte 書き込み
VGS_IO_SEQ_COMMIT = 0; // バッファ内容を saveNNN.dat へ書き込み
```
1. `VGS_IO_SEQ_OPEN_W` により、書き込み対象のシーケンシャルファイルを選択します。
2. `VGS_IO_SEQ_WRITE` への書き込みごとに、**1 byte** が内部バッファへ追加されます。
3. `VGS_IO_SEQ_COMMIT` を実行すると、内部バッファの内容がファイルに書き込まれます。
同一インデックスの既存ファイルが存在する場合、**上書きされます**。
#### Read Operation
```c
VGS_IO_SEQ_OPEN_R = index; // 読み込み対象ファイルを選択 (0–255)
uint32_t value = VGS_IO_SEQ_READ; // 次の 1 byte を読み出し
```
1. `VGS_IO_SEQ_OPEN_R` により、対象ファイルの内容が **すべて内部バッファへ読み込まれます**。
2. `VGS_IO_SEQ_READ` は、内部バッファから **1 byte ずつ順番に**返します。
3. バッファ内のデータをすべて読み終えると、`VGS_IO_SEQ_READ` は
**`0xFFFFFFFF`** を返します。
#### Buffer Size and Limitations
* シーケンシャルファイルの最大サイズは、
**約 64KB の固定サイズ内部バッファ** によって制限されます。
* 書き込み時に内部バッファが満杯になった場合:
* 以降の `VGS_IO_SEQ_WRITE` は **黙って無視されます**。
* **ストリーミング I/O、分割書き込み、逐次フラッシュ**には対応していません。
* 実際に記録できる最大データ量は、この内部バッファサイズに依存します。
#### Remarks
* 本インタフェースは **バッファリングされたファイル I/O** のみを提供します。
* 大容量データや長時間記録には適していません。
* ファイル内容に対する **整合性チェック(チェックサムやバージョン管理等)** は行われません。
* 本設計は、拡張性よりも **単純性と予測可能な挙動** を優先しています。
### 0xE040xx[in] - Calendar
現在の日付と時刻を取得できます。
協定世界時(UTC):
- 0xE04000: Year (例: 2025)
- 0xE04004: Month (1 to 12)
- 0xE04008: Day of Month (1 to 31)
- 0xE0400C: Hour (0 to 23)
- 0xE04010: Minute (0 to 59)
- 0xE04014: Second (0 to 59)
ローカル・タイムゾーン:
- 0xE04020: Year (例: 2025)
- 0xE04024: Month (1 to 12)
- 0xE04028: Day of Month (1 to 31)
- 0xE0402C: Hour (0 to 23)
- 0xE04030: Minute (0 to 59)
- 0xE04034: Second (0 to 59)
### 0xE050xx[i/o] - Mouse
マウスインタフェースは、ポインタの現在状態を画面座標系(`320x200`)で提供し、VGS-X カーソルの制御も行えます。
| Address | In | Out | 説明 |
|:-------:|:--:|:---:|:-----|
| 0xE05000 | Enabled | Enabled | マウス入力の有効フラグ(`0`: 無効、非 0: 有効) |
| 0xE05004 | Hidden | Hidden | VGS-X カーソルの非表示フラグ(`0`: 表示、非 0: 非表示) |
| 0xE05008 | Moving | - | 現フレームでマウスが移動したか |
| 0xE0500C | X | - | 現在のマウス X 座標 |
| 0xE05010 | Y | - | 現在のマウス Y 座標 |
| 0xE05014 | Cursor Pattern | Cursor Pattern | カーソルの基底キャラクタパターン番号 |
| 0xE05018 | Cursor Palette | Cursor Palette | カーソルのパレット番号(0~1023) |
| 0xE0501C | -256 〜 255 | - | 縦スクロール |
| 0xE05020 | -256 〜 255 | - | 横スクロール |
| 0xE05100 | Left Button | - | 左ボタンの押下状態 |
| 0xE05104 | Left Click | - | 現フレームで左クリックが発生したか |
| 0xE05108 | Left Click X | - | 左クリック開始位置の X 座標 |
| 0xE0510C | Left Click Y | - | 左クリック開始位置の Y 座標 |
| 0xE05200 | Right Button | - | 右ボタンの押下状態 |
| 0xE05204 | Right Click | - | 現フレームで右クリックが発生したか |
| 0xE05208 | Right Click X | - | 右クリック開始位置の X 座標 |
| 0xE0520C | Right Click Y | - | 右クリック開始位置の Y 座標 |
備考:
- マウスを有効にするには VGS-X 本体側でマウスが有効化されていなければなりません。
- ポインタが表示画面外にある場合、X と Y は `-1` を返します。
- `Moving`、`Left Click`、`Right Click` はフレーム単位の状態です。
- カーソルパターンレジスタは基底パターン番号を指定します。16x16 のカーソルは連続する 4 パターンを消費します。
- hidden フラグが制御するのは VGS-X のカーソルのみです。ホストランタイム側は OS カーソルを独立に制御する場合があります。
例:
```c
vgs_mouse_setup(128, 0);
vgs_mouse_enabled(ON);
vgs_mouse_hidden(OFF);
if (vgs_mouse_left_clicked(&x, &y)) {
// クリック処理
}
```
### 0xE06xxx - YM2612
各チャンネルの現在の YM2612 再生状態を読み取ることができます。
- `0xE06000` ~ `0xE06014`: チャンネル 0 ~ 5 の周波数値 (入力専用)
- `0xE06100` ~ `0xE06114`: チャンネル 0 ~ 5 の音量値 (入力専用)
- `0xE06200` ~ `0xE06214`: チャンネル 0 ~ 5 のミュート (入出力)
備考:
- 周波数値は、おおよその生の YM2612 `block/fnum` 値です。
- 音量値は、各チャンネルの現在の出力振幅のおおよその値です。
### 0xE070xx - Debug Switch
デバッグ用に利用できるスイッチの Push 情報を取得できます。
- 0xE07000 : スイッチ `0`
- 0xE07004 : スイッチ `1`
- 0xE07008 : スイッチ `2`
- 0xE0700C : スイッチ `3`
- 0xE07010 : スイッチ `4`
- 0xE07014 : スイッチ `5`
- 0xE07018 : スイッチ `6`
- 0xE0701C : スイッチ `7`
- 0xE07020 : スイッチ `8`
- 0xE07024 : スイッチ `9`
備考:
- スイッチの入力は PC キーボードの `0` 〜 `9` キーで行います。
- スイッチは押し込まれた瞬間フレームのみ not 0 になります。
### 0xE7FFF4[out] - Abort
スタックバックトレースを表示してプログラムを異常終了させます。
なお、コンパイルオプションで最適化 (`-O`) を指定した場合は正常にバックトレースが拾えないことがあります。
**本機能を利用する場合は一時的に最適化を無効にしてください。**
最適化無効で Abort した時の出力例:
```
[error] Stack trace (FP=0xFFFF74):
[error] #0: 0x001A78
[error] #1: 0x001BDC
```
> VGS-X のプログラムは初期エントリ `crt0` から `main` がコールされていることが分かります。
### 0xE7FFF8[out] - Reset
VGS-X にリセット要求を送ります。
### 0xE7FFFC[out] - Exit
VGS-X に終了要求を送ります。デバッグ用 SDL2 エミュレータでは、ここに書き込んだ値がプロセスの終了コードになります。
### 0xE8XXXX[i/o] - User-Defined I/O
0xE80000~0xE8FFFC のアドレス範囲はユーザー定義 I/O として利用できます。具体的な利用方法は [Runtime Implementation Guide](#runtime-implementation-guide) の [User-Defined I/O](#6-user-defined-io) を参照してください。
# VGS Standard Library
VGS Standard Library(Video Game System Standard Library)は、VGS-X と将来の VGS シリーズ間でユーザープログラムのソース互換性を最大限維持できるよう設計(標準化)された C 言語ライブラリ仕様です。典型的な 2D ゲームを制作する際に必要となる機能を網羅的に提供することを方針としています。
この README.md に記載された VGS-X のハードウェア機能は、すべて本ライブラリを経由して C 言語で記述したゲームプログラムから利用できます。
## Static Libraries
| Library | Header File | Desctiption |
|:--------|:------------|:------------|
| [libc.a](#libca---basic-function) (`-lc`) | [vgs.h](./lib/vgs.h) | 基本機能を提供します |
| [liblog.a](#libloga---logging-function) (`-llog`) | [log.h](./lib/log.h) | ログ出力用の補助ライブラリ |
各関数の仕様はヘッダファイルに Doxygen 形式で記述されているため、Visual Studio Code などのエディタで適切な C/C++ プラグインを用いれば、関数名入力時に概要を参照できます。
## libc.a - Basic Function
`libc.a` は VGS シリーズ向けゲーム開発を補助する API をまとめた C ライブラリです。`-lc` が暗黙にリンクされるため、リンクオプションで明示的に指定する必要はありません。
基本機能は [Video Game Functions](#video-game-functions) と [Standard Functions](#standard-functions) に分類され、いずれも [vgs.h](./lib/vgs.h) をインクルードするだけで利用できます。
```c
#include
```
### (Video Game Functions)
| Category | Function | Description |
|:---------|:---------|:------------|
| system | `vgs_abort` | スタックバックトレースを出力して [Abort](#0xe7fff4out---abort) |
| system | `vgs_vsync` | 60fps の [V-SYNC](#0xe00000in---v-sync) と同期する |
| system | `vgs_user_in` | [User-Defined I/O](#0xe8xxxxio---user-defined-io) を入力する |
| system | `vgs_user_out` | [User-Defined I/O](#0xe8xxxxio---user-defined-io) を出力する |
| cg | `vgs_ptn_copy` | [Copy Character Pattern](#0xd20090-0xd20094-copy-character-pattern) を実行する |
| cg | `vgs_ptn_transfer` | [Transfer Character Pattern](#0xd20098-0xd200a0-transfer-character-pattern) を実行する |
| cg | `vgs_pal_get` | [Palette](#palette) から色コードを取得する |
| cg | `vgs_pal_set` | [Palette](#palette) に色コードを設定する |
| cg:bg | `vgs_bg_width` | [Character Pattern Mode](#0xd20028-0xd20034-bitmap-mode) における [Name Table](#name-table) の幅を取得する |
| cg:bg | `vgs_bg_height` | [Character Pattern Mode](#0xd20028-0xd20034-bitmap-mode) における [Name Table](#name-table) の高さを取得する |
| cg:bg | `vgs_chr_width` | 表示領域としての [Name Table](#name-table) の幅を取得する |
| cg:bg | `vgs_chr_height` | 表示領域としての [Name Table](#name-table) の高さを取得する |
| cg:bg | `vgs_put_bg` | [Character Pattern Mode](#0xd20028-0xd20034-bitmap-mode) の [BG](#name-table) に文字を描画する |
| cg:bg | `vgs_print_bg` | [Character Pattern Mode](#0xd20028-0xd20034-bitmap-mode) の [BG](#name-table) に文字列を描画する |
| cg:bg | `vgs_cls_bg_all` | 全 BG を [クリア](#0xd20038-0xd20048-clear-screen) する |
| cg:bg | `vgs_cls_bg` | 指定した BG を [クリア](#0xd20038-0xd20048-clear-screen) する |
| cg:bmp | `vgs_draw_mode` | BG の描画モードを [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) とキャラクタパターンで切り替える |
| cg:bmp | `vgs_draw_window` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) で表示領域を限定する [window](#0xd2004c-0xd20068-bitmap-graphic-draw) を設定する |
| cg:bmp | `vgs_read_pixel` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG から [pixel](#0xd2004c-0xd20068-bitmap-graphic-draw) を取得する |
| cg:bmp | `vgs_draw_pixel` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に [pixel](#0xd2004c-0xd20068-bitmap-graphic-draw) を描画する |
| cg:bmp | `vgs_draw_line` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に [line](#0xd2004c-0xd20068-bitmap-graphic-draw) を描画する |
| cg:bmp | `vgs_draw_lineH` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に水平 [line](#0xd2004c-0xd20068-bitmap-graphic-draw) を描く |
| cg:bmp | `vgs_draw_lineV` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に垂直 [line](#0xd2004c-0xd20068-bitmap-graphic-draw) を描く |
| cg:bmp | `vgs_draw_box` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に [rectangle](#0xd2004c-0xd20068-bitmap-graphic-draw) を描く |
| cg:bmp | `vgs_draw_boxf` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に塗りつぶし矩形を描く |
| cg:bmp | `vgs_draw_clear` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG で指定矩形を 0 クリアする |
| cg:bmp | `vgs_draw_character` | [Bitmap Mode](#0xd20028-0xd20034-bitmap-mode) の BG に [character-pattern](#character-pattern) を描く |
| cg:bg+bmp | `vgs_skip_bg` | [特定の BG の描画をスキップ](#0xd2006c-0xd20078-skip-rendering-a-specific-bg) する |
| cg:bg+bmp | `vgs_scroll` | BG を [スクロール](#0xd20008-0xd20024-hardware-scroll) する |
| cg:bg+bmp | `vgs_scroll_x` | BG を X 方向に [スクロール](#0xd20008-0xd20024-hardware-scroll) する |
| cg:bg+bmp | `vgs_scroll_y` | BG を Y 方向に [スクロール](#0xd20008-0xd20024-hardware-scroll) する |
| cg:sp | `vgs_sprite_priority` | スプライトの表示優先度を設定する |
| cg:sp | `vgs_sprite` | [OAM](#oam-object-attribute-memory) をまとめて設定する |
| cg:sp | `vgs_sprite_hide_all` | すべてのスプライトを非表示にする |
| cg:sp | `vgs_oam` | [OAM](#oam-object-attribute-memory) レコードを取得する |
| cg:sp | `vgs_sprite_alpha8` | スプライトのアルファ値を8bitで設定する |
| bmpfont | `vgs_pfont_init` | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) を初期化する |
| bmpfont | `vgs_pfont_get` | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) 情報を取得する |
| bmpfont | `vgs_pfont_set` | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) 情報を設定する |
| bmpfont | `vgs_pfont_print` | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) で文字列を描画する |
| bmpfont | `vgs_pfont_strlen` | [Proportional Font](#0xd2007c-0xd2008c-Proportional-font) で描画した文字列の幅を取得する |
| bmpfont | `vgs_k8x12_print` | [k8x12 フォント](#0xd2004c-0xd20068-bitmap-graphic-draw) で文字列を描画する |
| bgm | `vgs_bgm_master_volume` | [BGM](#0xe010xxo---background-music-bgm) のマスターボリュームを設定する |
| bgm | `vgs_bgm_master_volume_get` | [BGM](#0xe010xxo---background-music-bgm) のマスターボリュームを取得する |
| bgm | `vgs_bgm_play` | [BGM](#0xe010xxo---background-music-bgm) を再生する |
| bgm | `vgs_bgm_pause` | [BGM](#0xe010xxo---background-music-bgm) を一時停止する |
| bgm | `vgs_bgm_resume` | [BGM](#0xe010xxo---background-music-bgm) を再開する |
| bgm | `vgs_bgm_fadeout` | [BGM](#0xe010xxo---background-music-bgm) をフェードアウトする |
| sfx | `vgs_sfx_master_volume` | [SFX](#0xe011xxo---sound-effect-sfx) のマスターボリュームを設定する |
| sfx | `vgs_sfx_master_volume_get` | [SFX](#0xe011xxo---sound-effect-sfx) のマスターボリュームを取得する |
| sfx | `vgs_sfx_play` | [SFX](#0xe011xxo---sound-effect-sfx) を再生する |
| sfx | `vgs_sfx_stop` | [SFX](#0xe011xxo---sound-effect-sfx) を停止する |
| sfx | `vgs_sfx_stop_all` | すべての [SFX](#0xe011xxo---sound-effect-sfx) を停止する |
| gamepad | `vgs_key_up` | 方向キー上が押されているか確認する |
| gamepad | `vgs_key_down` | 方向キー下が押されているか確認する |
| gamepad | `vgs_key_left` | 方向キー左が押されているか確認する |
| gamepad | `vgs_key_right` | 方向キー右が押されているか確認する |
| gamepad | `vgs_key_a` | A ボタンが押されているか確認する |
| gamepad | `vgs_key_b` | B ボタンが押されているか確認する |
| gamepad | `vgs_key_x` | X ボタンが押されているか確認する |
| gamepad | `vgs_key_y` | Y ボタンが押されているか確認する |
| gamepad | `vgs_key_code` | 方向キーと ABXY ボタンの状態を `uint8_t` コードで取得する |
| gamepad | `vgs_key_code_up` | キーコードで方向キー上を確認する |
| gamepad | `vgs_key_code_down` | キーコードで方向キー下を確認する |
| gamepad | `vgs_key_code_left` | キーコードで方向キー左を確認する |
| gamepad | `vgs_key_code_right` | キーコードで方向キー右を確認する |
| gamepad | `vgs_key_code_a` | キーコードで A ボタンを確認する |
| gamepad | `vgs_key_code_b` | キーコードで B ボタンを確認する |
| gamepad | `vgs_key_code_x` | キーコードで X ボタンを確認する |
| gamepad | `vgs_key_code_y` | キーコードで Y ボタンを確認する |
| gamepad | `vgs_key_type` | 接続されている [Gamepad Type](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_id_a` | 接続中パッドの A ボタンの [Button ID](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_id_b` | 接続中パッドの B ボタンの [Button ID](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_id_x` | 接続中パッドの X ボタンの [Button ID](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_id_y` | 接続中パッドの Y ボタンの [Button ID](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_id_start` | 接続中パッドの Start ボタンの [Button ID](#0xe021xxio---gamepad-types) を取得する |
| gamepad | `vgs_button_name` | ボタン識別子に対応する名称文字列を取得する |
| mouse | `vgs_mouse_setup` | マウスカーソルの [pattern](#0xe050xxio---mouse) と palette を設定する |
| mouse | `vgs_mouse_enabled` | [mouse](#0xe050xxio---mouse) を有効または無効にする |
| mouse | `vgs_mouse_hidden` | VGS-X の [mouse cursor](#0xe050xxio---mouse) を表示または非表示にする |
| mouse | `vgs_mouse_moving` | 現フレームで [mouse](#0xe050xxio---mouse) が移動したか確認する |
| mouse | `vgs_mouse_x` | 現在のマウス X 座標を取得する |
| mouse | `vgs_mouse_y` | 現在のマウス Y 座標を取得する |
| mouse | `vgs_mouse_left` | 左ボタンの現在の押下状態を取得する |
| mouse | `vgs_mouse_right` | 右ボタンの現在の押下状態を取得する |
| mouse | `vgs_mouse_left_clicked` | 左クリック発生の確認とクリック座標の取得を行う |
| mouse | `vgs_mouse_right_clicked` | 右クリック発生の確認とクリック座標の取得を行う |
| save | `vgs_save` | [SaveData](#0xe030xxio---savedata) を保存する |
| save | `vgs_load` | [SaveData](#0xe030xxio---savedata) を読み込む |
| save | `vgs_save_check` | [SaveData](#0xe030xxio---savedata) のサイズを確認する |
| save | `vgs_seq_open_w` | [Large Sequencial File](#0xe031xxio---large-sequencial-file-io) を書き込み用に開く |
| save | `vgs_seq_write` | [Large Sequencial File](#0xe031xxio---large-sequencial-file-io) に 1 バイト書き込む |
| save | `vgs_seq_commit` | [Large Sequencial File](#0xe031xxio---large-sequencial-file-io) をコミットする |
| save | `vgs_seq_open_r` | [Large Sequencial File](#0xe031xxio---large-sequencial-file-io) を読み込み用に開く |
| save | `vgs_seq_read` | [Large Sequencial File](#0xe031xxio---large-sequencial-file-io) から 1 バイト読み込む |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_year` | 現在の年 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_month` | 現在の次 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_mday` | 現在の日 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_hour` | 現在の時間 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_minute` | 現在の分 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_utc_second` | 現在の秒 (UTC) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_year` | 現在の年 (TZ) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_month` | 現在の次 (TZ) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_mday` | 現在の日 (TZ) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_hour` | 現在の時間 (TZ) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_minute` | 現在の分 (TZ) を取得 |
| [calendar](#0xe040xxin---calendar) | `vgs_local_second` | 現在の秒 (TZ) を取得 |
### (Standard Functions)
| Category | Function | Description |
|:---------|:---------|:------------|
| stdlib | `vgs_rand` | 16 ビットの [乱数](#0xe00004io---random) を取得する |
| stdlib | `vgs_rand32` | 32 ビットの [乱数](#0xe00004io---random) を取得する |
| stdlib | `vgs_srand` | [乱数](#0xe00004io---random) のシードを設定する |
| stdlib | `vgs_exit` | プログラムを [Exit](#0xe7fffcout---exit) させる |
| string | `vgs_d32str` | 32 ビット符号付き整数を文字列に変換する |
| string | `vgs_u32str` | 32 ビット符号なし整数を文字列に変換する |
| string | `vgs_memcpy` | [DMA Copy](#dma-copy) を利用した高速メモリコピー |
| string | `vgs_memset` | [DMA Set](#dma-set) を利用した高速メモリ初期化 |
| string | `vgs_strlen` | [DMA Search](#dma-search) を利用した高速文字列長取得 |
| string | `vgs_sjis_from_utf8` | [UTF-8 文字列を SJIS に変換](#dma-utf8-to-sjis-string) する |
| string | `vgs_strchr` | 文字列内の特定文字を検索する |
| string | `vgs_strrchr` | 文字列内の特定文字を後方から検索する |
| string | `vgs_strcmp` | 文字列を比較する |
| string | `vgs_stricmp` | 大文字/小文字を無視して文字列を比較する |
| string | `vgs_strncmp` | 指定長で文字列を比較する |
| string | `vgs_strstr` | 文字列内の部分文字列を検索する |
| string | `vgs_strcpy` | 文字列をコピーする |
| string | `vgs_strcat` | 文字列を連結する |
| ctype | `vgs_atoi` | 文字列を整数に変換する |
| ctype | `vgs_isdigit` | 文字が数字か判定する |
| ctype | `vgs_isupper` | 文字が大文字か判定する |
| ctype | `vgs_islower` | 文字が小文字か判定する |
| ctype | `vgs_isalpha` | 文字がアルファベットか判定する |
| ctype | `vgs_isalnum` | 文字が英数字か判定する |
| ctype | `vgs_toupper` | 小文字を大文字に変換する |
| ctype | `vgs_tolower` | 大文字を小文字に変換する |
| math | `vgs_degree` | 2 点間の [角度](#0xe00100-0xe00118io---angle) を度数で計算する |
| math | `vgs_sin` | [角度](#0xe00100-0xe00118io---angle) から整数サインを求める |
| math | `vgs_cos` | [角度](#0xe00100-0xe00118io---angle) から整数コサインを求める |
| math | `vgs_abs` | 整数の絶対値を求める |
| math | `vgs_sgn` | 整数の符号を判定する |
| math | `vgs_hitchk` | 矩形の当たり判定を行う |
## liblog.a - Logging Function
`liblog.a` はデバッグログ出力補助ライブラリです。リンク時に `-llog` を指定してください。
```c
#include
```
| Function | Description |
|:---------|:------------|
| `vgs_print` | [Console Output](#0xe00000out---console-output) に改行なしで出力 |
| `vgs_println` | [Console Output](#0xe00000out---console-output) に改行付きで出力 |
| `vgs_putlog` | フォーマット付きテキストを出力 |
`vgs_putlog` は `%d`、`%u`、`%s` を使用できます。`%d` は `int32_t`、`%u` は `uint32_t` を渡してください。
```c
vgs_putlog("d32=%d, u32=%u, str=%s", (int32_t)123, (uint32_t)456, "text");
```
# Toolchain
本章では、**本リポジトリで提供しているコマンドラインツール群のマニュアル**を示します。
これらのツールは、VGS-X 向けゲームのビルド、アセット変換、ROM 生成など、
開発フローの各段階を補助するために用意されています。
すべてのツールを最初から把握する必要はありません。
実装内容や開発フェーズに応じて、必要なものを参照してください。
| Name | Description |
|:-----|:------------|
| [vgsx](#vgs-x-emulator-for-debug) | デバッグ用 VGS-X エミュレータ |
| [bin2var](#bin2var) | バイナリを C 言語の配列に変換 |
| [bmp2chr](#bmp2chr) | [CHR](#character-pattern) データ生成 |
| [bmp2img](#bmp2img) | [Bitmap Sprite](#bitmap-sprite) 形式のデータを生成 |
| [bmp2pal](#bmp2pal) | 初期 [palette](#palette) 生成 |
| [csv2var](#csv2var) | Tiled CSV をバイナリへ変換 |
| [makeprj](#makeprj) | 新規プロジェクト作成 |
| [makerom](#makerom) | プログラムとアセットから ROM を生成 |
| [vgmplay](#vgmplay) | コマンドラインで .vgm を再生 |
## VGS-X Emulator for Debug
パス: [./tools/sdl2/](./tools/sdl2/)
SDL2 を用いた VGS-X エミュレータです。主に開発時のデバッグ用途を想定しています。
```
usage: vgsx [-i]
[-d]
[-g /path/to/pattern.chr]
[-c /path/to/palette.bin]
[-b /path/to/bgm.vgm]
[-s /path/to/sfx.wav]
[-x expected_exit_code]
{ /path/to/program.elf | /path/to/program.rom }
```
- `-i` を指定するとブートロゴ表示後にアプリを起動します(`makerom` で生成した ROM が必要)。
- `-d` オプションを指定するとプログラム終了時に RAM とセーブデータのダンプを出力します。
- `-g`、`-b`、`-s` は複数指定可能です。
- .elf と .rom はヘッダ情報から自動判別します。
- `-x` は CI などのテスト用途向けで、ユーザープログラムの終了コードが期待値と一致すると 0、異なると -1 を返します。指定時は SDL の映像・音声出力を抑制します。
## bin2var
パス: [./tools/bin2var](./tools/bin2var/)
バイナリファイルを C 言語の `const uint8_t` 配列などに変換します。
```
bin2var /path/to/binary.rom [u8|u16|u16l|u16b]
```
- 変換結果は標準出力に出力されるのでリダイレクトして使用してください。
- `u8`: uint8_t として出力(既定)
- `u16`: uint16_t として出力
- `u16l`: little-endian として解釈(`u16` と同等)
- `u16b`: big-endian として解釈
## bmp2chr
パス: [./tools/bmp2chr](./tools/bmp2chr/)
256 色または 16 色の .bmp(Windows bitmap)ファイルまたは 256 色かつアルファチャンネルを含まない .png ファイルから VGS-X 用 [Character Pattern](#character-pattern) を生成します。
```
usage: bmp2chr [-s sizeMinus1] {input.bmp|input.png} output.chr
```
- 画像の幅・高さは `(sizeMinus1 + 1) * 8` の倍数である必要があります。
- `-s` を省略した場合、`sizeMinus1` は `0` として扱います。
- 左上から `(sizeMinus1 + 1)x(sizeMinus1 + 1)` タイルのブロック単位で順に読み込みます。
## bmp2img
パス: [./tools/bmp2img](./tools/bmp2img/)
256 色または 16 色の .bmp(Windows bitmap)ファイルまたは 256 色かつアルファチャンネルを含まない .png ファイルから VGS-X 用 [Bitmap Sprite](#bitmap-sprite) 形式のピクセルデータを生成します。
```
Usage: bmp2img input.(png|bmp) output.img
```
- 画像の幅・高さは 8 の倍数である必要があります。
- 本ツールで生成した .img ファイルは [bin2var](#bin2var) コマンドで変換したコードをプログラムにリンクして使用することを想定しています。
## bmp2pal
パス: [./tools/bmp2pal](./tools/bmp2pal/)
256 色または 16 色の .bmp(Windows bitmap)ファイル、またはアルファチャンネルを含まない .png ファイルから VGS-X 用の初期 [Palette](#palette) を生成します。
- .bmp 入力では従来通り 256 色のパレットデータを生成します。
- .png 入力では最大 16,384 色(1,024 パレット x 16 色)のパレットデータを生成できます。
```
usage: bmp2pal input.png palette.dat
```
## csv2var
パス: [./tools/csv2var](./tools/csv2var/)
Tiled Map Editor の CSV をバイナリに変換します。
```
usage: csv2var input.csv [u8|u16]
```
- レイヤーを持たない Tiled CSV のみに対応しています。
- 出力は標準出力に出力されるためリダイレクトして利用してください。
- `u8`: uint8_t で出力(既定)
- `u16`: uint16_t で出力
## makeprj
パス: [./tools/makeprj](./tools/makeprj/)
新しいゲームプロジェクトを作成します。
```
Usage: makeprj /path/to/project
```
- `makeprj` は [シンプルなシェルスクリプト](./tools/makeprj/makeprj) です。
- 指定するパスは存在しないディレクトリである必要があります。
- 作成されたプロジェクトディレクトリは Git で初期化され、作成時点の suzukiplan/vgsx リポジトリ最新版をサブモジュールとして含みます。VGS-X の仕様が更新された場合でも互換性を維持できます(必要に応じてサブモジュールを更新してください)。
- 後からルートディレクトリをリネーム/移動しても問題ありません。
プロジェクト内で VGS-X を最新版へ更新する手順:
```bash
cd /path/to/your_project
cd vgsx
git pull origin master
cd ..
git commit -m "update submodule" vgsx
```
> `make all` の実行時にサブモジュール初期化が行われるため、更新した場合は事前にコミットしておいてください。
## makerom
パス: [./tools/makerom](./tools/makerom/)
プログラムとアセットをまとめた ROM ファイルを生成します。
```
usage: makerom -o /path/to/output.rom
-e /path/to/program.elf
[-c /path/to/palette.bin]
[-g /path/to/pattern.chr ...]
[-b /path/to/bgm.vgm ...]
[-s /path/to/sfx.wav ...]
```
- `-g`、`-b`、`-s` は複数指定可能で、`-g file1 file2 file3` のように並べて指定もできます。
- 指定順にファイルを読み込み、最初の `-g` で指定したパターンがインデックス 0 に配置されます。
## vgmplay
コマンドラインで .vgm を再生します。
```
usage: vgmplay /path/to/bgm.vgm
```
> 既定ではビルドされないため、利用時は必要に応じてビルドしてください。
# Runtime Implementation Guide
本章は、VGS-X 向けの**ゲームを制作するためのガイドではありません**。
ここでは、VGS-X の仮想ハードウェア仕様を実行するための
**ランタイム環境(エミュレータ/実行基盤)を実装する際のヒント情報**をまとめています。
本リポジトリに含まれる SDL2 ベースのランタイムは、
PC 環境における開発・検証を目的としたリファレンス実装のひとつに過ぎません。
VGS-X は、特定の実装やプラットフォームに依存しない仮想ハードウェア仕様として設計されており、
必要に応じて開発者自身が独自のランタイムを実装できることを前提としています。
本章に記載されている内容は、
VGS-X の仕様をどのように解釈し、どのような構成で実装すればよいかを理解するための
**指針および設計上の参考情報**です。
すべてのゲーム開発者が本章を読む必要はありません。
## 1. Setup Your C++ Project
1. [core ソースコード](./src) を C++ プロジェクトに組み込みます(必要なファイル例は [./tools/sdl2/Makefile](./tools/sdl2/Makefile) を参照)。
2. `#include "vgsx.h"` を追加します。
3. `VGSX` クラスのシングルトン `vgsx` を通じてエミュレータを実行します。
## 2. Load a game ROM
[`makerom`](#makerom) で作成した ROM をロードする前に、`VGSX::enableBootBios` または `VGSX::disableBootBios` で BIOS 起動の有無を設定し、`VGSX::loadRom` で ROM を読み込みます。
```c++
vgsx.enableBootBios();
vgsx.loadRom(romData, romSize);
```
`romData` は VGS-X 実行中に解放しないでください。
> BIOS 起動は任意ですが、可能な限り有効化することを推奨します。
## 3. Main Loop Sequence
`VGSX::isExit` が `false` の間(ユーザープログラムが終了していない間)は、以下を繰り返してゲームを進行させます。
1. [Gamepad](#0xe020xxi---gamepad) の入力に応じて `vgsx.key.{up|down|left|right|a|b|x|y|start}` を 1/0 に設定
2. `VGSX::tick` で MC68030 を 1 フレーム(60fps)分実行
3. `VGSX::getDisplay` で表示用ピクセルデータを取得して画面描画
4. `VGSX::tickSound` でサウンド処理を 1 フレーム分実行
## 4. VGSX::tick
- `VGSX::tick` はユーザープログラムが [V-SYNC](#0xe00000in---v-sync) を要求するか終了するまで、4Hz 間隔で MC68030 を進めます。
- 呼び出し間隔は 1 秒あたり 60 回で維持してください。
- 画面処理と音声処理を別スレッドで並行処理する場合は、排他制御を適切に実装する必要があります。
## 5. VGSX::tickSound
- 多くの OS のサウンド API は固定サイズのバッファをコールバックで処理するため、`VGSX::tickSound` はそのコールバック内で呼び出すことを想定しています。
- PCM(44.1kHz / 16bit / 2ch)のバッファサイズに合わせて呼び出してください。
## 6. User-Defined I/O
ユーザー定義 I/O を利用する場合、`VGSX::tick` を呼び出す前に `VGSX::subscribeInput` / `VGSX::subscribeOutput` でコールバックを登録します。
```c++
// Subscribe Input
vgsx.subscribeInput([](uint32_t port) {
return myInputFunction(port);
});
// Subscribe Output
vgsx.subscribeOutput([](uint32_t port, uint32_t value) {
myOutputFunction(port, value);
});
```
これにより、MC68k 側だけでは扱えないネイティブ機能(例: Steam のリーダーボード参照や実績解除処理など)を連携させることが可能です。
# License
本編では、VGS-X に関連するソフトウェアおよびアセットのライセンス情報をまとめています。
VGS-X は、複数のオープンソースソフトウェアを内部的に利用して構成されていますが、
**すべてが必須というわけではありません**。
以下のライブラリは、それぞれ異なる役割と利用条件を持っています。
## Required (Used Internally by VGS-X)
以下のコンポーネントは、VGS-X の仮想ハードウェア仕様および
リファレンス実装の内部で利用されており、VGS-X を構成する上で必須です。
- MC680x0 Emulator - [Musashi](https://github.com/kstenerud/Musashi)
- Copyright © 1998-2001 Karl Stenerud
- License: [MIT](./LICENSE-Musashi.txt)
- FM Sound Chip Emulator - [ymfm](https://github.com/aaronsgiles/ymfm)
- Copyright (c) 2021, Aaron Giles
- License: [3-clause BSD](./LICENSE-ymfm.txt)
- Japanese Font - [k8x12](https://littlelimit.net/k8x12.htm)
- Created by Num Kadoma
- License: [Free Software](./LICENSE-k8x12.txt)
- [VGS-X](https://github.com/suzukiplan/vgsx) and VGS Standard Library for MC68030
- Copyright (c) 2025-2026 Yoji Suzuki.
- License: [MIT](./LICENSE-VGSX.txt)
## Optional (Runtime Implementation Dependent)
以下のコンポーネントは、**VGS-X の仮想ハードウェア仕様そのものには必須ではありません**。
本リポジトリに含まれる SDL2 ベースのランタイム実装を利用する場合にのみ必要となります。
- [SDL2](https://www.libsdl.org/)
- Copyright (C) 1997-2025 Sam Lantinga
- License: [ZLIB License](./LICENSE-SDL2.txt)
独自のランタイム環境を実装する場合や、
SDL2 を使用しない実行環境を構築する場合には、
SDL2 を利用する必要はありません。