この文書は、Z80 を CPU に持つコンピュータ向けの C コンパイラ・アセンブラツールチェーンである Z88DK を使って MSX のゲームをつくるための環境構築メモです。
The development kit for over a hundred z80 family machines - c compiler, assembler, linker, libraries.
手順では Ubuntu 20.04 LTS もしくは Ubuntu 22.04 LTS を用いています。Z88DK の準備については Windows WSL2 でも同様の手順です。
筆者が Z88DK で実装した MSX ゲームです。GitHub からソースコードも見ることができます。
🔗 https://github.com/h1romas4/z88dk-msx-template
🔗 https://github.com/h1romas4/noborunoca
Z88DK の最新版を使うためソースコードからビルドします。(ここでは master
ブランチの d0c5ff4
版で確認しています)
依存関係の導入(Z88DK の Github Actions build-on-ubuntu.yml を参考にしてください)
sudo apt install -y ragel re2c dos2unix texinfo texi2html gdb curl cpanminus ccache libboost-all-dev libmodern-perl-perl libyaml-perl liblocal-lib-perl libcapture-tiny-perl libpath-tiny-perl libtest-differences-perl libtext-table-perl libdata-hexdump-perl libregexp-common-perl libclone-perl
Perl の依存関係をローカルホーム(~/.perl5
)に導入
cpanm App::Prove Capture::Tiny::Extended CPU::Z80::Assembler Data::HexDump File::Path List::Uniq Modern::Perl Object::Tiny::RW Test::Cmd Test::Cmd::Common Test::Harness Test::HexDifferences Text::Table YAML::Tiny
ビルド前に Perl のライブラリーパスを設定
eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
ビルド(submodule を使っているので --recursive
を必ずつける)
git clone --recursive https://github.com/z88dk/z88dk.git
cd z88dk
git checkout 9ffe204 # v2.2 を指定
./build.sh
ターゲットが MSX だけの場合は次のようにするとビルド時間が短縮できます。
make clean
./build.sh -p msx
ビルド確認(zcc
がコンパイラフロントエンドコマンドになります)
$ ls -laF bin/
合計 13764
drwxrwxr-x 2 hiromasa hiromasa 4096 6月 23 17:20 ./
drwxrwxr-x 18 hiromasa hiromasa 4096 6月 23 17:19 ../
-rwxr-xr-x 1 hiromasa hiromasa 3447096 6月 23 17:20 z80asm*
-rwxr-xr-x 1 hiromasa hiromasa 1376688 6月 23 17:19 z88dk-appmake*
-rwxr-xr-x 1 hiromasa hiromasa 15657 6月 23 17:20 z88dk-asmpp*
-rwxr-xr-x 1 hiromasa hiromasa 6789 6月 23 17:20 z88dk-asmstyle*
-rwxr-xr-x 1 hiromasa hiromasa 260568 6月 23 17:20 z88dk-basck*
-rwxr-xr-x 1 hiromasa hiromasa 185104 6月 23 17:19 z88dk-copt*
-rwxr-xr-x 1 hiromasa hiromasa 444432 6月 23 17:20 z88dk-dis*
-rwxr-xr-x 1 hiromasa hiromasa 28128 6月 23 17:20 z88dk-dzx0*
-rwxr-xr-x 1 hiromasa hiromasa 17184 6月 23 17:20 z88dk-dzx7*
-rwxr-xr-x 1 hiromasa hiromasa 35016 6月 23 17:20 z88dk-font2pv1000*
-rwxr-xr-x 1 hiromasa hiromasa 1061816 6月 23 17:20 z88dk-gdb*
-rwxr-xr-x 1 hiromasa hiromasa 47264 6月 23 17:20 z88dk-lib*
-rwxr-xr-x 1 hiromasa hiromasa 834616 6月 23 17:19 z88dk-sccz80*
-rwxr-xr-x 1 hiromasa hiromasa 1132312 6月 23 17:20 z88dk-ticks*
-rwxr-xr-x 1 hiromasa hiromasa 311624 6月 23 17:19 z88dk-ucpp*
-rwxr-xr-x 1 hiromasa hiromasa 3447096 6月 23 17:20 z88dk-z80asm*
-rwxr-xr-x 1 hiromasa hiromasa 335840 6月 23 17:20 z88dk-z80nm*
-rwxr-xr-x 1 hiromasa hiromasa 101464 6月 23 17:20 z88dk-z80svg*
-rwxr-xr-x 1 hiromasa hiromasa 121312 6月 23 17:19 z88dk-zcpp*
-rwxr-xr-x 1 hiromasa hiromasa 383360 6月 23 17:20 z88dk-zobjcopy*
-rwxr-xr-x 1 hiromasa hiromasa 45344 6月 23 17:20 z88dk-zpragma*
-rwxr-xr-x 1 hiromasa hiromasa 48304 6月 23 17:20 z88dk-zx0*
-rwxr-xr-x 1 hiromasa hiromasa 21448 6月 23 17:20 z88dk-zx7*
-rwxr-xr-x 1 hiromasa hiromasa 329480 6月 23 17:20 zcc*
ZCCCFG
環境変数を z88dk/lib/config
に設定して、PATH
を z88dk/bin
に通します。 また、この後 VS Code のソース編集時に include path を通すため Z88DK_HOME
(任意の名前)を設定しておきます。
.bashrc
の再下部に追加
# z88dk
export Z88DK_HOME=/home/hiromasa/devel/msx/z88dk
export ZCCCFG=${Z88DK_HOME}/lib/config
export PATH=${Z88DK_HOME}/bin:${PATH}
zcc
の起動確認
$ zcc +msx
zcc - Frontend for the z88dk Cross-C Compiler - v18586-be7c8763a-20210901
Usage: zcc +[target] {options} {files}
Options:
-v -verbose Output all commands that are run (-vn suppresses)
-h -help Display this text
-o Set the basename for linker output files
-specs Print out compiler specs
# ..snip..
Z88DK のコアやライブラリーソースなどを VS Code で開いて参照する場合は、ビルド後に多数生成される .o
オブジェクトファイルで VS Code が遅くならないよう .vscode/settings.json
を追加し以下の files.watcherExclude
設定をすると良いです。
.vscode/settings.json
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/obj/**": true,
}
}
導入したツールチェーンの動作を確認するため、筆者がつくったサンプルプログラムをセットしたテンプレートプロジェクトを git clone します。
.git/
を削除して、自分のプログラムを入れるとそのままプロジェクトディレクトリとして使えると思います。.rom
を GitHub のコンテナでビルドしてリリースできます。Z88DK MSX build template with sample game
Use this template
cmake の導入
apt install cmake
テンプレートプロジェクトのビルド(サンプルソースをコンパイルして .rom
をビルド)
cmake/z88dk.cmake
内で ${Z88DK_HOME}
環境変数を利用している。git clone https://github.com/h1romas4/z88dk-msx-template.git
cd z88dk-msx-template
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/z88dk.cmake ..
make
cmake/z88dk.cmake
set(CMAKE_C_COMPILER $ENV{Z88DK_HOME}/bin/zcc)
set(CMAKE_ASM_COMPILER $ENV{Z88DK_HOME}/bin/zcc)
src/msx
配下のサンプルプログラムがコンパイルされ、dist/
配下に MSX で起動可能な .rom
ファイルが生成されます。(任意の MSX エミュレータにセットすることで起動可能です)
$ ls -laF dist/
合計 128
drwxrwxr-x 2 hiromasa hiromasa 4096 9月 9 23:26 ./
drwxrwxr-x 10 hiromasa hiromasa 4096 9月 9 18:04 ../
-rw-rw-r-- 1 hiromasa hiromasa 94546 9月 9 17:02 example.map
-rw-rw-r-- 1 hiromasa hiromasa 16384 9月 9 17:02 example.rom
-rw-rw-r-- 1 hiromasa hiromasa 245 9月 9 17:02 example_BSS.bin
-rw-rw-r-- 1 hiromasa hiromasa 16 9月 9 17:02 example_DATA.bin
CMakeLists.txt
の構成プロジェクトのビルドは CMakeLists.txt
に従って行われます。プロジェクトに合わせて修正します。
コンパイラのパス指定(${Z88DK_HOME}
環境変数を利用している)
set(CMAKE_C_COMPILER $ENV{Z88DK_HOME}/bin/zcc)
set(CMAKE_ASM_COMPILER $ENV{Z88DK_HOME}/bin/zcc)
dist/example.rom
の出力 ROM 名の設定
project(example.rom C ASM)
ビルド対象ソースファイルの追加削除(.c 及び .asm に対応)
add_source_files(
./src/msx/example.c
./src/msx/chars.asm
)
CMakeLists.txt
を修正した後は、一度 cmake をし直します。
# キャッシュがあるので build ディレクトリごと削除
rm -Rf build
# build ディレクトリをつくってカレントを移動
mkdir build && cd build
# CMakeLists.txt の場所を指定して cmake
cmake -DCMAKE_C_COMPILER=${Z88DK_HOME}/bin/zcc ..
# Makefile が生成されるので make
make
通常のソースコード変更だけの場合は make だけで OK です。なお、アセンブラで include
を使っている場合、include 先の更新までは make が追従しませんので、更新がある場合はいったん make clean
してください。
その他のコンパイルオプション
The frontend of z88dk is called zcc, it is this that you should call if you want to do any compilations. To invoke the frontend use the command:
C 向け(-m
が後述の example.map
シンボルマップファイルを出力する指定)
add_compile_flags(C
+msx
-vn
-llib3d
-lm
-lndos
-lmsxbios
-m
# -debug
# https://github.com/z88dk/z88dk/wiki/Classic-allocation#automatic-heap-configuration
-DAMALLOC
)
アセンブラ向け
add_compile_flags(ASM
+msx
)
リンカー向け
-subtype=rom
で 0x4000
からの ROM 版をつくることを指定。0xc0000
からの RAM 版をつくる場合は -subtype=default
(.cas
形式) もしくは -subtype=wav
(カセットテープのピーガー形式) を指定。BLOAD "CAS:",R
でロード・ランできる。add_compile_flags(LD
-create-app
-subtype=rom
)
cmake が生成した Makefile の zcc
コマンドをデバッグする場合は CMakeLists.txt
の以下の部分をコメントアウトする。
# for debug
# set(CMAKE_VERBOSE_MAKEFILE 1)
テンプレートプロジェクトは C/C++ for Visual Studio Code 拡張用の .vscode/settings.json
を含んでいます。Z88DK への include path の設定がされているので、拡張を有効にした後、ディレクトリを VS Code で開くことで、ソースコード編集中の定義の参照やインテリセンスによる補完ができます。
The C/C++ extension adds language support for C/C++ to Visual Studio Code, including features such as IntelliSense and debugging.
.vscode/settings.json
(${env:Z88DK_HOME}
環境変数で Z88DK へのパスをたどっている)
{
"configurations": [
{
"name": "z88dk",
"includePath": [
"${workspaceFolder}/src/msx/*",
"${env:Z88DK_HOME}/include/*",
"${env:Z88DK_HOME}/include/**/*"
],
"defines": [],
"compilerPath": "${env:Z88DK_HOME}/bin/zcc",
"cStandard": "c11",
"intelliSenseMode": "gcc-x86"
}
],
"version": 4
}
また、.vscode/tasks.json
で F1 キー押下後の Run Tasks メニューに、ビルド系のコマンドを追加しています。
.vscode/tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Clean CMake directory",
"type": "shell",
"linux": {
"command": "rm -Rf ${workspaceFolder}/build && mkdir ${workspaceFolder}/build"
}
},
{
"label": "Run CMake",
"type": "shell",
"dependsOn": "Clean CMake directory",
"linux": {
"command": "(cd ${workspaceFolder}/build && cmake -DCMAKE_C_COMPILER=${Z88DK_HOME}/bin/zcc ..)"
}
},
{
"label": "Run Build",
"type": "shell",
"linux": {
"command": "(cd ${workspaceFolder}/build && make)"
}
},
]
}
ビルドした .rom
の動作確認を行うため openMSX をビルドします。(ここでは master
ブランチの 5841294
版で確認しています)
the MSX emulator that aims for perfection
依存関係の導入
sudo apt-get install libgl1-mesa-dev
ビルド
-j6
はコンパイルに使う CPU コア数。マシンに合わせて設定すると速くビルドできます。staticbindist
を指定しています。derived
ディレクトリを削除のこと。git clone https://github.com/openMSX/openMSX.git
cd openMSX
make -j6 OPENMSX_TARGET_CPU=x86_64 OPENMSX_TARGET_OS=linux OPENMSX_FLAVOUR=opt staticbindist
# ...snip...
Creating binary package:
Executable...
Data files...
Documentation...
C-BIOS...
Creating symlinks...
インストール
derived/x86_64-linux-opt-3rd/bindist/install/bin
に生成された openmsx
をパスが通っている適当な場所にコピー。
cp -p derived/x86_64-linux-opt-3rd/bindist/install/bin/openmsx ~/.local/bin
~/.openMSX
にコンフィグをコピー
mkdir ~/.openMSX
cp -Rfp derived/x86_64-linux-opt-3rd/bindist/install/share/ ~/.openMSX
起動確認(テンプレートプロジェクトの example.rom
による)
$ cd /home/hiromasa/devel/msx/z88dk-msx-template/dist
$ ls -laF
合計 128
drwxrwxr-x 2 hiromasa hiromasa 4096 9月 9 23:26 ./
drwxrwxr-x 10 hiromasa hiromasa 4096 9月 9 18:04 ../
-rw-rw-r-- 1 hiromasa hiromasa 94546 9月 9 17:02 example.map
-rw-rw-r-- 1 hiromasa hiromasa 16384 9月 9 17:02 example.rom
-rw-rw-r-- 1 hiromasa hiromasa 245 9月 9 17:02 example_BSS.bin
-rw-rw-r-- 1 hiromasa hiromasa 16 9月 9 17:02 example_DATA.bin
$ openmsx example.rom
もし openmsx 起動時に次のエラーが出力され、音声の発音などに問題がある場合は、SDL を static のものから OS 標準のものに変更してビルドしてみます。
SDL init failed (16)
依存関係のライブラリー devel を導入。
sudo apt-get install libsdl2-dev libpng-dev tcl-dev libglew-dev libsdl2-ttf-dev libvorbis-dev libtheora-dev libogg-dev libao-dev libfreetype6-dev
openMSX を clone したディレクトリにて再ビルド。(staticbindist
を付与しない)
$ pwd
/home/hiromasa/devel/msx/openMSX
$ make clean
$ make -j6 OPENMSX_TARGET_CPU=x86_64 OPENMSX_TARGET_OS=linux OPENMSX_FLAVOUR=opt
できたバイナリーをパスが通っている位置にコピー。
$ ls -laF ./derived/x86_64-linux-opt/bin/openmsx
-rwxrwxr-x 1 hiromasa hiromasa 7675296 10月 23 15:30 ./derived/x86_64-linux-opt/bin/openmsx*
$ cp -p ./derived/x86_64-linux-opt/bin/openmsx ~/.local/bin
コンフィグレーションファイルをコピー。
$ mkdir -p ~/.openMSX/
$ cp -Rfp share/ ~/.openMSX/
$ ls -laF ~/.openMSX/
drwxr-xr-x 13 hiromasa hiromasa 4096 10月 23 01:50 share/
$ ls -laF ~/.openMSX/share
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 extensions/
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 icons/
-rw-r--r-- 1 hiromasa hiromasa 7321 10月 23 01:12 init.tcl
drwxr-xr-x 3 hiromasa hiromasa 16384 10月 23 01:12 machines/
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 nettou_yakyuu/
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 playball/
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 scripts/
-rw-r--r-- 1 hiromasa hiromasa 317 10月 23 15:38 settings.xml
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 shaders/
drwxr-xr-x 10 hiromasa hiromasa 4096 10月 23 01:12 skins/
drwxr-xr-x 2 hiromasa hiromasa 4096 10月 23 01:12 software/
-rw-r--r-- 1 hiromasa hiromasa 1208129 10月 23 01:12 softwaredb.xml
-rw-r--r-- 1 hiromasa hiromasa 301 10月 23 01:12 softwaredb1.dtd
drwxr-xr-x 3 hiromasa hiromasa 4096 10月 23 01:12 systemroms/
drwxr-xr-x 3 hiromasa hiromasa 4096 10月 23 01:12 unicodemaps/
起動確認。
$ which openmsx
~/.local/bin/openmsx
$ openmsx -v
openMSX 18.0-102-g953affe10
flavour: opt
components: ALSAMIDI CORE GL LASERDISC
$ openmsx
openMSX 上で起動した .rom
をアセンブルデバッグするために openmsx-debugger を導入します。(ここでは master
ブランチの 1bac8ea
版で確認しています)
openmsx-debugger The openMSX debugger is a separate program that interfaces with openMSX and controls its debugger from within a graphical user interface.
依存関係の導入(Ubuntu 20.04 LTS)
sudo apt install qtbase5-dev qttools5-dev-tools qt5-default
依存関係の導入(Ubuntu 21.04 | Ubuntu 22.04 LTS)
sudo apt install qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake
.map
シンボルファイルが読めるよう hack 追加。
Ubuntu 22.04 LTS(Qt 5.14) の場合:
git clone https://github.com/openMSX/debugger
cd debugger
git checkout bc999831fb14ce7d4505e06ad75a821842049ab4 # master ブランチでパッチが通らない場合
wget https://gist.githubusercontent.com/h1romas4/9fdbcd45c4d6bdd87312dc3ca83059be/raw/122ec08b62df3a285b6c710e05a3e33a24c45364/0001-support-z88dk-map-symble-file.patch
patch -p1 < 0001-support-z88dk-map-symble-file.patch
make -j6
Ubuntu 20.04 LTS(Qt 5.12) の場合:
git clone https://github.com/openMSX/debugger
cd debugger
git checkout bbbc33c1fc07a66ea80d38251fdd078d0242abd8 # 少し前のバージョン
wget https://gist.githubusercontent.com/h1romas4/c851be9afa16ff2ebb3a967548ed92ad/raw/54030a10567ec2fe6148a47f2dffd8b8ab81e81c/0001-add-z88dk-symbol-read-hack.patch
patch -p1 < 0001-add-z88dk-symbol-read-hack.patch
make -j6
ビルド確認と適当なパスの通った位置にコピー
$ ls -laF derived/bin/
-rwxrwxr-x 1 hiromasa hiromasa 20408664 9月 10 00:58 openmsx-debugger*
$ cp -p derived/bin/openmsx-debugger ~/.local/bin
$ openmsx-debugger
openMSX で dist/example.rom
を起動した状態で、openmsx-debugger から System -> Symbol Manager -> Add で dist/example.map
を読ませる。
System -> Connect で openMSX に接続すると、ソースコード内のシンボル名付きでアセンブラコードが表示され、デバッグブレイクなどができる。
Z88DK の master
ブランチには z88dk-gdb
コマンドが含まれており、MAME の gdbstub と通信してデバッグを行うこともできます。
CFLAGS
で -m
-debug
コンパイルオプションを有効にしてビルドすると C のレベルでデバッグできます。(プログラムは遅くなります)$ (cd ${MAME_HOME} && ./cbios cbios example -window -resolution 800x600 -debugger gdbstub -debug)
gdbstub: listening on port 23946
$ cd dist/ # .map ファイル内に出力されたソースコードのパス(../src/msx/example.c)と z88dk-gdb を起動するカレントディレクトリを合わせます。(v2.2 からソースマップがフルパスになったのでこの操作は不要)
$ z88dk-gdb -h 127.0.0.1 -p 23946 -x example.map
また、Native Debug 拡張を使い、gdb/mi2 に VS Code からアタッチすることで VS Code デバッガから操作することもできます。
.vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to MAME gdbserver",
"type": "gdb",
"request": "attach",
"target": "127.0.0.1:23946",
"remote": true,
"cwd": "${workspaceRoot}",
"gdbpath": "${env:Z88DK_HOME}/bin/z88dk-gdb",
"debugger_args": [
"-x",
"${workspaceRoot}/dist/example.map" // or appropriate .map of your project (-m -debug needed!)
],
"autorun": [
]
}
]
}
なお、2022/7 現在、Step Over すると動作が戻ってこなくなるようです。Continue と Step into はうまく動作しますので使い分けて活用すると良いと思います。
VS Code 上でアセンブラデバッグをする場合は以下の文書を参考にしてください。
DeZog + Z88DK + MAME で MSX アセンブリーをデバッグする手順
この文書は、VS Code の Z80 デバッガーである DeZog 拡張と Z80 ツールチェイン Z88DK、及び MSX エミュレータ・gdb スタブとして MAME を使い、VS Code 上の Z80 アセンブリーをデバッグする手順です。
MSX 向けのグラフィックを描くため nMSXtiles を導入してみます。(ここでは master
ブランチの e4db1b8
版で確認しています)
Editor de Baldosas (Tiles) y Sprites para MSX en modo gáfico SC2 y SC4
依存関係の導入
sudo apt install qtcreator
ソースコードの clone
git clone https://github.com/pipagerardo/nMSXtiles.git
依存関係で導入した Qt Creator を起動して ファイル/プロジェクトを開くから src/nmsxtiles.pro
を指定する。
ビルドからプロジェクトのビルドを実行する。
build/
下に nMSXtiles
実行バイナリが作成されるので、適当なパスの通ったディレクトリにコピーして起動する。
$ ls -laF build/
合計 452
drwxrwxr-x 4 hiromasa hiromasa 4096 9月 9 21:40 ./
drwxrwxr-x 9 hiromasa hiromasa 4096 9月 9 21:40 ../
drwxrwxr-x 2 hiromasa hiromasa 4096 9月 9 21:21 doc/
drwxrwxr-x 2 hiromasa hiromasa 4096 9月 9 21:52 examples/
-rwxrwxr-x 1 hiromasa hiromasa 445792 9月 9 21:40 nMSXtiles*
$ cp -p nMSXtiles ~/.local/bin
$ nMSXtiles
起動して build/examples
下にあるサンプルプロジェクトを開くと使い方が分かります。
Z88DK で +msx
で MSX をターゲットに指定した場合は C の標準ライブラリーは Classic Library になります。(もうひとつは New Library)
The classic library can be used with both sccz80 and zsdcc and the final binary can be made of object files generated by either compiler (though care will need to be taken around calling conventions and floating point usage).
sprintf
などが使える(便利)。src/example.c
#include <stdio.h>
#include <stdlib.h>
/**
* スコア等表示
*/
void print_state()
{
uchar score_string[VRAM_WIDTH + 1];
sprintf(score_string, "SCORE %06u HISCORE %06u %02u", game.score, game.score_hi, game.remein_clear);
vwrite(score_string, VPOS(0, 0), VRAM_WIDTH);
}
MSX の TMS9918a/TMS9928a VDP にアクセスする場合は msx/gfx.h
を include します。
set_color
や set_mode
set_mangled_mode
などで VDP の初期化ができる。vwrite
や fill
、vpeek
vpoke
などで VRAM へのアクセスができる。#include <msx/gfx.h>
/**
* グラフィックス初期化
*/
void init_graphics()
{
// スクリーンモード
set_color(15, 1, 1);
set_mangled_mode();
// スプライトモード
set_sprite_mode(sprite_default);
// キークリックスイッチ(OFF)
*(uint8_t *)MSX_CLIKSW = 0;
// 画面クリア
fill(VRAM_START, VRAM_NONE, VRAM_WIDTH * VRAM_HEIGHT);
// PCG 設定(3面に同じデーターを転送)
vwrite(chars, 0x0000, 0x800);
vwrite(chars, 0x0800, 0x800);
vwrite(chars, 0x1000, 0x800);
// 色設定(0000|0000 = 前|背景)
set_char_color('=', 0x54, place_all);
set_char_color('$', 0xa0, place_all);
set_char_color('>', 0x6d, place_all);
set_char_color('?', 0x60, place_all);
}
get_rnd
seed_rnd
関数で乱数が使える。uint8_t x = get_rnd() % 31 + 1;
uint8_t y = get_rnd() % 21 + 1;
get_stick
get_trigger
でコントローラ、キーボードの入力ができる。// 入力取得
uint8_t stick = st_dir[get_stick(0)];
uint8_t trigger = get_trigger(0);
Z88DK のブートストラップの解説は以下にドキュメントがあります。
The crt is the startup code that runs before calling main(). It is responsible for setting the memory map, instantiating device drivers on stdin/stdout/stderr, initializing the bss and data sections and calling any initialization code prior to calling main(). On return from main() it is responsible for closing open files, resetting the stack and preparing to return to the host.
+msx
subtype=rom
指定の場合は、lib/config/msx.cfg
より設定され、ブートストラップは次のアセンブラになります。
lib/target/msx/classic/rom.asm
;
; Main Code Entrance Point
;
IFNDEF CRT_ORG_CODE
defc CRT_ORG_CODE = $4000
ENDIF
org CRT_ORG_CODE
; ROM header
defm "AB"
defw start
defw 0 ;CallSTMT handler
defw 0 ;Device handler
defw 0 ;basic
defs 6
start:
di
INCLUDE "crt/classic/crt_init_sp.asm"
ei
;...snip...
0x4000
からの 16KB ROM。ROM ヘッダーがここでつけられる。0xc000
から。.asm
をエントリーポイントとしてプログラムを構成する場合は _main
を PUBLIC
にする。
; code_user is for read-only code
; called by the crt as entry to program
SECTION code_user
; _main という名前でラベルを PUBLIC にする
PUBLIC _main
; メイン
_main:
; ここからプログラム
LD BC,VALUE1
LD A,(BC)
LD IX,WORK1
LD (IX),A
RET
; rodata_user if for constant data
; kept in rom if program is in rom
SECTION rodata_user
; ROM に書き込むデーター
VALUE1: DB 10
; bss_user is for zeroed ram variables
; zeroed by crt when program started
SECTION bss_user
; RAM に配置するワークエリア(CRT が 0 に初期化してくれる)
WORK1: defs 1
; data_user is for initially non-zero ram variables
; initialized in ram by crt if the program is in rom
SECTION data_user
; RAM に配置するデーター(CRT が指定値で初期化してくれるらしい)
WORK2: DB 10
.asm
へのバイナリデータリンク.asm
ファイルにデータセクション rodata_user
(ROM に配置される)をつくり、_
アンダーバー付きラベルをパブリック指定することで .c
から extern
で、アンダーバーなしのラベル名で参照できる。
src/chars.asm
; rodata_user
; https://github.com/z88dk/z88dk/blob/master/doc/overview.md#a-quick-note-for-asm-code
; rodata_user if for constant data
; kept in rom if program is in rom
SECTION rodata_user
PUBLIC _chars, _colors
_chars:
DB $fe,$c2,$8a,$92,$aa,$86,$fe,$00,$fe,$c2,$8a,$92,$aa,$86,$fe,$00
DB $fe,$c2,$8a,$92,$aa,$86,$fe,$00,$fe,$c2,$8a,$92,$aa,$86,$fe,$00
src/example.c
// chars.asm::_chars ラベルのアドレス参照(VRAM PCG 転送用)
extern unsigned char chars[];
ABI を合わせることで .asm
と .c
間で関数呼び出しや call が可能。(本サンプルではアセンブラでかかれたサウンドドライバーの呼び出し部分で使用)
The z88dk parameter passing mechanism normally relies on the stack: local variables declared in a C program are allocated on the stack as a function is entered and are popped off the stack as functions are exited. z88dk supports multiple calling conventions so care should be taken when writing assembler that interfaces with C.
.asm
でかかれたプログラム領域は SECTION code_user
に配置する。.asm
の規定セクション名については、以下のドキュメントに記載があります。
Z88DK uses z80asm as a section-aware linking assembler. What this means is placement of code and data in memory is controlled by section assignment rather than ORG. This is an essential feature of a modern development environment.
本項「呼び出し規則と関数デコレータ」は Z88DK Wiki の DeepL 日本語訳です。
Z88DK には、関数を呼び出すためのいくつかの異なる規約があります。これらを理解することで、プログラムとアセンブラの機能をリンクさせたり、デバッグの際に役立つでしょう。
規約や修飾子は、例えば、関数のプロトタイプにサフィックスとして追加することで、コンパイラに示されます。
int function(long val) __z88dk_callee;
Z88DK は以下の呼び出し規則をサポートしています。
修飾子 | Stack cleanup | 説明 |
---|---|---|
__smallc |
呼出し元 | sccz80 が使用するデフォルトの呼び出し規則です。パラメータは、左から右に向かってスタックに押し込まれます。sccz80 の場合、char は word としてスタックにプッシュされます。注意 zsdcc のバグにより、__smallc 関数にアクセスすると、chars は 1 バイトとしてプッシュされます。戻り値は hl, または dehl です。 |
__stdc |
呼出し元 | パラメータは逆順(右から左)にプッシュされます。char は word としてスタックにプッシュされます。戻り値は hl または dehl です。 |
__z88dk_sdccdecl |
呼出し元 | zsdcc のデフォルトの規約です。パラメータは、右から左に向かってスタックにプッシュされます。char は、1 バイトとしてスタックにプッシュされます。戻り値は、l、hl、または dehl に保持されます。 |
修飾子 | 説明 |
---|---|
__z88dk_callee |
呼び出された関数(callee)は、スタックをクリーンアップする責任があります。 |
__z88dk_fastcall |
レジスターには最大で1つのパラメータが渡されます。smallc では、一番右のパラメータになります。また、__stdc と __z88dk_sdccdecl では、唯一のパラメータとなります。使用されるレジスタは、パラメータのビット幅に応じて、常に DEHL のサブセットとなります。 sccz80 の浮動小数点/倍精度は 48 ビットなので、少し扱いが異なります。 これらは「一次浮動小数点アキュムレータ」を介して渡されます。 従来の C ライブラリでは、これは "fa"と名付けられた6バイトのスタティックメモリです。 new c library では、これはexxセットのレジスタ BCDEHL です。 sdcc の 64 ビット long long 型は、fastcall リンケージを使用して渡すことができません。 |
__z88dk_saveframe |
sccz80 で生成されたコードにのみ有効です。sdccフレームポインタ(ix)は、この関数への入力時に保存されます。これはこの関数が sdcc コンパイルコードから呼び出されることが予想され、長さまたは浮動小数点を使用する場合に必要です。 |
__critical |
割り込みの状態は、関数への入力時に保存され、終了時に復元されます。 |
__interrupt(n) |
入力時にプライマリ・レジスタとインデックス・レジスタを保存し、終了時にリストアします。必要に応じてreti/retnして返します。z88dk で採用されている動作 はsdccと同じで「かなり紛らわしい」です。 |
__naked |
アセンブラ関数に使用され、関数のプロローグとエピローグが生成されないようにします。 |
__interrupt __critical
インタラクション:void func() __critical __interrupt
- レジスタをセーブし、retn
で返します(NMIの場合など)void func() __interrupt
- ei
し、レジスターを保存し、reti
で返します (im2 の場合)void func() __critical __interrupt(0)
- レジスターを保存し、ei; reti
で返します (im1 の場合)修飾子 | 使用方法 |
---|---|
__banked |
ターゲット固有の banked_call 関数を使用して、関数を呼び出すことができます。呼び出しの後には、その関数の32ビットのアドレスが続きます。最初の2バイトがアドレスで、後の2バイトがバンクです。この呼び出し規則の関数例は、クラシックな +zx、+msx、+gbポートに見られます。 |
__z88dk_shortcall(RR, VV) |
rst RR トランポリンで関数を呼び出すことができます。VV < 256 の場合、生成されるコードは rst RR; defb VV であり、そうでない場合は rst RR; defw VV となります。これは、非ページ化されたメモリバンクにある関数にアクセスするために使用できます。 |
__z88dk_shortcall_hl(RR, VV) * |
生成されたトランポリンが次のようになることを除けば、上記と同じです。: ld hl, VV; rst RR |
__z88dk_hl_call(VV1, VV2) * |
spectranet スタイルトランポリンです。: ld hl, VV1; call VV2 |
__z88dk_params_offset(VV) |
トランポリンを介して呼び出された場合、関数のパラメータがsp+2を起点に配置されていないことがあります。このアノテーションでは、パラメータに到達するために必要な追加のオフセットを定義します。 |
*: __z88dk_fastcall
と組み合わせた場合、fastcall に必要なコンテンツや HL は BC にコピーオーバーされ、後に置き換えられます。トランポリンが受信側で処理されたときにHLの内容を復元するのは、ユーザーの責任です。
__z88dk_shortcall()
では、直接呼び出しのみがrstトランポリンを使用し、関数ポインタを介した呼び出しは通常通り呼び出します。したがって、ライブラリを配布したり、インターフェイスを提供したりする際には、rst トランポリンを介して呼び出すことができるマッチした関数スタブを提供する必要があります。
修飾子 | 利用方法 |
---|---|
__preserves_regs(r1,r2...) |
指定されたレジスタが関数によって保持されることを sdcc に示します。この情報は、sdcc が生成するコードの品質を向上させるために使用されます。 |
慣習的に、sccz80が使用するライブラリ関数は、型の後に__LIB__
と記されています。
void __LIB__ mylibrary_function(int param);
つまり、sccz80 は mylibrary_function
を、sdcc は _mylibrary_function
を呼び出すことになります。これらの異なるエントリーポイントは、呼び出し規則を調整するのに役立ちます。(vaargs関数では必須)
戻り値は、戻り値のビット幅に応じて、DEHLのサブセットに保持されます。 sccz80 の 48 ビット float/double は、クラシックライブラリではアドレス "fa"の 6 バイトのスタティックメモリである "primary floating point accumulator" に値を返し、new c library では exx セットのレジスタBCDEHL に値を返すなど、扱いが異なります。 なお、DEHL を介して1つのパラメータが関数に渡される fastcall リンケージと同じルールが適用されます。
64-bit long long 型の戻り値は特別に処理されます。 コンパイラは、関数呼び出しの最初のパラメータとして、戻り値のメモリへのポインタを渡します。 このパラメータは、関数のプロトタイプには記載されていません。 呼び出された関数は、そのポインタを使って、返された64ビットの値を格納しなければなりません。
[翻訳ここまで]
C 言語ソースコード中に #asm
#endasm
もしくは、__asm
__endasm;
を挟むことでインラインアセンブラを使うことができます。スタックは __smallc
にて、char は word のリトルエンディアンで積まれるので、C言語関数の引数は次のように取得できます。
void write_psg(uint8_t reg, uint8_t dat)
{
#asm
LD HL ,2 ;
ADD HL, SP ; リターンアドレスをスキップ
LD E, (HL) ; WRTPSG(E)
INC HL ; WORD の 2byte 目をスキップ
INC HL ;
LD A,(HL) ; WRTPSG(A)
CALL $0093 ; call WRTPSG(A, E)
#endasm
}
/**
* 特殊効果音
*/
void sound_jump()
{
// ..snip..
// ボリューム設定
write_psg(0xa, 0x0);
// ..snip..
}