任意コード実行の基礎知識

ゲーム内でコードを組む際にはゲームボーイの仕様をある程度把握しておく必要がある。
そこで、ゲームボーイの仕様にまつわる要素と各種命令コード及びポケモンにおける文字列との対応の解説を行う。

基礎の基礎として、1bitは0か1が入る2進数の単位で、1Byteは8bitで構成される。
0x○○や○○hで表現されることがある16進数は4bitをまとめて0~Fで表現したものであり、2桁で0~255までの数値を表現できる。
(hはhexの頭文字から。10進数はdecimalの頭文字でd、2進数はbinaryの頭文字からbで表現されることもある)
コンピューターでは多くの番号を0から数えるということを頭に入れておきたい。

1バイトのイメージ
  8bitで1Byte
16進表現 0~F 0~F
16進の桁 80h 40h 20h 10h 08h 04h 02h 01h
bit 7 6 5 4 3 2 1 0

参考資料

ぽけもんばぐ・りっちーず(リンク切れ)

メモリマップ - GB Spec - atwiki(アットウィキ)

レポートのデータ構造(初代) - まへっちのポケモンメモ

目次


メモリアドレス

数値は16進数で表記する。

大ざっぱなアドレス配置
始点 終点 内容 解説
0000 3FFF ROMバンク00 常時使う基本的なプログラムを格納してある。
4000 7FFF ROMバンク01~1F 場面ごとに必要に応じたデータを呼び出して格納してある。
2000~3FFFに数値を入れることでバンクを切り替えられる。
ポケモンではバンク切り替え時、FFB8にも切り替え先のバンクNo.を書き込んでいる。
8000 9FFF VRAM 画面情報にかかわる領域。
ここを弄ることで画面に好きなものを書き込んだりすることもできるがかなり難しい。
A000 BFFF SRAMバンク00~03 セーブデータの領域であり、一部(バンク00)はグラフィックデータの展開先としても用いられている。
初代でバグポケモンの姿を見ると殿堂入りデータが壊れるのは、グラフィックデータを展開する場所の先に殿堂入りデータがあるから。
C000 DFFF WRAM ここからはゲーム中で使用するメインメモリとなり、数値を書き換えることでゲームを進行する。
ゲームボーイカラーではD000~DFFFのバンクを変更可能だが、任意コード実行で使うことはまずない。
E000 FDFF EchoRAM C000~DDFFのミラー領域となっており、二つのアドレスは連動して書き換わる。
環境によっては全く別の数値が入っていることもある。(ポケスタ等)
FE00 FFFF システム領域 ここから先はシステムを管理する数値が入っている。
FF80~FFFEがHRAM(上位RAM)と呼ばれ、常時実行し続けるような命令が入っている。
FFFFは割り込みの有無を切り替える領域。

各種レジスタ

計算や代入を行う数値を格納するうえで使用するのがレジスタであり、ゲームボーイには10個のレジスタが存在する。

b,c,d,eレジスタ

汎用レジスタと呼ばれるもので、各種8bit(0~255/00h~FFh)の値を格納できる。
bcのペアとdeのペアで16bitの演算に使用することもある。(例:ld bc,DCE0)

h,lレジスタ

High/Lowの略らしい。
こちらも汎用レジスタではあるのだが、hlの組み合わせでアドレスの位置を指定することが多い。(例:ldd (hl),a)

aレジスタ

アキュムレータと呼ばれるもので、計算結果を格納するのに用いられる。
特定アドレスの読み書きに多用するため、プログラム構築で使う機会も多い。

fレジスタ

フラグレジスタと呼ばれるもので、上位4bitで演算にまつわる要素をつかさどる。

fレジスタの内容
bit 内容 解説
7 Z ゼロフラグ。演算結果が00hになった場合に1となる。
6 N サブストラクト。直前の演算が引き算なら1、足し算なら0となる。
要は引き算が行われたかを見ている。
5 H ハーフキャリー。計算処理で下位4bitから上位4bitへの繰り上がりや繰り下がりが行われると1となる。
例:0Bhに0Bhを足して16hになったらHが1になる。
4 C キャリー。直前の演算結果で繰り上がり(FFh→00h)や繰り下がり(00h→FFh)があった時に1となる。

プログラムで主に使うのはZとC。条件分岐に使える。
push,pop命令ではaレジスタとセット(af)で扱われる。文字入力ではどちらもできないのであまり使われないが。

キャリーフラグはscf命令で1にすることはできるが、0にする命令が(元となるZ80に)無く、後述の論理演算をした場合に0になることを利用して代用することがある。

spレジスタ

スタックポインタと呼ばれるもので、現在のスタック位置を記録している。
スタックにはサブルーチン実行時の戻り先を記録したり、他のレジスタの値を一時的に退避したりするのに用いられる。

pcレジスタ

プログラムカウンタと呼ばれるもので、次に実行するコードのアドレスを記録している。
通常は実行中のアドレスの次のアドレスが指定されるが、ジャンプ、コール、リターン命令を使うことで行き先を書き換えられる。


命令コード

nop

ノープログラム。何もしない。

00hで表現できるが、文字や道具欄では扱いにくいため、他の無意味になる命令で代用することが通例。
(例:ld a,aやcp a等)


ld命令

ロード命令。基本的にはld (対象1),(対象2)で記述し、対象1の値を対象2で書き換えるという意味になる。
数値の読み込みや書き込みに使用するため、非常に多く使われる命令である。

まずは8bitのロード命令から。(hl)というのはhlレジスタで示したアドレスの中身を参照するものである。

ld命令対応表(8bit)
対象2→
↓対象1
任意 b c d e h l (hl) a
b 06h xx
ガX
40h
41h
42h
43h
44h
45h
46h
47h
c 0Eh xx
ゾX
48h
49h 4Ah 4Bh 4Ch 4Dh 4Eh
改行
4Fh
d 16h xx 50h
終端
51h 52h 53h 54h 55h 56h 57h
e 1Eh xx 58h 59h 5Ah 5Bh 5Ch 5Dh 5Eh 5Fh
h 26h xx
がX
60h 61h 62h 63h 64h 65h 66h 67h
l 2Eh xx
ぜX
68h 69h 6Ah 6Bh 6Ch 6Dh 6Eh 6Fh
(hl) 36h xx 70h 71h 72h 73h 74h 75h - 77h
a 3Eh xx
ぼX
78h 79h 7Ah 7Bh 7Ch 7Dh 7Eh 7Fh
空白

赤字で示したものは入力不能文字に当たるコード。4Ehは第二世代のメールに必ず入る改行となる。そのため、メールコードでのcレジスタの扱いには注意が必要。
このことからわかるように、レジスタ間の直接の代入は(bレジスタ以外)ほぼ行えない。
(例:ョぺぽ[xor a→ld b,a→ld c,b]「AF 47 48」でbcレジスタを0000hにする。xor aについては後述)

無意味な命令として、「パ」(ld b,b)や「空白」(ld a,a)がスペーサーとして使われることがある。
終端というのは、文字列の最後を表すもので、ニックネームを付ける際も末尾に挿入される。(そのため、名前には5文字+終端分の6バイトが使われている)
dレジスタを扱う場合はこれが結構厄介。(終端文字が入るたびにbレジスタの値で上書きされてしまうため)

続いては16bitのロード命令。(ld ○○,yyxx)

ここで記述に注意が必要なのは、下位bit(bcの場合はc)に入れる値から書き始めること。(例:ld de,D2A2なら「11 A2 D2」と記述する)

ld命令対応表(16bit)
対象2→
↓対象1
任意
(yyxx)
bc 01h xx yy
de 11h xx yy
ヅXY
hl 21h xx yy
sp 31h xx yy
ぢXY

赤字で示したものは入力不能文字に当たるコード。

ここで重要なのは、

  • bcとhlは個別のld命令が入力可能なのに対し、deは個別のld命令が入力不能な点。
  • dレジスタは終端文字によってbレジスタの値で上書きされてしまう点。

ニックネームコーディングの場合は「ヅ」を4文字目に入れることで(dレジスタの値を50hにしつつも)ld e,xxの代わりに使用するというテクニックがある。

ld sp,yyxxを使うことはほぼ無い。(初代ポケモンではld sp,DFFFでスタックポインタをDFFFhから始めるようにしている)
一応、ld hl,sp+xx 「F8 xx」でスタックの途中にあるアドレスを参照して書き換えることで、戻り先を変更するという小技がある。
ld sp,hl「F9」というのも一応あるが、push(後述)と違ってret先を直接書き換えてしまうため、元の処理に戻ってくる必要がある任意コード実行とは相性が悪い。

次は特定アドレスからの読み込み及び代入について。

(hl)への読み書きは8bitのロード命令でも触れたが、その他のアドレス指定ロードはaレジスタを使うことになる。
(FF00+xx)は任意の値をFF00hに足した数値を指定するという意味になる。
(FF00+c)はcレジスタの値をFF00hに足した数値を指定するという意味になる。

ld命令対応表(アドレス指定)
指定アドレス 書き込み 読み込み
(bc) 02h 0Ah
(de) 12h
1Ah
(hl) 77h 7Eh
(FF00+xx) E0h xx
ゃX
F0h xx
(FF00+c) E2h
F2h
(yyxx) EAh xx yy
ゥXY
FAh xx yy
4XY

赤字で示したものは入力不能文字に当たるコード。(「ゥ」は第二世代で、「4」はメール本文で入力可能)
(yyxx)への読み書きは「EA xx yy」といったように3バイトを使って記述する命令となる。

これだと一見(hl)からの読み書きができないように見えるが、これらの読み書きには別の命令が存在するのでそちらを使用する。

なお、ld (yyxx),sp「08 xx yy」は、アドレスyyxxにスタックポインタの下位8bit、その一つ後ろ(yyxx+01h)のアドレスに上位8bitの数値を格納することになる。
(例:スタックポインタがDFF5hの場合、ld (D010),sp「08 10 D0」は、D010hにF5h、D011hにDFhを入れる)


ldi、ldd命令

hlレジスタ(で示すアドレスの中身)とaレジスタの間で読み書きを行った後、hlレジスタの数値を操作する命令。
ldiがインクリメント(hlレジスタの値を+01h)、lddがデクリメント(hlレジスタの値を-01h)となる。動くのは基本lレジスタとなるが、繰り上がり/繰り下がりが起こるとhレジスタの値も動く。
本来は連続したデータの読み書きに使用するものだが、ld (hl),a、ld a,(hl)共に入力不能文字のため、1バイトのみの読み書きにもこれを利用せざるをえなかったりする。
(一応、「ョぷア」[xor a→ld b,(hl)→add b]のようにbレジスタ経由で読み込むことはできなくもないが…)

ldi,ldd命令対応表
  (hl),a
(書き込み)
a,(hl)
(読み込み)
ldi 22h 2Ah
ldd 32h
3Ah

赤字で示したものは入力不能文字に当たるコード。

上記の通り、読み込みはldi,ldd共に行えるが、書き込みはlddの方しか入力可能文字で行えない。
そのため、連続したアドレスへの書き込みは下から行うのが通例となっている。(例:ョべづづづづ)

ちなみに道具欄で行う場合、ldd (hl),aが偽ポイントアップになって紛らわしいので、ldi (hl),aになるみずのいしを使う方が無難かもしれない。


push、pop命令

プッシュ、ポップ。これらはスタックポインタ(sp)の値を操作する命令となる。対象となるのはbc、de、hl、afの4種。
レジスタを他の用途に使う際に値を一時的に退避するのが基本的な使い方だが、ret命令(後述)の戻り先を変えるのにも使える。
(ゲーム中のプログラムでは現在のROMバンクとアドレスの値を退避する用途にも使われている)

pushでは(sp-1)と(sp-2)に2つのレジスタの値を挿入し、sp=sp-2とすることで、スタックの上にレジスタの値を積む。
(例:sp=DFF5hの時のpush afは、(DFF4h)=a、(DFF3h)=f、sp=DFF3hとなる)

popでは(sp+1)とspの値を2つのレジスタに代入し、sp=sp+2とすることで、スタックの上からレジスタに値を崩す。
(例:sp=DFF3hの時のpop afは、a=(DFF4h)、f=(DFF3h)、sp=DFF5hとなる)

push、pop命令対応表
  bc de hl af
push C5h
D5h
E5h F5h
pop C1h
D1h
E1h
F1h
×

赤字で示したものは入力不能文字に当たるコード。一応「×」はポケスタ金銀でボックスの名前やメール本文に入力できるようである。
afがpush、popどちらもできないため、aレジスタの値を退避するにはbレジスタを経由する必要が出てくる。
(例:ld b,a→push bc「ぺな」で退避、pop bc→xor a→add a「ちョア」で復帰)


jp、call命令

絶対ジャンプ、コール。指定したアドレスにプログラムを飛ばす命令。3バイトで記述する。(例:jp DE64「C3 64 DE」」

jp命令は文字通り指定したアドレスにプログラムを移動する。

call命令の場合は現在のプログラムカウンタをスタックに積むことで、ret命令で元のプログラムに戻ってくることができるようになる。
具体的には、現在のpc上位8bitを(sp-1)、下位8bitを(sp-2)に代入し、spをsp-2に設定した後、pcに指定した数値を入れる。
同じことを複数個所で行うようなケースで効果を発揮する。

キャリーやゼロフラグを条件にジャンプすることも可能で、特定条件下でプログラムを飛ばすだけでなく、繰り返し処理に利用してコード長の短縮を行える場合がある。
特にポケモンの文字入力では短距離の相対ジャンプ(後述)ができないことが多いため、絶対ジャンプで記述することで短距離の戻りジャンプを実現しているケースが多数ある。
ゼロフラグが1(直前の演算結果が00h)の場合はz、0の場合はnz。キャリーフラグが1(00hとFFhをまたぐような計算が行われた)の場合はc、0の場合はncとなる。

jp、call命令対応表
条件→
↓命令
条件なし nz z nc c
jp yyxx C3h xx yy
てXY
C2h xx yy
つXY
CAh xx yy
はXY
D2h xx yy
めXY
DAh xx yy
れXY
call yyxx CDh xx yy
へXY
C4h xx yy
とXY
CCh xx yy
ふXY
D4h xx yy
やXY
DCh xx yy
わXY

jpやcallはすべて入力可能文字となっているので、ニックネームコーディングでも扱いやすい。ただし、ジャンプ先のアドレスが入力不能文字の場合は一工夫がいる。

これとは別に、hlレジスタで記したアドレスにジャンプできるjp hlという1バイト命令もあるのだが、文字入力では初代で入力できない「ァ」(E9h)になるのがネック。
道具欄ではわざマシン33(初代)、わざマシン41(二世代)であるため、かみなりのいし(21h)でhlレジスタの値を指定してニックネーム部に飛ばすといった使い方をされる場合がある。


jr命令

相対ジャンプ命令。プログラムカウンタ(pc)に指定した数値を足すことで、+-7Fh(127)バイトの間のジャンプを行う。2バイトで記述する。(例:jr 64「メノクラゲ→プリン」)
変更するのがpc(基本的に実行した箇所の次のアドレスを指す)であることから、数値の指定はjr命令を書いた場所の後ろから数える点に注意。
戻りジャンプを用いる場合はFFhから減らしていく。(00hを起点にする)
(例:ldi (hl),a→dec b→jr z,FC「22 0520 FC」で、冒頭のldi (hl),aに飛ばせる。)

相対ジャンプを使うメリットは2バイトで記述できることによるコード長の短縮と、コード自体の移植のしやすさにある。
絶対ジャンプ同様に条件付きのジャンプも可能。
ゼロフラグが1(直前の演算結果が00h)の場合はz、0の場合はnz。キャリーフラグが1(00hとFFhをまたぐような計算が行われた)の場合はc、0の場合はncとなる。

jr命令対応表
条件なし nz z nc c
18h xx 20h xx 28h xx
ぐX
30h xx
だX
38h xx

赤字で示したものは入力不能文字に当たるコード。
条件なしジャンプはまだしもループ処理に便利なnzの条件付きジャンプも入力ができず、戻りジャンプには初代で入力できる最大の数値が「ー」のE3h(-1dh、つまり29バイト)であることが問題となる。
第二世代ではメールでF6h~FFhに当たる数字が入力できるため、短距離の戻りジャンプもいくらか使いやすくなってはいる。
条件なしのjrはメノクラゲやにどげりを使う方が多いかもしれない。第二世代ではアーボックに該当することも覚えておきたい。


ret命令

リターン。本来はサブルーチンから戻るための命令だが、任意コード実行を行う際も道具使用などのサブルーチンに入ってはいるため、retで終了して元の画面に戻る必要がある。(ジャンプ先でretするケースもある)
戻り先がスタックから取られることを利用し、jp hl「E9」の代わりにする運用方法もある。(例:ld bc,DE46→swap c→push bc→ret「01 46 DE CB 31 D5 C9」でjp DE64の代わりにする)

ジャンプ、コール命令同様、リターンにも条件を付けることが可能。
ゼロフラグが1(直前の演算結果が00h)の場合はz、0の場合はnz。キャリーフラグが1(00hとFFhをまたぐような計算が行われた)の場合はc、0の場合はncとなる。

ret命令対応表
条件なし nz z nc c
C9h
C0h
C8h
D0h
D8h

道具ではわざマシン01(初代)、わざマシン10(二世代)がretに該当する。個数201で書かれていることも。
割り込みを有効にしつつリターンするreti「D9」も存在するが、任意コード実行で使うことはまずないと思われる。


rst命令

リスタート…という意味だが、実際のところ頻繁に使うサブルーチンを1バイトで呼び出す命令となる。(call 00xxと同じ意味になる)

初代ではこれらの領域にすべてFFhが入っており、コードとして使うことができない。第二世代ではこの領域にバンク跨ぎコールなどの命令が入っており、うまく使うとコード長の短縮にもなる。

rst命令対応表
00 08 10 18 20 28 30 38
C7h
CFh
D7h
DFh
E7h
EFh
F7h
1
FFh
9

赤字で示したものは入力不能文字に当たるコード。第二世代ではメールの本文で「?」「1」「9」が入力でき、ポケスタ金銀では「♂」も入力可能になる。


inc、dec命令

インクリメントが増加、デクリメントが減少。数値を1ずつ上限させる命令。

各レジスタ及びhlレジスタで示したアドレスの数値に対して使用できるが、00hとFFhをまたいだ時にキャリーフラグが立たないという特徴がある。
8bitレジスタに使用する場合は数値が00hになった時にゼロフラグが立つため、繰り返し処理のカウンターとして使用することができる。
16bitレジスタで使用する場合、00FFhをincすると0100hになったりするが、この時フラグは一切変化しない。これは256回以上のループを行う際に注意が必要。

inc、dec対応表
レジスタ→
↓命令
bc b c de d e hl h l sp (hl) a
inc 03h 04h 0Ch
13h
14h 1Ch
23h 24h 2Ch
33h
34h
3Ch
dec 0Bh
05h
0Dh
1Bh
15h 1Dh 2Bh
25h 2Dh
3Bh
35h 3Dh

赤字で示したものは入力不能文字に当たるコード。
結構不自由が多く、特にhがincもdecもできないせいで困るケースがたびたびある。
inc (hl)は道具などの所持数を増やす際に便利だが、decできないのが惜しい。


add、adc命令

足し算を行う命令。aレジスタの値に他の値を足す。結果はaレジスタ(とフラグレジスタ)に格納される。
計算結果がFFhを超えた場合は繰り上がって00hから増え、キャリーフラグが立つ。
addの場合はキャリーフラグが立つだけだが、adcの場合はキャリーフラグが計算結果に足される。(繰り上がると1増える)

例:a=FBhの時に08hを足す場合、add a,08だとaが03hに、adc a,08だとaが04hになる。(どちらの場合もキャリーフラグは立つ)

add、adc対応表
足すもの→
↓命令
任意
(a,xx)
b c d e h l (hl) a
add C6h xx
にX
80h
81h
82h
83h
84h
85h
86h
87h
adc CEh xx
ほX
88h
89h
8Ah
8Bh
8Ch
8Dh
8Eh
8Fh

aレジスタを00hにしてから各レジスタの値をaddすることで、入力ができないld a,(各レジスタ)の代わりにすることができる。
他には入力不能文字を数値として扱いたい場合にadd a,xxで直前の値から加算する使い方もある。

これとは別にadd (対象1),(対象2)の形で表記する16bit加算命令もある。
これはhlレジスタに対して行うもので、参照するアドレスを一定数動かす用途で使える。

add hl命令対応表
rr(レジスタ)→ bc de hl sp
add hl,rr 09h
19h
29h
39h

赤字で示したものは入力不能文字に当たるコード。
ちなみにadd sp,xx「E8 xx」でspの位置を8bit値で動かすこともできる模様?


sub、sbc命令

引き算を行う命令。aレジスタの値から他の値を引く。結果はaレジスタ(とフラグレジスタ)に格納される。
計算結果が00hを下回った場合は繰り下がってFFhから減り、キャリーフラグが立つ。
subの場合はキャリーフラグが立つだけだが、sbcの場合はキャリーフラグが計算結果から引かれる。

a=00hの時にABhを引く場合、sub a,ABだとaが55hに、sbc a,ABだとaが54hになる。(どちらの場合もキャリーフラグは立つ)

sub、sbc対応表
足すもの→
↓命令
任意
(a,xx)
b c d e h l (hl) a
sub D6h xx
よX
90h
91h
92h
93h
94h
95h
96h
97h
sbc DEh xx
んX
98h
99h
9Ah
9Bh
9Ch
9Dh
9Eh
9Fh

add同様、入力不能文字を数値として扱いたい場合にsub a,xxで直前の値から減算する使い方もある。
sub aやsbc aだとaレジスタが00hになるが、aレジスタのリセットには後述のxor aの方がよく使われている。


scf、ccf命令

それぞれセットキャリーフラグ(scf)、コンプリメントキャリーフラグ(ccf)。
scfはキャリーフラグを1にする命令で、ccfはキャリーフラグを反転(0なら1、1なら0)する命令となる。

その名の通り、キャリーフラグを立てたり折ったりする命令だが、任意コード実行でわざわざ行う必要はない上、scf「37」、ccf「3F」共に入力不能文字なため、使う機会は無いと思われる。
金銀のわざマシン17(分類違い)や図鑑モード06h等、目的のコードにたどり着くまでに他のコードが混じってしまう場面で不意に立ってしまうと困ることがある程度。
(例:scfが混じってしまったためにjr nc,DFが失敗してフリーズ)

キャリーフラグを0にする命令が無いため、後述の論理演算を使ってキャリーフラグを折るという運用方法が(Z80系では)たびたび見られるという。


and、xor、or命令

論理演算と呼ばれるもので、aレジスタと指定した数値またはレジスタをbit比較し、その結果をaレジスタに格納する。

andが論理積(両方が1なら1、どちらか片方でも0なら0)、xorが排他的論理和(両方が異なれば1、同じなら0)、orが論理和(どちらかが1なら1、両方とも0なら0)となる。

例として、72hと21h比較するとそれぞれ以下のようになる。

and

[72h]
0111 0010
0010 0001
[21h]

0010 0000
[20h]

xor

[72h]
0111 0010
0010 0001
[21h]

0101 0011
[53h]

or

[72h]
0111 0010
0010 0001
[21h]

0111 0011
[73h]

ちなみに、aレジスタ同士の比較の場合、and aやor aだとaレジスタの値は変化しないが、a=00hの時にゼロフラグが立つので、条件分岐に利用できる。
(Z80には)論理演算を行うとキャリーフラグが0になるという仕様があるため、キャリーフラグを0にする目的でand aやor aが使われることもあるようだ。
dec bc→ld a,c→or bと組み合わせることで、bcレジスタが0000hになった時にゼロフラグが立つといった運用方法も見られる。
他にはand a,0Fで下位4bitを、and a,F0で上位4bitのみを取り出して使用したり、xor a,02で02hとそれ以外を切り替えるといった使い方もある。
xor aだとaレジスタが00hになるので、aレジスタをクリアする目的でよく使用される。

and、xor、or命令対応表
比較対象→
↓命令
任意
(a,xx)
b c d e h l (hl) a
and E6h xx
?X
A0h
A1h
A2h
A3h
A4h
A5h
A6h
A7h
xor EEh xx A8h
A9h
AAh
ABh
ACh
ADh
AEh
AFh
or F6h xx
0X
B0h
B1h
B2h
B3h
B4h
B5h
B6h
B7h

赤字で示したものは入力不能文字に当たるコード。しかし、EEh以外は第二世代で入力できる。(「?」と「0」はメール本文限定)


cpl命令

コンプリメント。aレジスタのbitをすべて反転(0は1に、1は0に)する命令。
FFhから引くようにすると計算しやすい。

cpl

[72h]
0111 0010

1000 1101
[8Dh]

文字入力では「ぞ」(2Fh)で使用できる。


daa命令

デシマルアジャストアキュムレーター。aレジスタの値を16進数から10進数の形に変換する命令。
実行時にはハーフキャリーフラグやサブストラクトフラグも参照するため、直前に計算が絡む場合はちょっとややこしくなる。

文字入力では「ぎ」(27h)で使用できるが、任意コード実行でわざわざ使う場面は少なそう。

ポケモンではお金やコインの計算にこれを使用しており、初代では10進化した状態で所持金やコインをメモリに格納している。なぜか第二世代では16進数の形で入っているが。


cp命令

コンペア。aレジスタの値と指定した数値やレジスタの値を比較する命令で、数値を引く点はsub命令に似ているが、aレジスタそのものは変化させず、結果によって立つフラグだけが変わる。
主な使い方は条件分岐となるが、cp aを事実上のnopとして使用したり、jr z,xxと組み合わせてjr xxの代わりに使用しているコードもある。(この場合、ゼロフラグが立ち、キャリーフラグが折られる)

cp命令対応表
任意
(a,xx)
b c d e h l (hl) a
FEh xx
8X
B8h
B9h
BAh
BBh
BCh
BDh
BEh
BFh

赤字で示したものは入力不能文字に当たるコード。しかし、FEhは第二世代のメール本文で入力できる。


halt、stop命令

それぞれホールト、ストップ。ともに動作を停止する命令だが、復帰のタイミングが異なる。

halt[76h]は割り込みが起こるまでCPUを停止させる命令。停止中は消費電力を抑えられる。
主に画面描画を待ちたい時に使用するが、文字としては入力不能なので、バイナリエディタを使用して作るような大型のプログラムで使用する程度。

stop「ヂ」[10h]はCPUと画面を休止してスリープモードにする命令で、実行するとかなり長い時間ゲームが止まってしまう(キー入力を受け付けなくなる)らしい?(特に5かい使用時は)このコードが混じらないように気を付けたい。(ニドクインが10hになる)


ei、di命令

eはEnable、dはDisable、iはInterruptsの略で、割り込みの有無を切り替える命令。任意コード実行で使うことはまずない。

ei[FBh]が割り込み有効。基本的にはこの状態にしておく。

di[F3h]が割り込み無効。使うとしてもreti[D9h]と組み合わせるのだろうか。


2バイト命令(CB xx)

CBh「ひ」と組み合わせた2バイトコードでは、ビット操作を行うことができる。
入力不能文字が絡む数値(主にアドレス)を指定したいときに役立つ。
ちなみにbitの位置は0から数えて8桁(上桁からbit7~bit0)と表記する。

rlc、rrc、rl、rr命令

ローテート。各bitを左右に1つずらす命令。一番前の数値が後ろに回る環のイメージで、cの有無はキャリーフラグを環の中に含むかの違い。

 

rlc

[8Ah]
1000 1010

0001 0101
[15h]

rrc

[E3h]
1110 0011

1111 0001
[F1h]

rl

[8Ah]
(C0)1000 1010

(C1)0001 0100
[14h]

※Cはキャリーフラグ

rr

[E3h]
(C0)1110 0011

(C1)0111 0001
[71h]

※Cはキャリーフラグ

※以上の例では桁があふれているため、キャリーフラグは1になる。

rlcおよびrlは左方向のローテート。
rlcだとbit7(左端)の数値がキャリーフラグ及びbit0(右端)に入るが、rlだとbit7(左端)の数値はキャリーフラグのみに入り、元からキャリーフラグが立っていた場合のみbit0(右端)が1になる。

rrcおよびrlは右方向のローテート。
rrcだとbit0(右端)の数値がキャリーフラグ及びbit7(左端)に入るが、rrだとbit0(右端)の数値はキャリーフラグのみに入り、元からキャリーフラグが立っていた場合のみbit7(左端)が1になる。

aレジスタに対しては、rlca、rrca、rla、rraと1バイトで実行できるようになっている。

rlc、rrc、rl、rr命令対応表
レジスタ→
↓命令
b c d e h l (hl) a
rlc CBh 00h CBh 01h CBh 02h CBh 03h CBh 04h CBh 05h
ひガ
CBh 06h
ひギ
07h
rrc CBh 08h
ひゲ
CBh 09h
ひゴ
CBh 0Ah
ひザ
CBh 0Bh
ひジ
CBh 0Ch
ひズ
CBh 0Dh
ひゼ
CBh 0Eh
ひゾ
0Fh
rl CBh 10h
ひヂ
CBh 11h
ひヅ
CBh 12h
ひデ
CBh 13h
ひド
CBh 14h CBh 15h CBh 16h 17h
rr CBh 18h CBh 19h
ひバ
CBh 1Ah
ひビ
CBh 1Bh
ひブ
CBh 1Ch
ひボ
CBh 1Dh CBh 1Eh 1Fh

赤字で示したものは入力不能文字に当たるコード。
rl及びrrはキャリーフラグの状態を正しく把握していないと使うのが難しいだけでなく、rla、rra共に入力不能文字なのもあって任意コード実行ではあまり使われない。
せいぜい後述のslaができないb、c、d、e、hレジスタの値を2倍にしたいときに使う程度。
端のbitがキャリーフラグに入ることを利用し、bit命令の代わりにrlcaやrrcaを使うことでコード長を短縮するテクニックもある。


sla、srl、sla命令

シフト。各bitを左右に一つずらす命令。ローテートとの違いは溢れた桁が反対側に行かない点。

sla

[8Ah]
1000 1010

0001 0100
[14h]

srl

[E3h]
1110 0011

0110 0001
[71h]

sra

[E3h]
1110 0011

1011 0001
[B1h]

※以上の例では桁があふれているため、キャリーフラグは1になる。

slaは左方向へのシフト。bit7(左端)はキャリーフラグに入り、、bit0(右端)は必ず0になる。
数値を倍にしたい場合に用いられる。

srlは右方向へのシフト。bit0(右端)はキャリーフラグに入り、bit7(左端)は必ず0になる。
数値を半分にしたい場合に用いられる。(端数は切り捨てになるが)

sraも右方向のシフトだが、こちらはbit7(左端)を変化させない。
本来はbit7を符号ビット(正か負かを表すもの)として扱う場合に使う右シフトとなる。

sla、srl、sra命令対応表
レジスタ b c d e h l (hl) a
sla CBh 20h CBh 21h CBh 22h CBh 23h CBh 24h CBh 25h CBh 26h
ひが
CBh 27h
ひぎ
srl CBh 38h CBh 39h CBh 3Ah
ひば
CBh 3Bh
ひび
CBh 3Ch
ひぶ
CBh 3Dh
ひべ
CBh 3Eh
ひぼ
CBh 3Fh
sra CBh 28h
ひぐ
CBh 29h
ひげ
CBh 2Ah
ひご
CBh 2Bh
ひざ
CBh 2Ch
ひじ
CBh 2Dh
ひず
CBh 2Eh
ひぜ
CBh 2Fh
ひぞ

赤字で示したものは入力不能文字に当たるコード。
数値を作る上ではrlcやrrcで十分なことが多いためあまり使われないが、ミニゲームを作ったりする場合は結構便利かも?


swap命令

スワップ。1バイトの内、上位4bitと下位4bitを反転させる命令。

swap

[81h]
1000 0001

0001 1000
[18h]

ポケモンのゲーム中では個体値の一部を取り出す過程で使われているとか。
数値の変化が直感的にわかりやすいためか、swap dで数値を作っているコードがたびたび見られる。

swap命令対応表
b c d e h l (hl) a
CBh 30h
ひだ
CBh 31h
ひぢ
CBh 32h
ひづ
CBh 33h
ひで
CBh 34h
ひど
CBh 35h CBh 36h CBh 37h

赤字で示したものは入力不能文字に当たるコード。
aレジスタに対して使えないのが結構痛い。


bit命令

ビット。各レジスタ(またはアドレス内の値)の特定bitを見て、0だった場合にゼロフラグを立てる命令。条件分岐に使用する。

bit命令対応表
bit→
↓レジスタ
7
(80h)
6
(40h)
5
(20h)
4
(10h)
3
(08h)
2
(04h)
1
(02h)
0
(01h)
b CBh 78h CBh 70h CBh 68h CBh 60h CBh 58h CBh 50h
ひ(終端)
CBh 48h
ひぽ
CBh 40h
ひパ
c CBh 79h CBh 71h CBh 69h CBh 61h CBh 59h CBh 51h CBh 49h CBh 41h
ひピ
d CBh 7Ah CBh 72h CBh 6Ah CBh 62h CBh 5Ah CBh 52h CBh 4Ah CBh 42h
ひプ
e CBh 7Bh CBh 73h CBh 6Bh CBh 63h CBh 5Bh CBh 53h CBh 4Bh CBh 43h
ひポ
h CBh 7Ch CBh 74h CBh 6Ch CBh 64h CBh 5Ch CBh 54h CBh 4Ch CBh 44h
ひぱ
l CBh 7Dh CBh 75h CBh 6Dh CBh 65h CBh 5Dh CBh 55h CBh 4Dh CBh 45h
ひぴ
(hl) CBh 7Eh CBh 76h CBh 6Eh CBh 66h CBh 5Eh CBh 56h CBh 4Eh
ひ(改行)
CBh 46h
ひぷ
a CBh 7Fh
ひ(空白)
CBh 77h CBh 6Fh CBh 67h CBh 5Fh CBh 57h CBh 4Fh CBh 47h
ひぺ

赤字で示したものは入力不能文字に当たるコード。4Ehは第二世代のメール本文に入る改行が該当する。
入力不能文字が多すぎて条件分岐の目的で使うのは困難。むしろ終端除けや改行避けとして使われている印象。
そもそもbitごとに条件分岐をしたいのはキー入力やイベントフラグぐらいなのだが、(数値は使い捨てになるが)rlcaを使った方がコード長を短くできる。


set、res命令

それぞれセット、リセット。各レジスタ(またはアドレス内の値)の特定bitをsetでは立て、resでは折る。
入力不能文字が絡む数値の作成にも使えるが、すでに立っているbitにsetをしても無意味だったりするなど、純粋な加減算目的で使用することはできない。

set、res命令対応表
ビット (80h)
7
(40h)
6
(20h)
5
(10h)
4
(08h)
3
(04h)
2
(02h)
1
(01h)
0
b set CBh F8h
ひ2
CBh F0h CBh E8h
ひ。
CBh E0h
ひゃ
CBh D8h
ひリ
CBh D0h
ひみ
CBh C8h
ひね
CBh C0h
ひた
res CBh B8h
ひく
CBh B0h
ひィ
CBh A8h
ひロ
CBh A0h
ひメ
CBh 98h
ひノ
CBh 90h
ひチ
CBh 88h
ひケ
CBh 80h
ひア
c set CBh F9h
ひ3
CBh F1h
ひ×
CBh E9h
ひァ
CBh E1h
ひゅ
CBh D9h
ひる
CBh D1h
ひむ
CBh C9h
ひの
CBh C1h
ひち
res CBh B9h
ひけ
CBh B1h
ひあ
CBh A9h
ひワ
CBh A1h
ひモ
CBh 99h
ひハ
CBh 91h
ひツ
CBh 89h
ひコ
CBh 81h
ひイ
d set CBh FAh
ひ4
CBh F2h
ひ.
CBh EAh
ひゥ
CBh E2h
ひょ
CBh DAh
ひれ
CBh D2h
ひめ
CBh CAh
ひは
CBh C2h
ひつ
res CBh BAh
ひこ
CBh B2h
ひい
CBh AAh
ひヲ
CBh A2h
ひヤ
CBh 9Ah
ひヒ
CBh 92h
ひテ
CBh 8Ah
ひサ
CBh 82h
ひウ
e set CBh FBh
ひ5
CBh F3h
ひ/
CBh EBh
ひェ
CBh E3h
ひー
CBh DBh
ひろ
CBh D3h
ひも
CBh CBh
ひひ
CBh C3h
ひて
res CBh BBh
ひさ
CBh B3h
ひう
CBh ABh
ひン
CBh A3h
ひユ
CBh 9Bh
ひフ
CBh 93h
ひト
CBh 8Bh
ひシ
CBh 83h
ひエ
h set CBh FCh
ひ6
CBh F4h
ひォ
CBh ECh CBh E4h CBh DCh
ひわ
CBh D4h
ひや
CBh CCh
ひふ
CBh C4h
ひと
res CBh BCh
ひし
CBh B4h
ひえ
CBh ACh
ひッ
CBh A4h
ひヨ
CBh 9Ch
ひホ
CBh 94h
ひナ
CBh 8Ch
ひス
CBh 84h
ひオ
l set CBh FDh
ひ7
CBh F5h
ひ♀
CBh EDh CBh E5h CBh DDh
ひを
CBh D5h
ひゆ
CBh CDh
ひへ
CBh C5h
ひな
res CBh BDh
ひす
CBh B5h
ひお
CBh ADh
ひャ
CBh A5h
ひラ
CBh 9Dh
ひマ
CBh 95h
ひニ
CBh 8Dh
ひセ
CBh 85h
ひカ
(hl) set CBh FEh
ひ8
CBh F6h
ひ0
CBh EEh CBh E6h
ひ?
CBh DEh
ひん
CBh D6h
ひよ
CBh CEh
ひほ
CBh C6h
ひに
res CBh BEh
ひせ
CBh B6h
ひか
CBh AEh
ひュ
CBh A6h
ひル
CBh 9Eh
ひミ
CBh 96h
ひヌ
CBh 8Eh
ひソ
CBh 86h
ひキ
a set CBh FFh
ひ9
CBh F7h
ひ1
CBh EFh
ひ♂
CBh E7h
ひ!
CBh DFh
ひっ
CBh D7h
ひら
CBh CFh
ひま
CBh C7h
ひぬ
res CBh BFh
ひそ
CBh B7h
ひき
CBh AFh
ひョ
CBh A7h
ひレ
CBh 9Fh
ひム
CBh 97h
ひネ
CBh 8Fh
ひタ
CBh 87h
ひク

赤字で示したものは入力不能文字に当たるコード。
しかし、「ヲ」「を」「ァ」「ィ」「ゥ」「ェ」「ォ」は第二世代で、数字および「?」「!」「/」はメールで、「♂」「♀」「×」「。」「.」はポケスタ金銀で入力可能。
resはほぼすべてのレジスタで行えるが、setは上位bitでの使用に制限が付く。
res 8,a、res 8,h、res 8,lあたりは使い勝手がいい。


タグ:

+ タグ編集
  • タグ:
最終更新:2024年04月26日 09:34