Z80ハードウェアエミュレータを通して「CPUとは何か」を語るトークの資料です
PHP Conference Japan 2022
続CPUとは何か - ハードウェアエミュレータから見たCPU
https://fortee.jp/phpcon-2022/proposal/c184ec3f-27dd-4e2c-a51c-6b7608b46d2a
以下のトークの続編です
PHP Conference Japan 2019
「CPUとは何か」をPHPで考える
https://fortee.jp/phpcon-2019/speaker/proposal/view/621ab238-115c-4fdc-8d43-0e9a67e188d5
長谷川智希 @tomzoh
2022/09/24 PHP Conference Japan 2022
続CPUとは何か
ハードウェアエミュレータから見たCPU
2
୩ஐر
͕ͤΘ ͱ͖
@tomzoh
http://www.dgcircus.com
デジタルサーカス株式会社 副団長CTO
ॴଐ ٕज़ΧϯϑΝϨϯεओ࠻
دߘɾஶॻ
来たれ!PHPer!We are hiring!
2
୩ஐر
͕ͤΘ ͱ͖
@tomzoh
http://www.dgcircus.com
デジタルサーカス株式会社 副団長CTO
ॴଐ ٕज़ΧϯϑΝϨϯεओ࠻
دߘɾஶॻ
来たれ!PHPer!We are hiring!
4PGUXBSF%FTJHO݄߸
ʮ$16ͱԿͩΖ͏͔ʯ
3
୩ஐر
͕ͤΘ ͱ͖
@tomzoh
ςοΫΧϯϑΝϨϯεӡӦࢀՃ
8FCJ04ΞϓϦ։ൃ
$16
ϨτϩήʔϜػ
ిࢠ࡞
Ϗʔϧ
αοΧʔ؍ઓ
ϨϯλϧΧʔτϨʔε
ʜ
ϥΠϑϫʔΫ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 4
続CPUとは何か
ハードウェアエミュレータから見たCPU
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
前回までのあらすじ
PHP Conference Japan 2019
• 「CPUとは何か」をPHPで考える
• CPUを3つの視点で見た
• プログラム実行環境
• エミュレート対象
• 電気回路
5
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 1: プログラム実行環境
6
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 1: プログラム実行環境
• 対象CPUで動くプログラムを書く
6
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 1: プログラム実行環境
• 対象CPUで動くプログラムを書く
• CPUをプログラム実行環境として見ると:
6
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 1: プログラム実行環境
• 対象CPUで動くプログラムを書く
• CPUをプログラム実行環境として見ると:
• CPUはプログラム言語の様に見える
6
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 1: プログラム実行環境
• 対象CPUで動くプログラムを書く
• CPUをプログラム実行環境として見ると:
• CPUはプログラム言語の様に見える
• CPUの命令セットを理解し、プログラムを書く
6
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
• CPUエミュレータと外部との境界は曖昧
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
• CPUエミュレータと外部との境界は曖昧
• CPUエミュレータ単体でなくコンピュータのエミュレータの中の1コンポーネントとして
実装される / グラフィックやサウンド、入出力との境界は曖昧
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
• CPUエミュレータと外部との境界は曖昧
• CPUエミュレータ単体でなくコンピュータのエミュレータの中の1コンポーネントとして
実装される / グラフィックやサウンド、入出力との境界は曖昧
• Hardware speci
fi
cation as Code
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
• CPUエミュレータと外部との境界は曖昧
• CPUエミュレータ単体でなくコンピュータのエミュレータの中の1コンポーネントとして
実装される / グラフィックやサウンド、入出力との境界は曖昧
• Hardware speci
fi
cation as Code
• エミュレータのソースを読むとハードウェアの仕様がわかる
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 2: エミュレート対象
• 対象CPUのエミュレータを作る
• CPUをエミュレート対象として見ると:
• 他人の書いたプログラムを受け入れる存在に見える
• CPUエミュレータと外部との境界は曖昧
• CPUエミュレータ単体でなくコンピュータのエミュレータの中の1コンポーネントとして
実装される / グラフィックやサウンド、入出力との境界は曖昧
• Hardware speci
fi
cation as Code
• エミュレータのソースを読むとハードウェアの仕様がわかる
• Undocumentedな仕様もエミュレータのソースには書いてある
7
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
• クロック ➡ CPUの状態変化
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
• クロック ➡ CPUの状態変化
• PC(プログラムカウンタ)が示すメモリアドレスから命令をフェッチする(読み込む)
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
• クロック ➡ CPUの状態変化
• PC(プログラムカウンタ)が示すメモリアドレスから命令をフェッチする(読み込む)
• フェッチした内容によってデータフロー・演算器のモードが決定される
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
• クロック ➡ CPUの状態変化
• PC(プログラムカウンタ)が示すメモリアドレスから命令をフェッチする(読み込む)
• フェッチした内容によってデータフロー・演算器のモードが決定される
• 演算器の出力がレジスタやメモリに格納される
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU 3つの視点 - 3: 電気回路
• 対象CPUを電気回路として作る
• CPUを電気回路として見ると:
• 外部から供給されるクロックによって内部状態が切り替わる状態装置に見える
• クロック: 心臓の鼓動
• クロック ➡ CPUの状態変化
• PC(プログラムカウンタ)が示すメモリアドレスから命令をフェッチする(読み込む)
• フェッチした内容によってデータフロー・演算器のモードが決定される
• 演算器の出力がレジスタやメモリに格納される
• プログラム実行環境, エミュレータの様な1本の流れとしての実行ではなく
複数の回路が同時に切り替わる形での実行
8
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
• 4つ目の視点
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
• 4つ目の視点
• ハードウェアエミュレータから見たCPU
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
• 4つ目の視点
• ハードウェアエミュレータから見たCPU
• コンピュータのCPUのフリをする
ハードウェア
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
今回のおはなし
PHP Conference Japan 2022
• 4つ目の視点
• ハードウェアエミュレータから見たCPU
• コンピュータのCPUのフリをする
ハードウェア
• ハードウェアエミュレータを作るとCPU
がどう見えるか
9
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80のハードウェアエミュレータ
10
今回作ったもの
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Zilog Z80
11
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Zilog Z80
• 1976年に米国ザイログ社より発表 現在も製造されている
• PC-8801(1981-1989), X1(1982-1988), MSX(1983-1990)
• セガ・マークIII(1985), セガ・マスターシステム(1987), メガドライブ(サブ・1988), ゲ
ームボーイ(1989), ゲームギア(1990), ネオジオ(サブ・1991)
• ギャラクシアン(1979), パックマン(1980), ディグダグ(1982), ゼビウス(1983),
1942(1984), 戦場の狼(1985), 魔界村(サウンド・1985), ファイナルファイト(サウンド・
1989), ストリートファイターII(サウンド・1991), …
• 競合: MC6809(モトローラ), MOS 6502(モステクノロジー)
12
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 13
https://ja.wikipedia.org/wiki/パチンコ
https://ja.wikipedia.org/wiki/パチスロ
https://www.msx.org/wiki/Sony_HB-F1XDJ https://ja.wikipedia.org/wiki/PC-8800シリーズ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 シングルボードコンピュータ SBCZ80
14
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
15
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
• Z80で動いているシステムからZ80を抜いて
自作のハードウェアを挿入して動作させる
15
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
• Z80で動いているシステムからZ80を抜いて
自作のハードウェアを挿入して動作させる
15
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
• Z80で動いているシステムからZ80を抜いて
自作のハードウェアを挿入して動作させる
15
Z80を抜く
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
• Z80で動いているシステムからZ80を抜いて
自作のハードウェアを挿入して動作させる
15
Z80を抜く
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ
• Z80で動いているシステムからZ80を抜いて
自作のハードウェアを挿入して動作させる
15
Z80を抜く
自作ハードウェアを
挿入して動作させる
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80
Z80 ハードウェアエミュレータのレイヤ構造
16
Z80コンピュータ (SBCZ80)
ΞυϨεόε σʔλόε ੍ޚ৴߸
RAM ROM SERIAL
PORT
CLOCK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェアエミュレータのレイヤ構造
16
Z80コンピュータ (SBCZ80)
ΞυϨεόε σʔλόε ੍ޚ৴߸
RAM ROM SERIAL
PORT
CLOCK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80ハードウェアエミュレータ
Raspberry Pi GPIO
Z80 エミュレータ
電圧変換 / ラッチ
Z80 ハードウェアエミュレータのレイヤ構造
16
Z80コンピュータ (SBCZ80)
ΞυϨεόε σʔλόε ੍ޚ৴߸
RAM ROM SERIAL
PORT
CLOCK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80ハードウェアエミュレータ
Raspberry Pi GPIO
Z80 エミュレータ
電圧変換 / ラッチ
Z80 ハードウェアエミュレータのレイヤ構造
16
Z80コンピュータ (SBCZ80)
ΞυϨεόε σʔλόε ੍ޚ৴߸
RAM ROM SERIAL
PORT
CLOCK
ソフトウェアエミュレータ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80ハードウェアエミュレータ
Raspberry Pi GPIO
Z80 エミュレータ
電圧変換 / ラッチ
Z80 ハードウェアエミュレータのレイヤ構造
16
Z80コンピュータ (SBCZ80)
ΞυϨεόε σʔλόε ੍ޚ৴߸
RAM ROM SERIAL
PORT
CLOCK
物理インターフェース
ソフトウェアエミュレータ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
物理的インターフェース(I/F)
17
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
• 40本のピン
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
• 40本のピン
• そのうち2本は電源なので38本の信号線
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
• 40本のピン
• そのうち2本は電源なので38本の信号線
• 16本のアドレス信号線(16bit), 8本のデータ信号線(8bit)
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
• 40本のピン
• そのうち2本は電源なので38本の信号線
• 16本のアドレス信号線(16bit), 8本のデータ信号線(8bit)
• 8本の出力信号線, 6本の入力信号線
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80の物理I/F
• 40本のピン
• そのうち2本は電源なので38本の信号線
• 16本のアドレス信号線(16bit), 8本のデータ信号線(8bit)
• 8本の出力信号線, 6本の入力信号線
• 38本の信号線の0/1(電圧)をエミュレートすれば良い
18
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
19
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
19
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
• ソフトウェアで入出力できるGPIOを28本持つ
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
• ソフトウェアで入出力できるGPIOを28本持つ
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
• ソフトウェアで入出力できるGPIOを28本持つ
• 制御はC, Pythonがラク
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
• ソフトウェアで入出力できるGPIOを28本持つ
• 制御はC, Pythonがラク
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
gpioWrite(0, 1);
bool value = gpioRead(0);
C言語
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi の物理I/F
• Raspberry Pi は Linux が動作する ARM CPU 搭載のコンピュータ
• ソフトウェアで入出力できるGPIOを28本持つ
• 制御はC, Pythonがラク
• PHPでもできる (おすすめはしない)
19
https://ja.wikipedia.org/wiki/Raspberry_Pi
gpioWrite(0, 1);
bool value = gpioRead(0);
C言語
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
• 電圧変換が必要
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
• 電圧変換が必要
• Raspberry Pi はGPIOを28本持つ, Z80は信号線を38本持つ
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
• 電圧変換が必要
• Raspberry Pi はGPIOを28本持つ, Z80は信号線を38本持つ
• 足りない
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
• 電圧変換が必要
• Raspberry Pi はGPIOを28本持つ, Z80は信号線を38本持つ
• 足りない
• 出力信号線はラッチを使って共有する
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Raspberry Pi と Z80 コンピュータの接続
• Raspberry Pi が Z80 のフリをするには工夫が必要
• Raspberry Pi GPIOは3.3V, Z80は5V
• 電圧変換が必要
• Raspberry Pi はGPIOを28本持つ, Z80は信号線を38本持つ
• 足りない
• 出力信号線はラッチを使って共有する
• ラッチ: 入力値を保持する電気回路
20
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
ラッチ
コントロール
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
5V 3.3V 変換
双方向
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
ラッチ
コントロール
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
5V 3.3V 変換
(Z80への)
入力
5V 3.3V 変換
双方向
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
ラッチ
コントロール
(Z80からの)
出力
3 x GPIO
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
5V 3.3V 変換
(Z80への)
入力
5V 3.3V 変換
双方向
GPIO不足の解消
21
Raspberry Pi
8 x GPIO
8 x GPIO
6 x GPIO
3 x GPIO
ラッチ1
ラッチ2
ラッチ3
ラッチ
コントロール
(Z80からの)
出力
3 x GPIO ͋·Γ
28 x GPIO 38pin IC
16 x Address
8 x Data
Z80
8 x Control
6 x Control
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアからソフトウェアへ
22
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアからソフトウェアへ
• Raspberry Pi が(ハードウェア的に)Z80のフリをできる様になった
22
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアからソフトウェアへ
• Raspberry Pi が(ハードウェア的に)Z80のフリをできる様になった
• 38本の信号線をソフトウェア的にコントロールできるようになった
22
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアからソフトウェアへ
• Raspberry Pi が(ハードウェア的に)Z80のフリをできる様になった
• 38本の信号線をソフトウェア的にコントロールできるようになった
• 38本の信号線を適切に制御(=Z80エミュレート)すれば良い
22
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアからソフトウェアへ
• Raspberry Pi が(ハードウェア的に)Z80のフリをできる様になった
• 38本の信号線をソフトウェア的にコントロールできるようになった
• 38本の信号線を適切に制御(=Z80エミュレート)すれば良い
• 適切とは?
22
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
• 信号線の意味、CPUの動作と信号線が変化するタイミング
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
• 信号線の意味、CPUの動作と信号線が変化するタイミング
• メモリから命令を読み込む
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
• 信号線の意味、CPUの動作と信号線が変化するタイミング
• メモリから命令を読み込む
• メモリからデータを読み込む / 書き込む
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
• 信号線の意味、CPUの動作と信号線が変化するタイミング
• メモリから命令を読み込む
• メモリからデータを読み込む / 書き込む
• 周辺装置からデータを読み込む / データを書き込む
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
信号線のエミュレート
• Z80の38本の信号線の仕様はZ80のマニュアルに書いてある
• ザイログ社がPDFで公開している
• さすが現役CPU
• 信号線の意味、CPUの動作と信号線が変化するタイミング
• メモリから命令を読み込む
• メモリからデータを読み込む / 書き込む
• 周辺装置からデータを読み込む / データを書き込む
• 周辺装置から割り込みを受け付ける
23
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
例えば: メモリリード
24
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
1. 読み込むアドレスを出力する
①
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
1. 読み込むアドレスを出力する
2. MREQ, RDを有効(0)にする
① ②
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
1. 読み込むアドレスを出力する
2. MREQ, RDを有効(0)にする
3. メモリがWAITを無効(0)に
するまで間待つ
① ② ③
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
1. 読み込むアドレスを出力する
2. MREQ, RDを有効(0)にする
3. メモリがWAITを無効(0)に
するまで間待つ
4. データを取り込む
① ② ③ ④
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 25
メモリからの読み込み
1. 読み込むアドレスを出力する
2. MREQ, RDを有効(0)にする
3. メモリがWAITを無効(0)に
するまで間待つ
4. データを取り込む
5. MREQ, RDを無効化(1)する
① ② ③ ④ ⑤
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリリードのエミュレート
26
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリリードのエミュレート
• 仕様書に書いてあるとおりに各信号線を上げ下げすれば良い
26
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリリードのエミュレート
• 仕様書に書いてあるとおりに各信号線を上げ下げすれば良い
• 愚直にやるシリーズ
26
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
3. クロックの立ち下がり(1→0になる)を待つ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
3. クロックの立ち下がり(1→0になる)を待つ
4. MREQ, RDを有効にする(0にする)
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
3. クロックの立ち下がり(1→0になる)を待つ
4. MREQ, RDを有効にする(0にする)
5. WAITが無効になる(0になる)のを待つ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
3. クロックの立ち下がり(1→0になる)を待つ
4. MREQ, RDを有効にする(0にする)
5. WAITが無効になる(0になる)のを待つ
6. データバスの信号を読み取る
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
愚直に実装したメモリリード
27
uint8_t Mcycle::m2(Cpu* cpu, uint16_t addr){
// T1
cpu->bus->waitClockRising();
cpu->bus->setAddress(addr);
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_LOW;
cpu->bus->pin_o_rd = Bus::PIN_LOW;
cpu->bus->syncControl();
// T2
cpu->bus->waitClockRising();
cpu->bus->waitClockFalling();
while (!cpu->bus->getInput(
Bus::Z80_PIN_I_WAIT)){
cpu->bus->waitClockFalling();
}
// T3
cpu->bus->waitClockRising();
uint8_t data = cpu->bus->getData();
cpu->bus->waitClockFalling();
cpu->bus->pin_o_mreq = Bus::PIN_HIGH;
cpu->bus->pin_o_rd = Bus::PIN_HIGH;
cpu->bus->syncControl();
Log::mem_read(cpu, addr, data);
return data;
}
1. クロックの立ち上がり(0→1になる)を待つ
2. アドレスバスに読み込むアドレスを出力する
3. クロックの立ち下がり(1→0になる)を待つ
4. MREQ, RDを有効にする(0にする)
5. WAITが無効になる(0になる)のを待つ
6. データバスの信号を読み取る
7. MREQ, RDを無効にする(1にする)
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアI/F完成
28
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
• メモリの読み書き = 配列の読み書き
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
• メモリの読み書き = 配列の読み書き
• Z80 ハードウェア エミュレータ ではメモリアクセスは電気的に表現されている
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
• メモリの読み書き = 配列の読み書き
• Z80 ハードウェア エミュレータ ではメモリアクセスは電気的に表現されている
• メモリの読み書き = 信号線の制御
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
• メモリの読み書き = 配列の読み書き
• Z80 ハードウェア エミュレータ ではメモリアクセスは電気的に表現されている
• メモリの読み書き = 信号線の制御
• CPU機能実装のレイヤーから見るとその差は無い
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPU機能の実装
• ここからはただの(?)エミュレータの世界
• ソフトウェアエミュレータではメモリを配列などで表現していた
• メモリの読み書き = 配列の読み書き
• Z80 ハードウェア エミュレータ ではメモリアクセスは電気的に表現されている
• メモリの読み書き = 信号線の制御
• CPU機能実装のレイヤーから見るとその差は無い
• 既存のZ80エミュレータの知見を活かせる
29
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
30
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
• 全命令だと3,000行弱
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
• 全命令だと3,000行弱
• ひたすら地道に書いた
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
• 全命令だと3,000行弱
• ひたすら地道に書いた
• これからやる方はイチから書くのはお勧めしない
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
• 全命令だと3,000行弱
• ひたすら地道に書いた
• これからやる方はイチから書くのはお勧めしない
• ツライだけで(30個ぐらい以降は)あまり楽しくない
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令を実装する
• 左のリストはZ80の命令 0x00 〜 0xff まで
• 256個の case 文 1,300行ぐらい
• 実際は少し工夫してるのでもう少し少ない
• 工夫しない方が速いことも
• 全命令だと3,000行弱
• ひたすら地道に書いた
• これからやる方はイチから書くのはお勧めしない
• ツライだけで(30個ぐらい以降は)あまり楽しくない
• 既存の実装をベースに書くのがオススメ
30
void OpCode::execute(uint8_t opCode){
if ((opCode >> 6) == 0b01 && (opCode & 0b00111000) != 0b00110000 && (opCode & 0b00000111) != 0b00000110){
// ld r, r'
Log::execute(this->_cpu, opCode, "ld r, r'");
uint8_t *reg = this->targetRegister(opCode, 3);
uint8_t *reg_dash = this->targetRegister(opCode, 0);
*reg = *reg_dash;
Log::dump_registers(this->_cpu);
return;
}
switch (opCode){
case 0x00: // nop
Log::execute(this->_cpu, opCode, "nop");
break;
case 0x01: // ld bc, nn
Log::execute(this->_cpu, opCode, "ld bc, nn");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x02: { // ld (bc), a
Log::execute(this->_cpu, opCode, "ld (bc), a");
Mcycle::m3(this->_cpu, this->_cpu->registers.bc(), this->_cpu->registers.a);
break;
}
case 0x03: // inc bc
Log::execute(this->_cpu, opCode, "inc bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() + 1);
break;
case 0x04: // inc r
case 0x0C:
case 0x14:
case 0x1C:
case 0x24:
case 0x2C:
case 0x3C: {
Log::execute(this->_cpu, opCode, "inc r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByIncrement(*reg);
(*reg)++;
break;
}
case 0x05: // dec r
case 0x0D:
case 0x15:
case 0x1D:
case 0x25:
case 0x2D:
case 0x3D: {
Log::execute(this->_cpu, opCode, "dec r");
uint8_t *reg = this->targetRegister(opCode, 3);
this->setFlagsByDecrement(*reg);
(*reg)--;
break;
}
case 0x06: // ld r, n
case 0x0e:
case 0x16:
case 0x1e:
case 0x26:
case 0x2e:
case 0x3e: {
Log::execute(this->_cpu, opCode, "ld r, n");
uint8_t* reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
break;
}
case 0x07: { // rlca
Log::execute(this->_cpu, opCode, "rlca");
bool carry_bit = (this->_cpu->registers.a >> 7);
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_bit;
this->_cpu->registers.FC_Carry = carry_bit;
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
break;
}
case 0x08: { // ex af, af'
Log::execute(this->_cpu, opCode, "ex af, af'");
uint16_t af = this->_cpu->registers.af();
this->_cpu->registers.af(this->_cpu->registers_alternate.af());
this->_cpu->registers_alternate.af(af);
break;
}
case 0x09: // add hl, rr
case 0x19:
case 0x29:
case 0x39: {
Log::execute(this->_cpu, opCode, "add hl, rr");
uint16_t value;
switch (opCode){ // NOLINT(hicpp-multiway-paths-covered)
case 0x09: value = this->_cpu->registers.bc(); break;
case 0x19: value = this->_cpu->registers.de(); break;
case 0x29: value = this->_cpu->registers.hl(); break;
case 0x39: value = this->_cpu->special_registers.sp; break;
}
this->setFlagsByAdd16(this->_cpu->registers.hl(), value);
this->_cpu->registers.hl(this->_cpu->registers.hl() + value);
break;
}
case 0x0A: // ld a,(bc)
Log::execute(this->_cpu, opCode, "ld a,(bc)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.bc());
break;
case 0x0B: // dec bc
Log::execute(this->_cpu, opCode, "dec bc");
this->_cpu->registers.bc(this->_cpu->registers.bc() - 1);
break;
case 0x0F: { // rrca
Log::execute(this->_cpu, opCode, "rrca");
bool carry_bit = ((this->_cpu->registers.a & 1) > 0);
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) + ((this->_cpu->registers.a & 1) << 7);
this->_cpu->registers.FH_HalfCarry = false;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FC_Carry = carry_bit;
break;
}
case 0x10: { // djnz n
Log::execute(this->_cpu, opCode, "djnz n");
this->_cpu->registers.b--;
if (this->_cpu->registers.b != 0){
auto diff = (int8_t)Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
}
case 0x11: // ld de, nn
Log::execute(this->_cpu, opCode, "ld de, nn");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1);
this->_cpu->special_registers.pc += 2;
break;
case 0x12: // ld (de),a
Log::execute(this->_cpu, opCode, "ld (de),a");
Mcycle::m3(this->_cpu, this->_cpu->registers.de(), this->_cpu->registers.a);
break;
case 0x13: // inc de
Log::execute(this->_cpu, opCode, "inc de");
this->_cpu->registers.de(this->_cpu->registers.de() + 1);
break;
case 0x17: { // rla
Log::execute(this->_cpu, opCode, "rla");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a >> 7;
this->_cpu->registers.a = (this->_cpu->registers.a << 1) | carry_flg;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x18: { // jr n
Log::execute(this->_cpu, opCode, "jr n");
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
break;
}
case 0x1A: // ld a,(de)
Log::execute(this->_cpu, opCode, "ld a,(de)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.de());
break;
case 0x1B: // dec de
Log::execute(this->_cpu, opCode, "dec de");
this->_cpu->registers.de(this->_cpu->registers.de() - 1);
break;
case 0x1F: { // rra
Log::execute(this->_cpu, opCode, "rra");
bool carry_flg = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = this->_cpu->registers.a & 1;
this->_cpu->registers.a = (this->_cpu->registers.a >> 1) | (carry_flg << 7);
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
}
case 0x20: // jr nz, n
Log::execute(this->_cpu, opCode, "jr nz, n");
if (! this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x21: // ld hl, nn
Log::execute(this->_cpu, opCode, "ld hl, nn");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8)
);
this->_cpu->special_registers.pc += 2;
break;
case 0x22: { // ld (nn), hl
Log::execute(this->_cpu, opCode, "ld (nn), hl");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.l);
Mcycle::m3(this->_cpu, addr + 1, this->_cpu->registers.h);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x23: // inc hl
Log::execute(this->_cpu, opCode, "inc hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() + 1);
break;
case 0x27: { // daa
Log::execute(this->_cpu, opCode, "daa");
uint8_t cr = 0;
if ((this->_cpu->registers.a & 0x0f) > 0x09 || this->_cpu->registers.FH_HalfCarry){
cr += 0x06;
}
if (this->_cpu->registers.a > 0x99 || this->_cpu->registers.FC_Carry){
cr += 0x60;
this->_cpu->registers.FC_Carry = true;
}
if (this->_cpu->registers.FN_Subtract){
this->_cpu->registers.FH_HalfCarry =
this->_cpu->registers.FH_HalfCarry &&
(this->_cpu->registers.a & 0x0f) < 0x06;
this->_cpu->registers.a -= cr;
} else {
this->_cpu->registers.FH_HalfCarry = (this->_cpu->registers.a & 0x0f) > 0x09;
this->_cpu->registers.a += cr;
}
this->_cpu->registers.FS_Sign = this->_cpu->registers.a >> 7;
this->_cpu->registers.FZ_Zero = this->_cpu->registers.a == 0;
this->_cpu->registers.FPV_ParityOverflow = (OpCode::count1(this->_cpu->registers.a) % 2 == 0);
break;
}
case 0x28: // jr z, n
Log::execute(this->_cpu, opCode, "jr z, n");
if (this->_cpu->registers.FZ_Zero){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x2A: { // ld hl, (nn)
Log::execute(this->_cpu, opCode, "ld hl, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.l = Mcycle::m2(this->_cpu, addr);
this->_cpu->registers.h = Mcycle::m2(this->_cpu, addr + 1);
Log::dump_registers(this->_cpu);
break;
}
case 0x2B: // dec hl
Log::execute(this->_cpu, opCode, "dec hl");
this->_cpu->registers.hl(this->_cpu->registers.hl() - 1);
break;
case 0x2F: // cpl
Log::execute(this->_cpu, opCode, "cpl");
this->_cpu->registers.a ^= 0xff;
this->_cpu->registers.FN_Subtract = true;
this->_cpu->registers.FH_HalfCarry = true;
break;
case 0x30: // jr nc, n
Log::execute(this->_cpu, opCode, "jr nc, n");
if (!this->_cpu->registers.FC_Carry){
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x31: // ld sp, nn
Log::execute(this->_cpu, opCode, "ld sp, nn");
this->_cpu->special_registers.sp =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
break;
case 0x32: { // ld (nn), a
Log::execute(this->_cpu, opCode, "ld (nn), a");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
Mcycle::m3(this->_cpu, addr, this->_cpu->registers.a);
this->_cpu->special_registers.pc += 2;
break;
}
case 0x33: // inc sp
Log::execute(this->_cpu, opCode, "inc sp");
this->_cpu->special_registers.sp++;
break;
case 0x34: { // inc (hl)
Log::execute(this->_cpu, opCode, "inc (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByIncrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value + 1);
break;
}
case 0x35: { // dec (hl)
Log::execute(this->_cpu, opCode, "dec (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByDecrement(value);
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), value - 1);
break;
}
case 0x36: // ld (hl), n
Log::execute(this->_cpu, opCode, "ld (hl), n");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
break;
case 0x37: // scf
Log::execute(this->_cpu, opCode, "scf");
this->_cpu->registers.FC_Carry = true;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = false;
break;
case 0x38: // jr c, n
Log::execute(this->_cpu, opCode, "jr c, n");
if (this->_cpu->registers.FC_Carry) {
auto diff = (int8_t)(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc));
this->_cpu->special_registers.pc++;
this->_cpu->special_registers.pc += diff;
} else {
this->_cpu->special_registers.pc++;
}
break;
case 0x3A: { // ld a, (nn)
Log::execute(this->_cpu, opCode, "ld a, (nn)");
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
Log::dump_registers(this->_cpu);
break;
}
case 0x3B: // dec sp
Log::execute(this->_cpu, opCode, "dec sp");
this->_cpu->special_registers.sp--;
break;
case 0x3F: { // ccf
Log::execute(this->_cpu, opCode, "ccf");
bool saved_carry = this->_cpu->registers.FC_Carry;
this->_cpu->registers.FC_Carry = !this->_cpu->registers.FC_Carry;
this->_cpu->registers.FN_Subtract = false;
this->_cpu->registers.FH_HalfCarry = saved_carry;
break;
}
case 0x46: // ld r, (hl)
case 0x4E:
case 0x56:
case 0x5E:
case 0x66:
case 0x6E: {
Log::execute(this->_cpu, opCode, "ld r, (hl)");
uint8_t *reg = this->targetRegister(opCode, 3);
*reg = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
}
case 0x70: // ld (hl), r
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x77:
Log::execute(this->_cpu, opCode, "ld (hl), r");
Mcycle::m3(this->_cpu, this->_cpu->registers.hl(), *(this->targetRegister(opCode, 0)));
break;
case 0x76: // halt
Log::execute(this->_cpu, opCode, "halt");
this->_cpu->halt = true;
break;
case 0x7E: // ld a,(hl)
Log::execute(this->_cpu, opCode, "ld a,(hl)");
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
break;
case 0x80: // add a, r
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x87: {
Log::execute(this->_cpu, opCode, "add a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsByAddition(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a += *reg;
break;
}
case 0x86: { // add a, (hl)
Log::execute(this->_cpu, opCode, "add a, (hl)");
uint16_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0x88: // adc a, r
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8F: {
Log::execute(this->_cpu, opCode, "adc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a += *reg + carry;
break;
}
case 0x8E: { // adc a, (hl)
Log::execute(this->_cpu, opCode, "adc a, (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0x90: // sub r
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x97: {
Log::execute(this->_cpu, opCode, "sub r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, 0);
this->_cpu->registers.a -= *reg;
break;
}
case 0x96: { // sub (hl)
Log::execute(this->_cpu, opCode, "sub (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0x98: // sbc a, r
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9F: {
Log::execute(this->_cpu, opCode, "sbc a, r");
uint8_t* reg = this->targetRegister(opCode, 0);
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, *reg, carry);
this->_cpu->registers.a -= *reg + carry;
break;
}
case 0x9E: { // sbc a,(hl)
Log::execute(this->_cpu, opCode, "sbc a,(hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xA0: // and r
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA7: {
Log::execute(this->_cpu, opCode, "and r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a &= *reg;
this->setFlagsByLogical(true);
break;
}
case 0xA6: // and (hl)
Log::execute(this->_cpu, opCode, "and (hl)");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(true);
break;
case 0xA8: // xor r
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAF: {
Log::execute(this->_cpu, opCode, "xor r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a ^= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xAE: // xor (hl)
Log::execute(this->_cpu, opCode, "xor (hl)");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB0: // or r
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB7: {
Log::execute(this->_cpu, opCode, "or r");
uint8_t* reg = this->targetRegister(opCode, 0);
this->_cpu->registers.a |= *reg;
this->setFlagsByLogical(false);
break;
}
case 0xB6: // or (hl)
Log::execute(this->_cpu, opCode, "or (hl)");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsByLogical(false);
break;
case 0xB8: // cp r
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBF:
Log::execute(this->_cpu, opCode, "cp r");
this->setFlagsBySubtract(this->_cpu->registers.a, *(this->targetRegister(opCode, 0)), 0);
break;
case 0xBE: { // cp (hl)
Log::execute(this->_cpu, opCode, "cp (hl)");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->registers.hl());
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
break;
}
case 0xC0: // ret nz
Log::execute(this->_cpu, opCode, "ret nz");
if (!this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC1: // pop bc
Log::execute(this->_cpu, opCode, "pop bc");
this->_cpu->registers.c = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.b = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xC2: // jp nz, nn
Log::execute(this->_cpu, opCode, "jp nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC3: // jp nn
Log::execute(this->_cpu, opCode, "jp nn");
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
break;
case 0xC4: // call nz, nn
Log::execute(this->_cpu, opCode, "call nz, nn");
if (!this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xC5: // push bc
Log::execute(this->_cpu, opCode, "push bc");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.b);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.c);
Log::dump_registers(this->_cpu);
break;
case 0xC6: { // add a, n
Log::execute(this->_cpu, opCode, "add a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByAddition(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a += value;
break;
}
case 0xC7: // rst n (n = 0 - 7)
case 0xCF:
case 0xD7:
case 0xDF:
case 0xE7:
case 0xEF:
case 0xF7:
case 0xFF:
Log::execute(this->_cpu, opCode, "rst n (n = 0 - 7)");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->special_registers.pc & 0xff);
this->_cpu->special_registers.pc = (opCode & 0b00111000);
break;
case 0xC8: // ret z
Log::execute(this->_cpu, opCode, "ret z");
if (this->_cpu->registers.FZ_Zero) {
executeRet();
}
break;
case 0xC9: // ret
Log::execute(this->_cpu, opCode, "ret");
executeRet();
break;
case 0xCA: // jp z, nn
Log::execute(this->_cpu, opCode, "jp z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCB: { // BITS
Log::execute(this->_cpu, opCode, "BITS");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeCb(opcode);
break;
}
case 0xCC: // call z, nn
Log::execute(this->_cpu, opCode, "call z, nn");
if (this->_cpu->registers.FZ_Zero) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xCD: { // call nn
Log::execute(this->_cpu, opCode, "call nn");
this->executeCall();
break;
}
case 0xCE: { // adc a, n
Log::execute(this->_cpu, opCode, "adc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsByAddition(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a += value + carry;
break;
}
case 0xD0: // ret nc
Log::execute(this->_cpu, opCode, "ret nc");
if (!this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD1: // pop de
Log::execute(this->_cpu, opCode, "pop de");
this->_cpu->registers.e = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp);
this->_cpu->registers.d = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xD2: // jp nc, nn
Log::execute(this->_cpu, opCode, "jp nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD3: { // out (n),a
Log::execute(this->_cpu, opCode, "out (n),a");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc += 1;
Mcycle::out(this->_cpu, port, this->_cpu->registers.a, this->_cpu->registers.a);
break;
}
case 0xD4: // call nc, nn
Log::execute(this->_cpu, opCode, "call nc, nn");
if (!this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xD5: // push de
Log::execute(this->_cpu, opCode, "push de");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.d);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.e);
Log::dump_registers(this->_cpu);
break;
case 0xD6: { // sub n
Log::execute(this->_cpu, opCode, "sub n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsBySubtract(this->_cpu->registers.a, value, 0);
this->_cpu->registers.a -= value;
break;
}
case 0xD8: // ret c
Log::execute(this->_cpu, opCode, "ret c");
if (this->_cpu->registers.FC_Carry) {
executeRet();
}
break;
case 0xD9: { // exx
Log::execute(this->_cpu, opCode, "exx");
uint16_t temp;
temp = this->_cpu->registers.bc();
this->_cpu->registers.bc(this->_cpu->registers_alternate.bc());
this->_cpu->registers_alternate.bc(temp);
temp = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers_alternate.de());
this->_cpu->registers_alternate.de(temp);
temp = this->_cpu->registers.hl();
this->_cpu->registers.hl(this->_cpu->registers_alternate.hl());
this->_cpu->registers_alternate.hl(temp);
break;
}
case 0xDA: // jp c, nn
Log::execute(this->_cpu, opCode, "jp c, nn");
if (this->_cpu->registers.FC_Carry) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDB: { // in a, (n)
Log::execute(this->_cpu, opCode, "in a, (n)");
uint8_t port = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->_cpu->registers.a = Mcycle::in(this->_cpu, port, this->_cpu->registers.a);
break;
}
case 0xDC: // call c, nn
Log::execute(this->_cpu, opCode, "call c, nn");
if (this->_cpu->registers.FC_Carry) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xDD: { // IX
Log::execute(this->_cpu, opCode, "IX");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeDd(opcode);
break;
}
case 0xDE: { // sbc a, n
Log::execute(this->_cpu, opCode, "sbc a, n");
uint8_t value = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
uint8_t carry = this->_cpu->registers.carry_by_val();
this->setFlagsBySubtract(this->_cpu->registers.a, value, carry);
this->_cpu->registers.a -= value + carry;
break;
}
case 0xE0: // ret po
Log::execute(this->_cpu, opCode, "ret po");
if (! this->_cpu->registers.FPV_ParityOverflow){
executeRet();
}
break;
case 0xE1: // pop hl
Log::execute(this->_cpu, opCode, "pop hl");
this->_cpu->registers.hl(
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8)
);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xE2: // jp po, nn
Log::execute(this->_cpu, opCode, "jp po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE3: { // ex (sp), hl
Log::execute(this->_cpu, opCode, "ex (sp), hl");
uint16_t mem_value =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1) << 8);
uint16_t temp_hl = this->_cpu->registers.hl();
this->_cpu->registers.hl(mem_value);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, temp_hl & 0xff);
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp + 1, temp_hl >> 8);
break;
}
case 0xE4: // call po, nn
Log::execute(this->_cpu, opCode, "call po, nn");
if (! this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xE5: // push hl
Log::execute(this->_cpu, opCode, "push hl");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() >> 8);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.hl() & 0xff);
Log::dump_registers(this->_cpu);
break;
case 0xE6: // and n
Log::execute(this->_cpu, opCode, "and n");
this->_cpu->registers.a &= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(true);
break;
case 0xE8: // ret pe
Log::execute(this->_cpu, opCode, "ret pe");
if (this->_cpu->registers.FPV_ParityOverflow) {
executeRet();
}
break;
case 0xE9: // jp (hl)
Log::execute(this->_cpu, opCode, "jp (hl)");
this->_cpu->special_registers.pc = this->_cpu->registers.hl();
break;
case 0xEA: // jp pe, nn
Log::execute(this->_cpu, opCode, "jp pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xEB: { // ex de,hl
Log::execute(this->_cpu, opCode, "ex de,hl");
uint16_t de = this->_cpu->registers.de();
this->_cpu->registers.de(this->_cpu->registers.hl());
this->_cpu->registers.hl(de);
break;
}
case 0xEC: // call pe, nn
Log::execute(this->_cpu, opCode, "call pe, nn");
if (this->_cpu->registers.FPV_ParityOverflow) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xED: { // EXTD
Log::execute(this->_cpu, opCode, "EXTD");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeEd(opcode);
break;
}
case 0xEE: // xor n
Log::execute(this->_cpu, opCode, "xor n");
this->_cpu->registers.a ^= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF0: // ret p
Log::execute(this->_cpu, opCode, "ret p");
if (! this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF1: // pop af
Log::execute(this->_cpu, opCode, "pop af");
this->_cpu->registers.f(Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp));
this->_cpu->registers.a = Mcycle::m2(this->_cpu, this->_cpu->special_registers.sp + 1);
this->_cpu->special_registers.sp += 2;
Log::dump_registers(this->_cpu);
break;
case 0xF2: // jp p, nn
Log::execute(this->_cpu, opCode, "jp p, nn");
if (! this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF3: // di
Log::execute(this->_cpu, opCode, "di");
this->_cpu->waitingDI = 1;
break;
case 0xF4: // call p, nn
Log::execute(this->_cpu, opCode, "call p, nn");
if (! this->_cpu->registers.FS_Sign) {
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xF5: // push af
Log::execute(this->_cpu, opCode, "push af");
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.a);
this->_cpu->special_registers.sp--;
Mcycle::m3(this->_cpu, this->_cpu->special_registers.sp, this->_cpu->registers.f());
Log::dump_registers(this->_cpu);
break;
case 0xF6: // or n
Log::execute(this->_cpu, opCode, "or n");
this->_cpu->registers.a |= Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
this->setFlagsByLogical(false);
break;
case 0xF8: // ret m
Log::execute(this->_cpu, opCode, "ret m");
if (this->_cpu->registers.FS_Sign){
executeRet();
}
break;
case 0xF9: // ld sp,hl
Log::execute(this->_cpu, opCode, "ld sp,hl");
this->_cpu->special_registers.sp = this->_cpu->registers.hl();
break;
case 0xFA: // jp m, nn
Log::execute(this->_cpu, opCode, "jp m, nn");
if (this->_cpu->registers.FS_Sign){
this->_cpu->special_registers.pc =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFB: // ei
Log::execute(this->_cpu, opCode, "ei");
this->_cpu->waitingEI = 2;
break;
case 0xFC: // call m, nn
Log::execute(this->_cpu, opCode, "call m, nn");
if (this->_cpu->registers.FS_Sign){
this->executeCall();
} else {
this->_cpu->special_registers.pc += 2;
}
break;
case 0xFD: { // IY
Log::execute(this->_cpu, opCode, "IY");
uint8_t opcode = Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc);
this->_cpu->special_registers.pc++;
executeFd(opcode);
break;
}
case 0xFE: { // cp n
Log::execute(this->_cpu, opCode, "cp n");
this->setFlagsBySubtract(this->_cpu->registers.a, Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc), 0);
this->_cpu->special_registers.pc++;
break;
}
default:
char error[100];
sprintf(error, "Invalid op code: %02x", opCode);
Log::error(this->_cpu, error);
throw std::runtime_error(error);
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPUエミュレートのメインループ
31
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CPUエミュレートのメインループ
31
void Cpu::instructionCycle(){
while(true){
if (!this->bus->getInput(Bus::Z80_PIN_I_RESET)){
this->reset();
}
Mcycle::m1t1(this);
Mcycle::m1t2(this);
Mcycle::m1t3(this);
Mcycle::m1t4(this);
this->opCode.execute(this->executing);
this->interrupt();
}
}
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
• デバッグ
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
• デバッグ
• たいへんつらい
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
• デバッグ
• たいへんつらい
• 「すべてのZ80対象プログラムが動く」のがゴール
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
• デバッグ
• たいへんつらい
• 「すべてのZ80対象プログラムが動く」のがゴール
• 既存のプログラムを動かして「なんかおかしい」からのデバッグ
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実装の検証
• よし!ひととおりできたぞ!動かしてみよう!
• で動く訳がない
• デバッグ
• たいへんつらい
• 「すべてのZ80対象プログラムが動く」のがゴール
• 既存のプログラムを動かして「なんかおかしい」からのデバッグ
• 非現実的
32
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
• zexallはCP/M(太古のOS)用の文字出力API(BDOSコール)を使っている
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
• zexallはCP/M(太古のOS)用の文字出力API(BDOSコール)を使っている
• それほど複雑なAPIでないのでエミュレータを書いた
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
• zexallはCP/M(太古のOS)用の文字出力API(BDOSコール)を使っている
• それほど複雑なAPIでないのでエミュレータを書いた
• バイナリのままでも実行できるけどソースからアセンブルできる様にした方が効率的
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
• zexallはCP/M(太古のOS)用の文字出力API(BDOSコール)を使っている
• それほど複雑なAPIでないのでエミュレータを書いた
• バイナリのままでも実行できるけどソースからアセンブルできる様にした方が効率的
• zexallはめちゃくちゃ時間がかかるので途中から実行したい
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラム
• Z80の動作をテストするためのプログラムが存在する
• prelim: zexallを動作させるための命令のテスト
• zexall: 全命令のテスト
• Z80バイナリで配布されている
• メモリの読み書きメソッドでテストプログラムを返す様にして実行
• zexallはCP/M(太古のOS)用の文字出力API(BDOSコール)を使っている
• それほど複雑なAPIでないのでエミュレータを書いた
• バイナリのままでも実行できるけどソースからアセンブルできる様にした方が効率的
• zexallはめちゃくちゃ時間がかかるので途中から実行したい
• 命令群まとめてのテストをバラして実行したい
33
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
全命令の検証が済んだぞ!
完成だ!
34
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
とは行かない
35
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
• メモリアクセスはこの範囲でカバーされる
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
• メモリアクセスはこの範囲でカバーされる
• リセット, I/O, 割り込みなどはカバーされない
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
• メモリアクセスはこの範囲でカバーされる
• リセット, I/O, 割り込みなどはカバーされない
• 実アプリケーションを動かしてテストするしかない
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
• メモリアクセスはこの範囲でカバーされる
• リセット, I/O, 割り込みなどはカバーされない
• 実アプリケーションを動かしてテストするしかない
• 良い方法知っている方教えてください!
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
テストプログラムのカバー範囲
• prelim, zexall は命令しかカバーしない
• メモリアクセスはこの範囲でカバーされる
• リセット, I/O, 割り込みなどはカバーされない
• 実アプリケーションを動かしてテストするしかない
• 良い方法知っている方教えてください!
• 実アプリケーションを動かしてみる
36
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1
37
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
• Z80は0x00がNOP(No OPeration)
命令
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
• Z80は0x00がNOP(No OPeration)
命令
• データバスをすべて0に固定した簡易コンピュータ
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
• Z80は0x00がNOP(No OPeration)
命令
• データバスをすべて0に固定した簡易コンピュータ
• アドレスバスに何を出しても0x00が読み込める
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
• Z80は0x00がNOP(No OPeration)
命令
• データバスをすべて0に固定した簡易コンピュータ
• アドレスバスに何を出しても0x00が読み込める
• NOPを実行し(=何もしないで)次のアドレスを読み込む、を繰り返すコンピュータ
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション1: NOPコンピュータ
• Z80は0x00がNOP(No OPeration)
命令
• データバスをすべて0に固定した簡易コンピュータ
• アドレスバスに何を出しても0x00が読み込める
• NOPを実行し(=何もしないで)次のアドレスを読み込む、を繰り返すコンピュータ
• ここまで実装できていればふつうに動く
38
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
NOPコンピュータ
39
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
NOPコンピュータ
39
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
NOPコンピュータ
39
CLK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
NOPコンピュータ
39
アドレスバス
CLK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
NOPコンピュータ
39
アドレスバス
RD & WR
CLK
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
これを Z80 ハードウェア エミュレータ で…
40
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ 動作の様子
41
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ 動作の様子
41
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
完璧!
42
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
完璧!
42
(に見える)
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2
43
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
• Z80のシングルボードコンピュータ
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
• Z80のシングルボードコンピュータ
• RAM, ROMを搭載して
シリアルポート経由でBASICが動く
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
• Z80のシングルボードコンピュータ
• RAM, ROMを搭載して
シリアルポート経由でBASICが動く
• Z80の機能をフル活用している
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
• Z80のシングルボードコンピュータ
• RAM, ROMを搭載して
シリアルポート経由でBASICが動く
• Z80の機能をフル活用している
• DRAM, I/O, 割り込み, …
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
実アプリケーション2: SBCZ80
• Z80のシングルボードコンピュータ
• RAM, ROMを搭載して
シリアルポート経由でBASICが動く
• Z80の機能をフル活用している
• DRAM, I/O, 割り込み, …
• これが動けば
「Z80 ハードウェア エミュレータ」
を名乗ってもよかろう
44
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
これを動かすのだけど…
45
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
まあもちろん…
46
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
• 絶望的に遅い
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
• 絶望的に遅い
• GPIOエクステンダをやめてラッチを使う様にした
(ここまでの解説はすべてラッチ版)
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
• 絶望的に遅い
• GPIOエクステンダをやめてラッチを使う様にした
(ここまでの解説はすべてラッチ版)
• たまにメモリ読み込みが化ける
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
• 絶望的に遅い
• GPIOエクステンダをやめてラッチを使う様にした
(ここまでの解説はすべてラッチ版)
• たまにメモリ読み込みが化ける
• 信号の衝突 ➡ データバスのバスバッファの方向切替前にHi-Zを経由する
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
一発で動く訳がない
• zexallでカバーされていないI/Oと割り込みにきっちりバグがあった
• 回路にも問題があった
• 絶望的に遅い
• GPIOエクステンダをやめてラッチを使う様にした
(ここまでの解説はすべてラッチ版)
• たまにメモリ読み込みが化ける
• 信号の衝突 ➡ データバスのバスバッファの方向切替前にHi-Zを経由する
• なんとなく動く様になった
47
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
しかし…
48
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い
49
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い
• なんとなく動くが遅い
49
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い
• なんとなく動くが遅い
• Raspberry Pi 4 Model B って 1.8GHzとかっすよ…?
49
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い
• なんとなく動くが遅い
• Raspberry Pi 4 Model B って 1.8GHzとかっすよ…?
• 2.5MHzのエミュレーションすらできないとは…720倍速いのに…
49
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い
• なんとなく動くが遅い
• Raspberry Pi 4 Model B って 1.8GHzとかっすよ…?
• 2.5MHzのエミュレーションすらできないとは…720倍速いのに…
• 遅い理由が2つ
49
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由1
C++力が赤児
50
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由1
C++力が赤児
• zexallを動かしていても既存の実装に比べてめっちゃ遅い
50
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由1
C++力が赤児
• zexallを動かしていても既存の実装に比べてめっちゃ遅い
• レジスタをクラス化してるのがダメ?
50
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由1
C++力が赤児
• zexallを動かしていても既存の実装に比べてめっちゃ遅い
• レジスタをクラス化してるのがダメ?
• C++でなくCで書いた方が良いのか…?
50
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由1
C++力が赤児
• zexallを動かしていても既存の実装に比べてめっちゃ遅い
• レジスタをクラス化してるのがダメ?
• C++でなくCで書いた方が良いのか…?
• 既存の実装ぐらいまで高速化を…
50
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
• 2.5MHzだと1クロックは 250 ns
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
• 2.5MHzだと1クロックは 250 ns
• M1サイクル(命令読み込み)は4クロックなので 1us (マイクロ秒)
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
• 2.5MHzだと1クロックは 250 ns
• M1サイクル(命令読み込み)は4クロックなので 1us (マイクロ秒)
• Raspberry Pi 4 Model B のGPIO操作
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
• 2.5MHzだと1クロックは 250 ns
• M1サイクル(命令読み込み)は4クロックなので 1us (マイクロ秒)
• Raspberry Pi 4 Model B のGPIO操作
• 1ビット出力の gpioWrite(gpio, value) は平均 118ns かかる
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• Z80のサイクル
• 2.5MHzだと1クロックは 250 ns
• M1サイクル(命令読み込み)は4クロックなので 1us (マイクロ秒)
• Raspberry Pi 4 Model B のGPIO操作
• 1ビット出力の gpioWrite(gpio, value) は平均 118ns かかる
• 32ビット同時出力の gpioWrite_Bits_0_31_Set(data) は平均 76ns かかる
51
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
• 近い処理はまとめる
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
• 近い処理はまとめる
• M1, RD, MREQ有効化はまとめて同じタイミングで出力する
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
• 近い処理はまとめる
• M1, RD, MREQ有効化はまとめて同じタイミングで出力する
• クロックを見ない
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
• 近い処理はまとめる
• M1, RD, MREQ有効化はまとめて同じタイミングで出力する
• クロックを見ない
• メモリや外部デバイスはCPUが「もういいよ」を表現するまで
データを提示し続ける
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
遅い理由2
GPIOの制御に時間がかかる
• M1サイクル(の2クロック目まで)
• CLK(クロック)の立ち上がりを待つ
• M1を有効化
• CLKの立ち下がりを待つ
• 16ビットのアドレスを出力
• MREQ, RDを有効化
• M1を有効化
• クロックの立ち上がりを待つ
• クロックの立ち下がりを待つ
• WAITが無効になるのを待つ
• クロックの立ち上がりを待つ
• 8ビットのデータを入力
• MREQ, RDを無効化
52
• 愚直にやると2クロック目までで988nsかかる
• クロック的には500nsで終了が必要
• 対策
• 近い処理はまとめる
• M1, RD, MREQ有効化はまとめて同じタイミングで出力する
• クロックを見ない
• メモリや外部デバイスはCPUが「もういいよ」を表現するまで
データを提示し続ける
• 他のシステムでも通用するかはわからない
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
そしてBASICがそれなりに動く様に…
53
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ 動作の様子
54
シリアルコンソールで
SBCZ80に接続しています
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ 動作の様子
54
シリアルコンソールで
SBCZ80に接続しています
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
やったぜ 🎉
55
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
56
まとめ
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
• 電気信号のプロトコル(API)のエミュレート
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
• 電気信号のプロトコル(API)のエミュレート
• Z80マニュアルに書いてあるとおりに信号線を上げ下げする
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
• 電気信号のプロトコル(API)のエミュレート
• Z80マニュアルに書いてあるとおりに信号線を上げ下げする
• 参考: 「PHPから見たハードウェア制御」 (PHP Conference Japan 2021)
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
• 電気信号のプロトコル(API)のエミュレート
• Z80マニュアルに書いてあるとおりに信号線を上げ下げする
• 参考: 「PHPから見たハードウェア制御」 (PHP Conference Japan 2021)
• CPUエミュレータ部分はCPU機能のソフトウェア実装
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 ハードウェア エミュレータ の構成
• ハードウェアI/Fエミュレータ + ソフトウェアCPUエミュレータ
• ハードウェアI/Fエミュレータ部分はCPUの物理I/Fのソフトウェア実装
• 電気信号のプロトコル(API)のエミュレート
• Z80マニュアルに書いてあるとおりに信号線を上げ下げする
• 参考: 「PHPから見たハードウェア制御」 (PHP Conference Japan 2021)
• CPUエミュレータ部分はCPU機能のソフトウェア実装
• 「エミュレート対象としてのCPU」と同じ
57
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
• 対象CPUを搭載したコンピュータ用に書かれたプログラムを
すべて動作させる必要がある電気回路 + ソフトウェア
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
• 対象CPUを搭載したコンピュータ用に書かれたプログラムを
すべて動作させる必要がある電気回路 + ソフトウェア
• 命令実行部分は完全なエミュレートが要求される
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
• 対象CPUを搭載したコンピュータ用に書かれたプログラムを
すべて動作させる必要がある電気回路 + ソフトウェア
• 命令実行部分は完全なエミュレートが要求される
• ハードウェアI/F部分は周辺デバイスが割と忖度してくれる
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
• 対象CPUを搭載したコンピュータ用に書かれたプログラムを
すべて動作させる必要がある電気回路 + ソフトウェア
• 命令実行部分は完全なエミュレートが要求される
• ハードウェアI/F部分は周辺デバイスが割と忖度してくれる
• 「我・CPU」みたいな態度でちょっと適当にやっても大丈夫(かも)
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
ハードウェアエミュレータから見たCPU
• 対象CPUを搭載したコンピュータ用に書かれたプログラムを
すべて動作させる必要がある電気回路 + ソフトウェア
• 命令実行部分は完全なエミュレートが要求される
• ハードウェアI/F部分は周辺デバイスが割と忖度してくれる
• 「我・CPU」みたいな態度でちょっと適当にやっても大丈夫(かも)
• バスマスタ的な機能が付いたCPUだとそうはいかない
58
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
• C++プログラムとしての最適化
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
• C++プログラムとしての最適化
• Raspberry Pi のベアメタルモード
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
• C++プログラムとしての最適化
• Raspberry Pi のベアメタルモード
• I/O or/and 割り込みを取りこぼす問題の修正
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
• C++プログラムとしての最適化
• Raspberry Pi のベアメタルモード
• I/O or/and 割り込みを取りこぼす問題の修正
• キー入力を取りこぼしている
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• 遅い → 高速化
• C++プログラムとしての最適化
• Raspberry Pi のベアメタルモード
• I/O or/and 割り込みを取りこぼす問題の修正
• キー入力を取りこぼしている
• 割り込みルーチンがおかしいのかも
59
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
• Raspberry Piのメモリをメインメモリに見せかけてさらなる高速化
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
• Raspberry Piのメモリをメインメモリに見せかけてさらなる高速化
• I/Oを乗っ取ってRaspberry Pi上のFDDイメージにアクセス
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
• Raspberry Piのメモリをメインメモリに見せかけてさらなる高速化
• I/Oを乗っ取ってRaspberry Pi上のFDDイメージにアクセス
• 小型化
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
• Raspberry Piのメモリをメインメモリに見せかけてさらなる高速化
• I/Oを乗っ取ってRaspberry Pi上のFDDイメージにアクセス
• 小型化
• Raspberry Pi + フラットケーブル + CPUぐらいのサイズの基板 ぐらいにしてみたい
60
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Future works
今後の展開
• パソコンでの動作
• HB-F1XDJ(MSX2+パソコン)に刺して動かしたい
• CPUアクセラレータとしての効果
• Raspberry Piのメモリをメインメモリに見せかけてさらなる高速化
• I/Oを乗っ取ってRaspberry Pi上のFDDイメージにアクセス
• 小型化
• Raspberry Pi + フラットケーブル + CPUぐらいのサイズの基板 ぐらいにしてみたい
• もうちょっといろいろやってみようと思います
60
61
୩ஐر
͕ͤΘ ͱ͖
@tomzoh
続CPUとは何か
ϋʔυΣΞΤϛϡϨʔλ͔Βݟͨ$16
tomzoh先生の次回作にご期待ください
俺たちの戦いはまだ始まったばかりだ!
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Refs:
Z80 hardware emulator
https://github.com/hasegawa-tomoki/z80-hardware-emulator
prelim, zexall
https://github.com/superzazu/z80
prelim, zexall アセンブル & 配列形式への変換
https://github.com/hasegawa-tomoki/z80-hardware-emulator-test
8ビット CPU Z80LED点滅
http://www.yamamo10.jp/yamamoto/comp/Z80/FlashingLEDs/index.php
SBCZ80技術資料
http://www.amy.hi-ho.ne.jp/officetetsu/storage/sbcz80_techdata.pdf
62
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
余談
63
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
CLion
JetBrainsのC/C++用IDE
• 機能が PhpStorm と同じで快適
• Raspberry Pi に ssh 接続して
ctrl + r で実行・デバッグ
• 気の利くWarningで
へたれC++プログラマに最高
64
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
おまけ
65
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムの実行とは…?
66
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
67
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
php hoge.php
67
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
php hoge.php
php という名前のプログラムが①実行され
php は hoge.php を読み込んで②実行する
67
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
php hoge.php
php という名前のプログラムが①実行され
php は hoge.php を読み込んで②実行する
67
①実行 CPUがマシン語のプログラムを実行する
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
php hoge.php
php という名前のプログラムが①実行され
php は hoge.php を読み込んで②実行する
67
①実行 CPUがマシン語のプログラムを実行する
②実行 phpがPHP言語のプログラムを実行する
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムを「実行する」とは?
php hoge.php
php という名前のプログラムが①実行され
php は hoge.php を読み込んで②実行する
67
①実行 CPUがマシン語のプログラムを実行する
②実行 phpがPHP言語のプログラムを実行する
①実行 と ②実行 は何が違う?
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
①実行 CPUがマシン語のプログラムを実行する
68
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
• 一般プログラミング言語で言うところの変数
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
• 一般プログラミング言語で言うところの変数
• 汎用レジスタ, 特定用途レジスタ (PC: プログラムカウンタ, SP: スタックポインタ)
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
• 一般プログラミング言語で言うところの変数
• 汎用レジスタ, 特定用途レジスタ (PC: プログラムカウンタ, SP: スタックポインタ)
• PC: CPUが実行中のメモリアドレスを指す
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
• 一般プログラミング言語で言うところの変数
• 汎用レジスタ, 特定用途レジスタ (PC: プログラムカウンタ, SP: スタックポインタ)
• PC: CPUが実行中のメモリアドレスを指す
• 「命令を実行してPCレジスタの値を+1」を繰り返すのがCPU
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
マシン語の実行
• CPUはマシン語を実行する機械 (machine)
• マシン語 = 構文解析なしに単体で実行可能な命令
• シンプルな単一機能の命令
• CPUは内部にレジスタを持つ
• 決まった名前を持ち、決まったサイズの超高速なメモリ
• 一般プログラミング言語で言うところの変数
• 汎用レジスタ, 特定用途レジスタ (PC: プログラムカウンタ, SP: スタックポインタ)
• PC: CPUが実行中のメモリアドレスを指す
• 「命令を実行してPCレジスタの値を+1」を繰り返すのがCPU
• マルチスレッドCPUはPCレジスタを2つ持っている
69
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F $78
$D8
$A9 $10
$8D $00 $20
$A2 $ff
$9A
SEI
CLD
LDA #imm
STA abs
LDX #imm
TXS
$8000
$8001
$8002
$8004
$8007
$8009
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F $78
$D8
$A9 $10
$8D $00 $20
$A2 $ff
$9A
SEI
CLD
LDA #imm
STA abs
LDX #imm
TXS
$8000
$8001
$8002
$8004
$8007
$8009
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F $78
$D8
$A9 $10
$8D $00 $20
$A2 $ff
$9A
SEI
CLD
LDA #imm
STA abs
LDX #imm
TXS
$8000
$8001
$8002
$8004
$8007
$8009
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F $78
$D8
$A9 $10
$8D $00 $20
$A2 $ff
$9A
SEI
CLD
LDA #imm
STA abs
LDX #imm
TXS
$8000
$8001
$8002
$8004
$8007
$8009
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
プログラムカウンタ(PC)とマシン語の実行
70
78 D8 A9 10 8D 00 20 A2 FF 9A AD 02 20 10 FB AD
02 20 10 FB A0 FE A2 05 BD D7 07 C9 0A B0 0C CA
10 F6 AD FF 07 C9 A5 D0 02 A0 D6 20 CC 90 8D 11
40 8D 70 07 A9 A5 8D FF 07 8D A7 07 A9 0F 8D 15
$8000
$8010
$8020
$8030
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F $78
$D8
$A9 $10
$8D $00 $20
$A2 $ff
$9A
SEI
CLD
LDA #imm
STA abs
LDX #imm
TXS
$8000
$8001
$8002
$8004
$8007
$8009
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
②実行 phpがPHP言語のプログラムを実行する
71
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
PHPプログラムの実行
72
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
PHPプログラムの実行
• php hoge.php
72
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
PHPプログラムの実行
• php hoge.php
• phpコマンドはマシン語のプログラム
72
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
PHPプログラムの実行
• php hoge.php
• phpコマンドはマシン語のプログラム
• 「PHPの変数をメモリ上に確保する」「PHPの関数コールを実行する」…が記述されてい
る
72
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
PHPプログラムの実行
• php hoge.php
• phpコマンドはマシン語のプログラム
• 「PHPの変数をメモリ上に確保する」「PHPの関数コールを実行する」…が記述されてい
る
• hoge.phpをテキストファイルとして読み込んで構文解析し、実行しやすい形でメモリに
展開して、順番に書かれた処理を実行していく
72
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
うそだ!
PHPはコンパイルするんだろ?
73
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
javac hoge.java
gcc hoge.c
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
javac hoge.java
gcc hoge.c
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
C言語のソースコード ➡ マシン語
javac hoge.java
gcc hoge.c
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
C言語のソースコード ➡ マシン語
どちらもコンパイル
javac hoge.java
gcc hoge.c
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
C言語のソースコード ➡ マシン語
どちらもコンパイル
違いは成果物がCPUでそのまま実行できるか否か
javac hoge.java
gcc hoge.c
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
C言語のソースコード ➡ マシン語
どちらもコンパイル
違いは成果物がCPUでそのまま実行できるか否か
javac hoge.java
gcc hoge.c
PHPもコンパイルという言葉を使う
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
コンパイル
74
Javaのソースコード ➡ Javaバイトコード
C言語のソースコード ➡ マシン語
どちらもコンパイル
違いは成果物がCPUでそのまま実行できるか否か
javac hoge.java
gcc hoge.c
PHPもコンパイルという言葉を使う
➡ PHPのソースコードをPHP Opcodeにコンパイルする
(CPUでそのまま実行できない = PHPが実行する)
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
回路図
75
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 76
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
77
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
• データバス
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
• データバス
• データの入出力に使う信号線の総称
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
• データバス
• データの入出力に使う信号線の総称
• Z80は8本の信号線を持つ = データバス 8bit
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
• データバス
• データの入出力に使う信号線の総称
• Z80は8本の信号線を持つ = データバス 8bit
• 1回に1byteのデータを入出力できる
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
アドレスバスとデータバス
• アドレスバス
• メモリなどのアドレスを指定する信号線の総称
• Z80は16本の信号線を持つ = アドレスバス 16bit
• アドレスとして 0x0000(0) 〜 0xffff(65,535) まで表現できる
• 0x = 16進数 ($も16進数)
• データバス
• データの入出力に使う信号線の総称
• Z80は8本の信号線を持つ = データバス 8bit
• 1回に1byteのデータを入出力できる
• Z80は65,536bytes = 64KBのメモリ空間にアクセスできる
78
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80 CPU命令の実装
79
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
例えば: メモリからレジスタへのロード
80
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
• メモリからデータを読み込んでレジスタに格納する
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
• メモリからデータを読み込んでレジスタに格納する
• Z80は8ビットレジスタ A, B, C, D, E, H, L を持っている
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
• メモリからデータを読み込んでレジスタに格納する
• Z80は8ビットレジスタ A, B, C, D, E, H, L を持っている
• レジスタはCPUの内部なのでソフトウェアとして好きに実装すれば良い
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
• メモリからデータを読み込んでレジスタに格納する
• Z80は8ビットレジスタ A, B, C, D, E, H, L を持っている
• レジスタはCPUの内部なのでソフトウェアとして好きに実装すれば良い
• Z80 ハードウェア エミュレータ では
Cpuクラスが Registersクラスのインスタンスを保持している
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
メモリからレジスタへのロード
• メモリからデータを読み込んでレジスタに格納する
• Z80は8ビットレジスタ A, B, C, D, E, H, L を持っている
• レジスタはCPUの内部なのでソフトウェアとして好きに実装すれば良い
• Z80 ハードウェア エミュレータ では
Cpuクラスが Registersクラスのインスタンスを保持している
• this->_cpu->registers.a = 0xff
81
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
0x3A: LD A, (nn)
nnで指定されたアドレスからデータを読み込んでAレジスタに格納する
82
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
case 0x3A: {
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
break;
}
0x3A: LD A, (nn)
nnで指定されたアドレスからデータを読み込んでAレジスタに格納する
82
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
case 0x3A: {
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
break;
}
0x3A: LD A, (nn)
nnで指定されたアドレスからデータを読み込んでAレジスタに格納する
82
LD A,(0x8000): 3A 00 80 xx
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
case 0x3A: {
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
break;
}
0x3A: LD A, (nn)
nnで指定されたアドレスからデータを読み込んでAレジスタに格納する
82
LD A,(0x8000): 3A 00 80 xx
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
case 0x3A: {
uint16_t addr =
Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc) +
(Mcycle::m2(this->_cpu, this->_cpu->special_registers.pc + 1) << 8);
this->_cpu->special_registers.pc += 2;
this->_cpu->registers.a = Mcycle::m2(this->_cpu, addr);
break;
}
0x3A: LD A, (nn)
nnで指定されたアドレスからデータを読み込んでAレジスタに格納する
82
LD A,(0x8000): 3A 00 80 xx
PC
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
• 次の命令はPCレジスタが指すアドレスから読み込まれる
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
• 次の命令はPCレジスタが指すアドレスから読み込まれる
• コールの場合、スタックに次のアドレスを入れてからジャンプする
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
• 次の命令はPCレジスタが指すアドレスから読み込まれる
• コールの場合、スタックに次のアドレスを入れてからジャンプする
• リターン命令を実行するとスタックからアドレスを取り出してジャンプする = 元の場所に戻る
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
• 次の命令はPCレジスタが指すアドレスから読み込まれる
• コールの場合、スタックに次のアドレスを入れてからジャンプする
• リターン命令を実行するとスタックからアドレスを取り出してジャンプする = 元の場所に戻る
• 特殊命令
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
命令のタイプ
Z80の持つ命令はいくつかのタイプに分類できる
• メモリとレジスタの間のロード
• メモリとレジスタの間の演算
• 加算, 減算, シフト, 比較, ビット操作, 論理演算 …
• ジャンプ, コール
• ジャンプはPCレジスタに値を入れるだけ
• 次の命令はPCレジスタが指すアドレスから読み込まれる
• コールの場合、スタックに次のアドレスを入れてからジャンプする
• リターン命令を実行するとスタックからアドレスを取り出してジャンプする = 元の場所に戻る
• 特殊命令
• スタック操作, ブロック転送, I/O, …
83
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU
Z80のM1サイクル
84
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 85
長谷川智希 @tomzoh
続CPUとは何か - ハードウェアエミュレータから見たCPU 85