Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
c++ source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (WINE)
ARM msvc v19.10 (WINE)
ARM msvc v19.14 (WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 12.4.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 4.9.4
ARM64 gcc 5.4
ARM64 gcc 5.5.0
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LoongArch64 clang (trunk)
LoongArch64 clang 17.0.1
LoongArch64 clang 18.1.0
LoongArch64 clang 19.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI CL430 21.6.1
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 12.4.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
qnx 8.0.0
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 14.1.0
s390x gcc 14.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (WINE)
x64 msvc v19.10 (WINE)
x64 msvc v19.14 (WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (WINE)
x86 msvc v19.10 (WINE)
x86 msvc v19.14 (WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.11
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86-64 Zapcc 190308
x86-64 clang (Chris Bazley N3089)
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (dascandy contracts)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2996)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (old concepts branch)
x86-64 clang (p1974)
x86-64 clang (pattern matching - P2688)
x86-64 clang (reflection)
x86-64 clang (resugar)
x86-64 clang (string interpolation - P3412)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 18.1.0 (clad 1.8)
x86-64 clang 19.1.0
x86-64 clang 19.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 clang rocm-6.0.2
x86-64 clang rocm-6.1.2
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
x86-64 icx 2024.1.0
x86-64 icx 2024.2.0
x86-64 icx 2025.0.0
x86-64 icx 2025.0.0
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.12.1
zig c++ 0.13.0
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
// SPDX-License-Identifier: ISC // // Copyright 2022 Michael Rodriguez // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. #pragma once #include "bit_ops.h" #include "types.h" namespace gameboy { template <typename MemoryReadFunc, typename MemoryWriteFunc> class CPU { public: /// Defines the possible current states of the CPU. The CPU can only be in /// one state at a time. enum SpecialConditions { /// The CPU has trapped an illegal instruction and cannot continue. kIllegalInstructionTrapped = -1, /// The CPU has reached the STOP instruction and is now waiting for /// button input before resuming. kStopped = -2, /// The CPU is powered down until an interrupt occurs. kHalted = -3 }; /// Defines the flag register bits. The flag register stores details about /// the results of the last arithmetic operation. enum Flag { kZero = 7, kSubtract = 6, kHalfCarry = 5, kCarry = 4 }; CPU(MemoryReadFunc mem_read_func, MemoryWriteFunc mem_write_func) noexcept : mem_read_func_(mem_read_func), mem_write_func_(mem_write_func) { Reset(); } /// Resets the CPU to the startup state. /// /// The startup state is defined as the state of the CPU after the boot ROM /// has finished executing. void Reset() __restrict__ noexcept { reg.bc.SetValue(PostBootROMRegisterValues::kBC); reg.de.SetValue(PostBootROMRegisterValues::kDE); reg.hl.SetValue(PostBootROMRegisterValues::kHL); reg.af.SetValue(PostBootROMRegisterValues::kAF); reg.pc = PostBootROMRegisterValues::kPC; reg.sp = PostBootROMRegisterValues::kSP; } /// Executes the next instruction. /// /// \return The number of T-cycles taken by the instruction, or a negative /// number if a special condition was encountered. Refer to SpecialConditions. __attribute__((hot)) auto Step() __restrict__ noexcept -> int { instruction = ReadNextByte(); static constexpr int timings_normal[] = { 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, 0, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 0, 3, 6, 2, 4, 2, 3, 3, 0, 3, 4, 2, 4, 2, 4, 3, 0, 3, 0, 2, 4, 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4}; static constexpr int cb_timings[] = { 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2}; int timing = timings_normal[instruction]; // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) switch (instruction) { // NOP case 0x00: break; // LD BC,d16 case 0x01: { const auto d16 = ReadNextWord(); reg.bc.SetValue(d16); break; } // LD (BC),A case 0x02: mem_write_func_(reg.bc.GetValue(), reg.af.hi); break; // INC BC case 0x03: reg.bc.Increment(); break; // INC B case 0x04: reg.bc.hi = INC(reg.bc.hi); break; // DEC B case 0x05: reg.bc.hi = DEC(reg.bc.hi); break; // LD B,d8 case 0x06: { const auto d8 = ReadNextByte(); reg.bc.hi = d8; break; } // RLCA case 0x07: reg.af.hi = DoRLC(reg.af.hi); SetZeroFlag(false); break; // LD (a16),SP case 0x08: { const auto a16 = ReadNextWord(); mem_write_func_(a16, reg.sp & 0x00FF); mem_write_func_(a16 + 1, reg.sp >> 8); break; } // ADD HL,BC case 0x09: ADD_HL(reg.bc); break; // LD A,(BC) case 0x0A: reg.af.hi = mem_read_func_(reg.bc.GetValue()); break; // DEC BC case 0x0B: reg.bc.Decrement(); break; // INC C case 0x0C: reg.bc.lo = INC(reg.bc.lo); break; // DEC C case 0x0D: reg.bc.lo = DEC(reg.bc.lo); break; // LD C,d8 case 0x0E: reg.bc.lo = ReadNextByte(); break; // RRCA case 0x0F: reg.af.hi = DoRRC(reg.af.hi); SetZeroFlag(false); break; // LD DE,d16 case 0x11: reg.de.SetValue(ReadNextWord()); break; // LD (DE),A case 0x12: mem_write_func_(reg.de.GetValue(), reg.af.hi); break; // INC DE case 0x13: reg.de.Increment(); break; // INC D case 0x14: reg.de.hi = INC(reg.de.hi); break; // DEC D case 0x15: reg.de.hi = DEC(reg.de.hi); break; // LD D,d8 case 0x16: { const auto d8 = ReadNextByte(); reg.de.hi = d8; break; } // RLA case 0x17: reg.af.hi = DoRL(reg.af.hi); SetZeroFlag(false); break; // JR r8 case 0x18: JR(true); break; // ADD HL,DE case 0x19: ADD_HL(reg.de); break; // LD A,(DE) case 0x1A: reg.af.hi = mem_read_func_(reg.de.GetValue()); break; // DEC DE case 0x1B: reg.de.Decrement(); break; // INC E case 0x1C: reg.de.lo = INC(reg.de.lo); break; // DEC E case 0x1D: reg.de.lo = DEC(reg.de.lo); break; // LD E,d8 case 0x1E: { const auto d8 = ReadNextByte(); reg.de.lo = d8; break; } // RRA case 0x1F: reg.af.hi = DoRR(reg.af.hi); SetZeroFlag(false); break; // JR NZ,r8 case 0x20: JR(!ZeroFlagIsSet()); break; // LD HL,d16 case 0x21: reg.hl.SetValue(ReadNextWord()); break; // LD (HL+),A case 0x22: mem_write_func_(reg.hl.GetValue(), reg.af.hi); reg.hl.Increment(); break; // INC HL case 0x23: reg.hl.Increment(); break; // INC H case 0x24: reg.hl.hi = INC(reg.hl.hi); break; // DEC H case 0x25: reg.hl.hi = DEC(reg.hl.hi); break; // LD H,d8 case 0x26: { const auto d8 = ReadNextByte(); reg.hl.hi = d8; break; } // DAA case 0x27: // Because there's no good information on how DAA is implemented in the // SM83, this implementation has been taken from here: // // https://forums.nesdev.org/viewtopic.php?f=20&t=15944 if (!SubtractFlagIsSet()) { if (CarryFlagIsSet() || reg.af.hi > 0x99) { reg.af.hi += 0x60; SetCarryFlag(true); } if (HalfCarryFlagIsSet() || (reg.af.hi & 0x0F) > 0x09) { reg.af.hi += 0x06; } } else { if (CarryFlagIsSet()) { reg.af.hi -= 0x60; } if (HalfCarryFlagIsSet()) { reg.af.hi -= 0x06; } } SetZeroFlag(reg.af.hi); SetHalfCarryFlag(false); break; // JR Z,r8 case 0x28: JR(ZeroFlagIsSet()); break; // ADD HL,HL case 0x29: ADD_HL(reg.hl); break; // LD A,(HL+) case 0x2A: reg.af.hi = mem_read_func_(reg.hl.GetValue()); reg.hl.Increment(); break; // DEC HL case 0x2B: reg.hl.Decrement(); break; // INC L case 0x2C: reg.hl.lo = INC(reg.hl.lo); break; // DEC L case 0x2D: reg.hl.lo = DEC(reg.hl.lo); break; // LD L,d8 case 0x2E: { const auto d8 = ReadNextByte(); reg.hl.lo = d8; break; } // CPL case 0x2F: reg.af.hi = ~reg.af.hi; SetSubtractFlag(true); SetHalfCarryFlag(true); break; // JR NC,r8 case 0x30: JR(!CarryFlagIsSet()); break; // LD SP,d16 case 0x31: { const auto d16 = ReadNextWord(); reg.sp = d16; break; } // LD (HL-),A case 0x32: mem_write_func_(reg.hl.GetValue(), reg.af.hi); reg.hl.Decrement(); break; // INC SP case 0x33: reg.sp++; break; // INC (HL) case 0x34: { auto data = mem_read_func_(reg.hl.GetValue()); data = INC(data); mem_write_func_(reg.hl.GetValue(), data); break; } // DEC (HL) case 0x35: { const auto hl = reg.hl.GetValue(); auto data = mem_read_func_(hl); data = DEC(data); mem_write_func_(hl, data); break; } // LD (HL),d8 case 0x36: { const auto d8 = ReadNextByte(); mem_write_func_(reg.hl.GetValue(), d8); break; } // SCF case 0x37: SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag(true); break; // JR C,r8 case 0x38: JR(CarryFlagIsSet()); break; // ADD HL,SP case 0x39: ADD_HL(reg.sp); break; // LD A,(HL-) case 0x3A: reg.af.hi = mem_read_func_(reg.hl.GetValue()); reg.hl.Decrement(); break; // DEC SP case 0x3B: reg.sp--; break; // INC A case 0x3C: reg.af.hi = INC(reg.af.hi); break; // DEC A case 0x3D: reg.af.hi = DEC(reg.af.hi); break; // LD A,d8 case 0x3E: { const auto d8 = ReadNextByte(); reg.af.hi = d8; break; } // CCF case 0x3F: SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((!CarryFlagIsSet())); break; // LD B,B case 0x40: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.bc.hi = reg.bc.hi; break; // LD B,C case 0x41: reg.bc.hi = reg.bc.lo; break; // LD B,D case 0x42: reg.bc.hi = reg.de.hi; break; // LD B,E case 0x43: reg.bc.hi = reg.de.lo; break; // LD B,H case 0x44: reg.bc.hi = reg.hl.hi; break; // LD B,L case 0x45: reg.bc.hi = reg.hl.lo; break; // LD B,(HL) case 0x46: reg.bc.hi = mem_read_func_(reg.hl.GetValue()); break; // LD B,A case 0x47: reg.bc.hi = reg.af.hi; break; // LD C,B case 0x48: reg.bc.lo = reg.bc.hi; break; // LD C,C case 0x49: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.bc.lo = reg.bc.lo; break; // LD C,D case 0x4A: reg.bc.lo = reg.de.hi; break; // LD C,E case 0x4B: reg.bc.lo = reg.de.lo; break; // LD C,H case 0x4C: reg.bc.lo = reg.hl.hi; break; // LD C,L case 0x4D: reg.bc.lo = reg.hl.lo; break; // LD C,(HL) case 0x4E: reg.bc.lo = mem_read_func_(reg.hl.GetValue()); break; // LD C,A case 0x4F: reg.bc.lo = reg.af.hi; break; // LD D,B case 0x50: reg.de.hi = reg.bc.hi; break; // LD D,C case 0x51: reg.de.hi = reg.bc.lo; break; // LD D,D case 0x52: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.de.hi = reg.de.hi; break; // LD D,E case 0x53: reg.de.hi = reg.de.lo; break; // LD D,H case 0x54: reg.de.hi = reg.hl.hi; break; // LD D,L case 0x55: reg.de.hi = reg.hl.lo; break; // LD D,(HL) case 0x56: reg.de.hi = mem_read_func_(reg.hl.GetValue()); break; // LD D,A case 0x57: reg.de.hi = reg.af.hi; break; // LD E,B case 0x58: reg.de.lo = reg.bc.hi; break; // LD E,C case 0x59: reg.de.lo = reg.bc.lo; break; // LD E,D case 0x5A: reg.de.lo = reg.de.hi; break; // LD E,E case 0x5B: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.de.lo = reg.de.lo; break; // LD E,H case 0x5C: reg.de.lo = reg.hl.hi; break; // LD E,L case 0x5D: reg.de.lo = reg.hl.lo; break; // LD E,(HL) case 0x5E: reg.de.lo = mem_read_func_(reg.hl.GetValue()); break; // LD E,A case 0x5F: reg.de.lo = reg.af.hi; break; // LD H,B case 0x60: reg.hl.hi = reg.bc.hi; break; // LD H,C case 0x61: reg.hl.hi = reg.bc.lo; break; // LD H,D case 0x62: reg.hl.hi = reg.de.hi; break; // LD H,E case 0x63: reg.hl.hi = reg.de.lo; break; // LD H,H case 0x64: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.hl.hi = reg.hl.hi; break; // LD H,L case 0x65: reg.hl.hi = reg.hl.lo; break; // LD H,(HL) case 0x66: reg.hl.hi = mem_read_func_(reg.hl.GetValue()); break; // LD H,A case 0x67: reg.hl.hi = reg.af.hi; break; // LD L,B case 0x68: reg.hl.lo = reg.bc.hi; break; // LD L,C case 0x69: reg.hl.lo = reg.bc.lo; break; // LD L,D case 0x6A: reg.hl.lo = reg.de.hi; break; // LD L,E case 0x6B: reg.hl.lo = reg.de.lo; break; // LD L,H case 0x6C: reg.hl.lo = reg.hl.hi; break; // LD L,L case 0x6D: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.hl.lo = reg.hl.lo; break; // LD L,(HL) case 0x6E: reg.hl.lo = mem_read_func_(reg.hl.GetValue()); break; // LD L,A case 0x6F: reg.hl.lo = reg.af.hi; break; // LD (HL),B case 0x70: mem_write_func_(reg.hl.GetValue(), reg.bc.hi); break; // LD (HL),C case 0x71: mem_write_func_(reg.hl.GetValue(), reg.bc.lo); break; // LD (HL),D case 0x72: mem_write_func_(reg.hl.GetValue(), reg.de.hi); break; // LD (HL),E case 0x73: mem_write_func_(reg.hl.GetValue(), reg.de.lo); break; // LD (HL),H case 0x74: mem_write_func_(reg.hl.GetValue(), reg.hl.hi); break; // LD (HL),L case 0x75: mem_write_func_(reg.hl.GetValue(), reg.hl.lo); break; // LD (HL),A case 0x77: mem_write_func_(reg.hl.GetValue(), reg.af.hi); break; // LD A,B case 0x78: reg.af.hi = reg.bc.hi; break; // LD A,C case 0x79: reg.af.hi = reg.bc.lo; break; // LD A,D case 0x7A: reg.af.hi = reg.de.hi; break; // LD A,E case 0x7B: reg.af.hi = reg.de.lo; break; // LD A,H case 0x7C: reg.af.hi = reg.hl.hi; break; // LD A,L case 0x7D: reg.af.hi = reg.hl.lo; break; // LD A,(HL) case 0x7E: reg.af.hi = mem_read_func_(reg.hl.GetValue()); break; // LD A,A case 0x7F: // XXX: This statement is only here for visual consistency. It is // certain to be optimized out. reg.af.hi = reg.af.hi; break; // ADD A,B case 0x80: ADD(reg.bc.hi); break; // ADD A,C case 0x81: ADD(reg.bc.lo); break; // ADD A,D case 0x82: ADD(reg.de.hi); break; // ADD A,E case 0x83: ADD(reg.de.lo); break; // ADD A,H case 0x84: ADD(reg.hl.hi); break; // ADD A,L case 0x85: ADD(reg.hl.lo); break; // ADD A,(HL) case 0x86: { const auto data = mem_read_func_(reg.hl.GetValue()); ADD(data); break; } // ADD A,A case 0x87: ADD(reg.af.hi); break; // ADC A,B case 0x88: ADC(reg.bc.hi); break; // ADC A,C case 0x89: ADC(reg.bc.lo); break; // ADC A,D case 0x8A: ADC(reg.de.hi); break; // ADC A,E case 0x8B: ADC(reg.de.lo); break; // ADC A,H case 0x8C: ADC(reg.hl.hi); break; // ADC A,L case 0x8D: ADC(reg.hl.lo); break; // ADC A,(HL) case 0x8E: { const auto data = mem_read_func_(reg.hl.GetValue()); ADC(data); break; } // ADC A,A case 0x8F: ADC(reg.af.hi); break; // SUB B case 0x90: SUB(reg.bc.hi); break; // SUB C case 0x91: SUB(reg.bc.lo); break; // SUB D case 0x92: SUB(reg.de.hi); break; // SUB E case 0x93: SUB(reg.de.lo); break; // SUB H case 0x94: SUB(reg.hl.hi); break; // SUB L case 0x95: SUB(reg.hl.lo); break; // SUB (HL) case 0x96: { const auto data = mem_read_func_(reg.hl.GetValue()); SUB(data); break; } // SUB A case 0x97: SUB(reg.af.hi); break; // SBC A,B case 0x98: SBC(reg.bc.hi); break; // SBC A,C case 0x99: SBC(reg.bc.lo); break; // SBC A,D case 0x9A: SBC(reg.de.hi); break; // SBC A,E case 0x9B: SBC(reg.de.lo); break; // SBC A,H case 0x9C: SBC(reg.hl.hi); break; // SBC A,L case 0x9D: SBC(reg.hl.lo); break; // SBC A,(HL) case 0x9E: { const auto data = mem_read_func_(reg.hl.GetValue()); SBC(data); break; } // SBC A,A case 0x9F: SBC(reg.af.hi); break; // AND B case 0xA0: AND(reg.bc.hi); break; // AND C case 0xA1: AND(reg.bc.lo); break; // AND D case 0xA2: AND(reg.de.hi); break; // AND E case 0xA3: AND(reg.de.lo); break; // AND H case 0xA4: AND(reg.hl.hi); break; // AND L case 0xA5: AND(reg.hl.lo); break; // AND (HL) case 0xA6: { const auto data = mem_read_func_(reg.hl.GetValue()); AND(data); break; } // AND A case 0xA7: AND(reg.af.hi); break; // XOR B case 0xA8: XOR(reg.bc.hi); break; // XOR C case 0xA9: XOR(reg.bc.lo); break; // XOR D case 0xAA: XOR(reg.de.hi); break; // XOR E case 0xAB: XOR(reg.de.lo); break; // XOR H case 0xAC: XOR(reg.hl.hi); break; // XOR L case 0xAD: XOR(reg.hl.lo); break; // XOR (HL) case 0xAE: { const auto data = mem_read_func_(reg.hl.GetValue()); XOR(data); break; } // XOR A case 0xAF: XOR(reg.af.hi); break; // OR B case 0xB0: OR(reg.bc.hi); break; // OR C case 0xB1: OR(reg.bc.lo); break; // OR D case 0xB2: OR(reg.de.hi); break; // OR E case 0xB3: OR(reg.de.lo); break; // OR H case 0xB4: OR(reg.hl.hi); break; // OR L case 0xB5: OR(reg.hl.lo); break; // OR (HL) case 0xB6: { const auto data = mem_read_func_(reg.hl.GetValue()); OR(data); break; } // OR A case 0xB7: OR(reg.af.hi); break; // CP B case 0xB8: CP(reg.bc.hi); break; // CP C case 0xB9: CP(reg.bc.lo); break; // CP D case 0xBA: CP(reg.de.hi); break; // CP E case 0xBB: CP(reg.de.lo); break; // CP H case 0xBC: CP(reg.hl.hi); break; // CP L case 0xBD: CP(reg.hl.lo); break; // CP (HL) case 0xBE: { const auto data = mem_read_func_(reg.hl.GetValue()); CP(data); break; } // CP A case 0xBF: CP(reg.af.hi); break; // RET NZ case 0xC0: RET(!ZeroFlagIsSet()); break; // POP BC case 0xC1: POP(reg.bc); break; // JP NZ,a16 case 0xC2: JP(!ZeroFlagIsSet()); break; // JP a16 case 0xC3: JP(true); break; // CALL NZ,a16 case 0xC4: CALL(!ZeroFlagIsSet()); break; // PUSH BC case 0xC5: PUSH(reg.bc); break; // ADD A,d8 case 0xC6: { const auto d8 = ReadNextByte(); ADD(d8); break; } // RST 00H case 0xC7: RST(0x0000); break; // RET Z case 0xC8: RET(ZeroFlagIsSet()); break; // RET case 0xC9: reg.pc = PopFromStack(); break; // JP Z,a16 case 0xCA: JP(ZeroFlagIsSet()); break; // PREFIX CB case 0xCB: { const auto cb_instruction = ReadNextByte(); timing = cb_timings[cb_instruction]; switch (cb_instruction) { // RLC B case 0x00: reg.bc.hi = RLC(reg.bc.hi); break; // RLC C case 0x01: reg.bc.lo = RLC(reg.bc.lo); break; // RLC D case 0x02: reg.de.hi = RLC(reg.de.hi); break; // RLC E case 0x03: reg.de.lo = RLC(reg.de.lo); break; // RLC H case 0x04: reg.hl.hi = RLC(reg.hl.hi); break; // RLC L case 0x05: reg.hl.lo = RLC(reg.hl.lo); break; // RLC (HL) case 0x06: { auto data = mem_read_func_(reg.hl.GetValue()); data = RLC(data); mem_write_func_(reg.hl.GetValue(), data); break; } // RLC A case 0x07: reg.af.hi = RLC(reg.af.hi); break; // RRC B case 0x08: reg.bc.hi = RRC(reg.bc.hi); break; // RRC C case 0x09: reg.bc.lo = RRC(reg.bc.lo); break; // RRC D case 0x0A: reg.de.hi = RRC(reg.de.hi); break; // RRC E case 0x0B: reg.de.lo = RRC(reg.de.lo); break; // RRC H case 0x0C: reg.hl.hi = RRC(reg.hl.hi); break; // RRC L case 0x0D: reg.hl.lo = RRC(reg.hl.lo); break; // RRC (HL) case 0x0E: { auto data = mem_read_func_(reg.hl.GetValue()); data = RRC(data); mem_write_func_(reg.hl.GetValue(), data); break; } // RRC A case 0x0F: reg.af.hi = RRC(reg.af.hi); break; // RL B case 0x10: reg.bc.hi = RL(reg.bc.hi); break; // RL C case 0x11: reg.bc.lo = RL(reg.bc.lo); break; // RL D case 0x12: reg.de.hi = RL(reg.de.hi); break; // RL E case 0x13: reg.de.lo = RL(reg.de.lo); break; // RL H case 0x14: reg.hl.hi = RL(reg.hl.hi); break; // RL L case 0x15: reg.hl.lo = RL(reg.hl.lo); break; // RL (HL) case 0x16: { auto data = mem_read_func_(reg.hl.GetValue()); data = RL(data); mem_write_func_(reg.hl.GetValue(), data); break; } // RL A case 0x17: reg.af.hi = RL(reg.af.hi); break; // RR B case 0x18: reg.bc.hi = RR(reg.bc.hi); break; // RR C case 0x19: reg.bc.lo = RR(reg.bc.lo); break; // RR D case 0x1A: reg.de.hi = RR(reg.de.hi); break; // RR E case 0x1B: reg.de.lo = RR(reg.de.lo); break; // RR H case 0x1C: reg.hl.hi = RR(reg.hl.hi); break; // RR L case 0x1D: reg.hl.lo = RR(reg.hl.lo); break; // RR (HL) case 0x1E: { auto data = mem_read_func_(reg.hl.GetValue()); data = RR(data); mem_write_func_(reg.hl.GetValue(), data); break; } // RR A case 0x1F: reg.af.hi = RR(reg.af.hi); break; // SLA B case 0x20: reg.bc.hi = SLA(reg.bc.hi); break; // SLA C case 0x21: reg.bc.lo = SLA(reg.bc.lo); break; // SLA D case 0x22: reg.de.hi = SLA(reg.de.hi); break; // SLA E case 0x23: reg.de.lo = SLA(reg.de.lo); break; // SLA H case 0x24: reg.hl.hi = SLA(reg.hl.hi); break; // SLA L case 0x25: reg.hl.lo = SLA(reg.hl.lo); break; // SLA (HL) case 0x26: { auto data = mem_read_func_(reg.hl.GetValue()); data = SLA(data); mem_write_func_(reg.hl.GetValue(), data); break; } // SLA A case 0x27: reg.af.hi = SLA(reg.af.hi); break; // SRA B case 0x28: reg.bc.hi = SRA(reg.bc.hi); break; // SRA C case 0x29: reg.bc.lo = SRA(reg.bc.lo); break; // SRA D case 0x2A: reg.de.hi = SRA(reg.de.hi); break; // SRA E case 0x2B: reg.de.lo = SRA(reg.de.lo); break; // SRA H case 0x2C: reg.hl.hi = SRA(reg.hl.hi); break; // SRA L case 0x2D: reg.hl.lo = SRA(reg.hl.lo); break; // SRA (HL) case 0x2E: { auto data = mem_read_func_(reg.hl.GetValue()); data = SRA(data); mem_write_func_(reg.hl.GetValue(), data); break; } // SRA A case 0x2F: reg.af.hi = SRA(reg.af.hi); break; // SWAP B case 0x30: reg.bc.hi = SWAP(reg.bc.hi); break; // SWAP C case 0x31: reg.bc.lo = SWAP(reg.bc.lo); break; // SWAP D case 0x32: reg.de.hi = SWAP(reg.de.hi); break; // SWAP E case 0x33: reg.de.lo = SWAP(reg.de.lo); break; // SWAP H case 0x34: reg.hl.hi = SWAP(reg.hl.hi); break; // SWAP L case 0x35: reg.hl.lo = SWAP(reg.hl.lo); break; // SWAP (HL) case 0x36: { auto data = mem_read_func_(reg.hl.GetValue()); data = SWAP(data); mem_write_func_(reg.hl.GetValue(), data); break; } // SWAP A case 0x37: reg.af.hi = SWAP(reg.af.hi); break; // SRL B case 0x38: reg.bc.hi = SRL(reg.bc.hi); break; // SRL C case 0x39: reg.bc.lo = SRL(reg.bc.lo); break; // SRL D case 0x3A: reg.de.hi = SRL(reg.de.hi); break; // SRL E case 0x3B: reg.de.lo = SRL(reg.de.lo); break; // SRL H case 0x3C: reg.hl.hi = SRL(reg.hl.hi); break; // SRL L case 0x3D: reg.hl.lo = SRL(reg.hl.lo); break; // SRL (HL) case 0x3E: { auto data = mem_read_func_(reg.hl.GetValue()); data = SRL(data); mem_write_func_(reg.hl.GetValue(), data); break; } // SRA A case 0x3F: reg.af.hi = SRL(reg.af.hi); break; // BIT 0,B case 0x40: BIT(0, reg.bc.hi); break; // BIT 0,C case 0x41: BIT(0, reg.bc.lo); break; // BIT 0,D case 0x42: BIT(0, reg.de.hi); break; // BIT 0,E case 0x43: BIT(0, reg.de.lo); break; // BIT 0,H case 0x44: BIT(0, reg.hl.hi); break; // BIT 0,L case 0x45: BIT(0, reg.hl.lo); break; // BIT 0,(HL) case 0x46: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(0, data); break; } // BIT 0,A case 0x47: BIT(0, reg.af.hi); break; // BIT 1,B case 0x48: BIT(1, reg.bc.hi); break; // BIT 1,C case 0x49: BIT(1, reg.bc.lo); break; // BIT 1,D case 0x4A: BIT(1, reg.de.hi); break; // BIT 1,E case 0x4B: BIT(1, reg.de.lo); break; // BIT 1,H case 0x4C: BIT(1, reg.hl.hi); break; // BIT 1,L case 0x4D: BIT(1, reg.hl.lo); break; // BIT 1,(HL) case 0x4E: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(1, data); break; } // BIT 1,A case 0x4F: BIT(1, reg.af.hi); break; // BIT 2,B case 0x50: BIT(2, reg.bc.hi); break; // BIT 2,C case 0x51: BIT(2, reg.bc.lo); break; // BIT 2,D case 0x52: BIT(2, reg.de.hi); break; // BIT 2,E case 0x53: BIT(2, reg.de.lo); break; // BIT 2,H case 0x54: BIT(2, reg.hl.hi); break; // BIT 2,L case 0x55: BIT(2, reg.hl.lo); break; // BIT 2,(HL) case 0x56: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(2, data); break; } // BIT 2,A case 0x57: BIT(2, reg.af.hi); break; // BIT 3,B case 0x58: BIT(3, reg.bc.hi); break; // BIT 3,C case 0x59: BIT(3, reg.bc.lo); break; // BIT 3,D case 0x5A: BIT(3, reg.de.hi); break; // BIT 3,E case 0x5B: BIT(3, reg.de.lo); break; // BIT 3,H case 0x5C: BIT(3, reg.hl.hi); break; // BIT 3,L case 0x5D: BIT(3, reg.hl.lo); break; // BIT 3,(HL) case 0x5E: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(3, data); break; } // BIT 3,A case 0x5F: BIT(3, reg.af.hi); break; // BIT 4,B case 0x60: BIT(4, reg.bc.hi); break; // BIT 4,C case 0x61: BIT(4, reg.bc.lo); break; // BIT 4,D case 0x62: BIT(4, reg.de.hi); break; // BIT 4,E case 0x63: BIT(4, reg.de.lo); break; // BIT 4,H case 0x64: BIT(4, reg.hl.hi); break; // BIT 4,L case 0x65: BIT(4, reg.hl.lo); break; // BIT 4,(HL) case 0x66: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(4, data); break; } // BIT 4,A case 0x67: BIT(4, reg.af.hi); break; // BIT 5,B case 0x68: BIT(5, reg.bc.hi); break; // BIT 5,C case 0x69: BIT(5, reg.bc.lo); break; // BIT 5,D case 0x6A: BIT(5, reg.de.hi); break; // BIT 5,E case 0x6B: BIT(5, reg.de.lo); break; // BIT 5,H case 0x6C: BIT(5, reg.hl.hi); break; // BIT 5,L case 0x6D: BIT(5, reg.hl.lo); break; // BIT 5,(HL) case 0x6E: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(5, data); break; } // BIT 5,A case 0x6F: BIT(5, reg.af.hi); break; // BIT 6,B case 0x70: BIT(6, reg.bc.hi); break; // BIT 6,C case 0x71: BIT(6, reg.bc.lo); break; // BIT 6,D case 0x72: BIT(6, reg.de.hi); break; // BIT 6,E case 0x73: BIT(6, reg.de.lo); break; // BIT 6,H case 0x74: BIT(6, reg.hl.hi); break; // BIT 6,L case 0x75: BIT(6, reg.hl.lo); break; // BIT 6,(HL) case 0x76: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(6, data); break; } // BIT 6,A case 0x77: BIT(6, reg.af.hi); break; // BIT 7,B case 0x78: BIT(7, reg.bc.hi); break; // BIT 7,C case 0x79: BIT(7, reg.bc.lo); break; // BIT 7,D case 0x7A: BIT(7, reg.de.hi); break; // BIT 7,E case 0x7B: BIT(7, reg.de.lo); break; // BIT 7,H case 0x7C: BIT(7, reg.hl.hi); break; // BIT 7,L case 0x7D: BIT(7, reg.hl.lo); break; // BIT 7,(HL) case 0x7E: { auto data = mem_read_func_(reg.hl.GetValue()); BIT(7, data); break; } // BIT 7,A case 0x7F: BIT(7, reg.af.hi); break; // RES 0,B case 0x80: reg.bc.hi = RES(0, reg.bc.hi); break; // RES 0,C case 0x81: reg.bc.lo = RES(0, reg.bc.lo); break; // RES 0,D case 0x82: reg.de.hi = RES(0, reg.de.hi); break; // RES 0,E case 0x83: reg.de.lo = RES(0, reg.de.lo); break; // RES 0,H case 0x84: reg.hl.hi = RES(0, reg.hl.hi); break; // RES 0,L case 0x85: reg.hl.lo = RES(0, reg.hl.lo); break; // RES 0,(HL) case 0x86: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(0, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 0,A case 0x87: reg.af.hi = RES(0, reg.af.hi); break; // RES 1,B case 0x88: reg.bc.hi = RES(1, reg.bc.hi); break; // RES 1,C case 0x89: reg.bc.lo = RES(1, reg.bc.lo); break; // RES 1,D case 0x8A: reg.de.hi = RES(1, reg.de.hi); break; // RES 1,E case 0x8B: reg.de.lo = RES(1, reg.de.lo); break; // RES 1,H case 0x8C: reg.hl.hi = RES(1, reg.hl.hi); break; // RES 1,L case 0x8D: reg.hl.lo = RES(1, reg.hl.lo); break; // RES 1,(HL) case 0x8E: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(1, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 1,A case 0x8F: reg.af.hi = RES(1, reg.af.hi); break; // RES 2,B case 0x90: reg.bc.hi = RES(2, reg.bc.hi); break; // RES 2,C case 0x91: reg.bc.lo = RES(2, reg.bc.lo); break; // RES 2,D case 0x92: reg.de.hi = RES(2, reg.de.hi); break; // RES 2,E case 0x93: reg.de.lo = RES(2, reg.de.lo); break; // RES 2,H case 0x94: reg.hl.hi = RES(2, reg.hl.hi); break; // RES 2,L case 0x95: reg.hl.lo = RES(2, reg.hl.lo); break; // RES 2,(HL) case 0x96: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(2, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 2,A case 0x97: reg.af.hi = RES(2, reg.af.hi); break; // RES 3,B case 0x98: reg.bc.hi = RES(3, reg.bc.hi); break; // RES 3,C case 0x99: reg.bc.lo = RES(3, reg.bc.lo); break; // RES 3,D case 0x9A: reg.de.hi = RES(3, reg.de.hi); break; // RES 3,E case 0x9B: reg.de.lo = RES(3, reg.de.lo); break; // RES 3,H case 0x9C: reg.hl.hi = RES(3, reg.hl.hi); break; // RES 3,L case 0x9D: reg.hl.lo = RES(3, reg.hl.lo); break; // RES 3,(HL) case 0x9E: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(3, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 3,A case 0x9F: reg.af.hi = RES(3, reg.af.hi); break; // RES 4,B case 0xA0: reg.bc.hi = RES(4, reg.bc.hi); break; // RES 4,C case 0xA1: reg.bc.lo = RES(4, reg.bc.lo); break; // RES 4,D case 0xA2: reg.de.hi = RES(4, reg.de.hi); break; // RES 4,E case 0xA3: reg.de.lo = RES(4, reg.de.lo); break; // RES 4,H case 0xA4: reg.hl.hi = RES(4, reg.hl.hi); break; // RES 4,L case 0xA5: reg.hl.lo = RES(4, reg.hl.lo); break; // RES 4,(HL) case 0xA6: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(4, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 4,A case 0xA7: reg.af.hi = RES(4, reg.af.hi); break; // RES 5,B case 0xA8: reg.bc.hi = RES(5, reg.bc.hi); break; // RES 5,C case 0xA9: reg.bc.lo = RES(5, reg.bc.lo); break; // RES 5,D case 0xAA: reg.de.hi = RES(5, reg.de.hi); break; // RES 5,E case 0xAB: reg.de.lo = RES(5, reg.de.lo); break; // RES 5,H case 0xAC: reg.hl.hi = RES(5, reg.hl.hi); break; // RES 5,L case 0xAD: reg.hl.lo = RES(5, reg.hl.lo); break; // RES 5,(HL) case 0xAE: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(5, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 5,A case 0xAF: reg.af.hi = RES(5, reg.af.hi); break; // RES 6,B case 0xB0: reg.bc.hi = RES(6, reg.bc.hi); break; // RES 6,C case 0xB1: reg.bc.lo = RES(6, reg.bc.lo); break; // RES 6,D case 0xB2: reg.de.hi = RES(6, reg.de.hi); break; // RES 6,E case 0xB3: reg.de.lo = RES(6, reg.de.lo); break; // RES 6,H case 0xB4: reg.hl.hi = RES(6, reg.hl.hi); break; // RES 6,L case 0xB5: reg.hl.lo = RES(6, reg.hl.lo); break; // RES 6,(HL) case 0xB6: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(6, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 6,A case 0xB7: reg.af.hi = RES(6, reg.af.hi); break; // RES 7,B case 0xB8: reg.bc.hi = RES(7, reg.bc.hi); break; // RES 7,C case 0xB9: reg.bc.lo = RES(7, reg.bc.lo); break; // RES 7,D case 0xBA: reg.de.hi = RES(7, reg.de.hi); break; // RES 7,E case 0xBB: reg.de.lo = RES(7, reg.de.lo); break; // RES 7,H case 0xBC: reg.hl.hi = RES(7, reg.hl.hi); break; // RES 7,L case 0xBD: reg.hl.lo = RES(7, reg.hl.lo); break; // RES 7,(HL) case 0xBE: { auto data = mem_read_func_(reg.hl.GetValue()); data = RES(7, data); mem_write_func_(reg.hl.GetValue(), data); break; } // RES 7,A case 0xBF: reg.af.hi = RES(7, reg.af.hi); break; // SET 0,B case 0xC0: reg.bc.hi = SET(0, reg.bc.hi); break; // SET 0,C case 0xC1: reg.bc.lo = SET(0, reg.bc.lo); break; // SET 0,D case 0xC2: reg.de.hi = SET(0, reg.de.hi); break; // SET 0,E case 0xC3: reg.de.lo = SET(0, reg.de.lo); break; // SET 0,H case 0xC4: reg.hl.hi = SET(0, reg.hl.hi); break; // SET 0,L case 0xC5: reg.hl.lo = SET(0, reg.hl.lo); break; // SET 0,(HL) case 0xC6: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(0, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 0,A case 0xC7: reg.af.hi = SET(0, reg.af.hi); break; // SET 1,B case 0xC8: reg.bc.hi = SET(1, reg.bc.hi); break; // SET 1,C case 0xC9: reg.bc.lo = SET(1, reg.bc.lo); break; // SET 1,D case 0xCA: reg.de.hi = SET(1, reg.de.hi); break; // SET 1,E case 0xCB: reg.de.lo = SET(1, reg.de.lo); break; // SET 1,H case 0xCC: reg.hl.hi = SET(1, reg.hl.hi); break; // SET 1,L case 0xCD: reg.hl.lo = SET(1, reg.hl.lo); break; // SET 1,(HL) case 0xCE: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(1, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 1,A case 0xCF: reg.af.hi = SET(1, reg.af.hi); break; // SET 2,B case 0xD0: reg.bc.hi = SET(2, reg.bc.hi); break; // SET 2,C case 0xD1: reg.bc.lo = SET(2, reg.bc.lo); break; // SET 2,D case 0xD2: reg.de.hi = SET(2, reg.de.hi); break; // SET 2,E case 0xD3: reg.de.lo = SET(2, reg.de.lo); break; // SET 2,H case 0xD4: reg.hl.hi = SET(2, reg.hl.hi); break; // SET 2,L case 0xD5: reg.hl.lo = SET(2, reg.hl.lo); break; // SET 2,(HL) case 0xD6: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(2, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 2,A case 0xD7: reg.af.hi = SET(2, reg.af.hi); break; // SET 3,B case 0xD8: reg.bc.hi = SET(3, reg.bc.hi); break; // SET 3,C case 0xD9: reg.bc.lo = SET(3, reg.bc.lo); break; // SET 3,D case 0xDA: reg.de.hi = SET(3, reg.de.hi); break; // SET 3,E case 0xDB: reg.de.lo = SET(3, reg.de.lo); break; // SET 3,H case 0xDC: reg.hl.hi = SET(3, reg.hl.hi); break; // SET 3,L case 0xDD: reg.hl.lo = SET(3, reg.hl.lo); break; // SET 3,(HL) case 0xDE: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(3, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 3,A case 0xDF: reg.af.hi = SET(3, reg.af.hi); break; // SET 4,B case 0xE0: reg.bc.hi = SET(4, reg.bc.hi); break; // SET 4,C case 0xE1: reg.bc.lo = SET(4, reg.bc.lo); break; // SET 4,D case 0xE2: reg.de.hi = SET(4, reg.de.hi); break; // SET 4,E case 0xE3: reg.de.lo = SET(4, reg.de.lo); break; // SET 4,H case 0xE4: reg.hl.hi = SET(4, reg.hl.hi); break; // SET 4,L case 0xE5: reg.hl.lo = SET(4, reg.hl.lo); break; // SET 4,(HL) case 0xE6: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(4, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 4,A case 0xE7: reg.af.hi = SET(4, reg.af.hi); break; // SET 5,B case 0xE8: reg.bc.hi = SET(5, reg.bc.hi); break; // SET 5,C case 0xE9: reg.bc.lo = SET(5, reg.bc.lo); break; // SET 5,D case 0xEA: reg.de.hi = SET(5, reg.de.hi); break; // SET 5,E case 0xEB: reg.de.lo = SET(5, reg.de.lo); break; // SET 5,H case 0xEC: reg.hl.hi = SET(5, reg.hl.hi); break; // SET 5,L case 0xED: reg.hl.lo = SET(5, reg.hl.lo); break; // SET 5,(HL) case 0xEE: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(5, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 5,A case 0xEF: reg.af.hi = SET(5, reg.af.hi); break; // SET 6,B case 0xF0: reg.bc.hi = SET(6, reg.bc.hi); break; // SET 6,C case 0xF1: reg.bc.lo = SET(6, reg.bc.lo); break; // SET 6,D case 0xF2: reg.de.hi = SET(6, reg.de.hi); break; // SET 6,E case 0xF3: reg.de.lo = SET(6, reg.de.lo); break; // SET 6,H case 0xF4: reg.hl.hi = SET(6, reg.hl.hi); break; // SET 6,L case 0xF5: reg.hl.lo = SET(6, reg.hl.lo); break; // SET 6,(HL) case 0xF6: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(6, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 6,A case 0xF7: reg.af.hi = SET(6, reg.af.hi); break; // SET 7,B case 0xF8: reg.bc.hi = SET(7, reg.bc.hi); break; // SET 7,C case 0xF9: reg.bc.lo = SET(7, reg.bc.lo); break; // SET 7,D case 0xFA: reg.de.hi = SET(7, reg.de.hi); break; // SET 7,E case 0xFB: reg.de.lo = SET(7, reg.de.lo); break; // SET 7,H case 0xFC: reg.hl.hi = SET(7, reg.hl.hi); break; // SET 7,L case 0xFD: reg.hl.lo = SET(7, reg.hl.lo); break; // SET 7,(HL) case 0xFE: { auto data = mem_read_func_(reg.hl.GetValue()); data = SET(7, data); mem_write_func_(reg.hl.GetValue(), data); break; } // SET 7,A case 0xFF: reg.af.hi = SET(7, reg.af.hi); break; default: break; } break; } // CALL Z,a16 case 0xCC: CALL(ZeroFlagIsSet()); break; // CALL a16 case 0xCD: CALL(true); break; // ADC A,d8 case 0xCE: { const auto d8 = ReadNextByte(); ADC(d8); break; } // RST 08H case 0xCF: RST(0x0008); break; // RET NC case 0xD0: RET(!CarryFlagIsSet()); break; // POP DE case 0xD1: POP(reg.de); break; // JP NC,a16 case 0xD2: JP(!CarryFlagIsSet()); break; // CALL NC,a16 case 0xD4: CALL(!CarryFlagIsSet()); break; // PUSH DE case 0xD5: PUSH(reg.de); break; // SUB d8 case 0xD6: { const auto d8 = ReadNextByte(); SUB(d8); break; } // RST 10H case 0xD7: RST(0x0010); break; // RET C case 0xD8: RET(CarryFlagIsSet()); break; // RETI case 0xD9: RET(true); break; // JP C,a16 case 0xDA: JP(CarryFlagIsSet()); break; // CALL C,a16 case 0xDC: CALL(CarryFlagIsSet()); break; // SBC A,d8 case 0xDE: { const auto d8 = ReadNextByte(); SBC(d8); break; } // RST 18H case 0xDF: RST(0x0018); break; // LDH (a8),A case 0xE0: { const auto a8 = ReadNextByte(); mem_write_func_(0xFF00 + a8, reg.af.hi); break; } // POP HL case 0xE1: POP(reg.hl); break; // LD (C),A case 0xE2: mem_write_func_(0xFF00 + reg.bc.lo, reg.af.hi); break; // PUSH HL case 0xE5: PUSH(reg.hl); break; // AND d8 case 0xE6: { const auto d8 = ReadNextByte(); AND(d8); break; } // RST 20H case 0xE7: RST(0x0020); break; // ADD SP,r8 case 0xE8: reg.sp = Do_ADD_SP_r8(); break; // JP (HL) case 0xE9: reg.pc = reg.hl.GetValue(); break; // LD (a16),A case 0xEA: { const auto a16 = ReadNextWord(); mem_write_func_(a16, reg.af.hi); break; } // XOR d8 case 0xEE: { const auto d8 = ReadNextByte(); XOR(d8); break; } // RST 28H case 0xEF: RST(0x0028); break; // LDH A,(a8) case 0xF0: { const auto a8 = ReadNextByte(); reg.af.hi = mem_read_func_(0xFF00 + a8); break; } // POP AF case 0xF1: POP(reg.af); reg.af.lo &= ~0x0F; break; // LD A,(C) case 0xF2: reg.af.hi = mem_read_func_(0xFF00 + reg.bc.lo); break; // DI case 0xF3: break; // PUSH AF case 0xF5: PUSH(reg.af); break; // OR d8 case 0xF6: { const auto d8 = ReadNextByte(); OR(d8); break; } // RST 30H case 0xF7: RST(0x0030); break; // LD HL,SP+r8 case 0xF8: { const auto result = Do_ADD_SP_r8(); reg.hl.SetValue(result); break; } // LD SP,HL case 0xF9: reg.sp = reg.hl.GetValue(); break; // LD A,(a16) case 0xFA: { const auto a16 = ReadNextWord(); reg.af.hi = mem_read_func_(a16); break; } // CP d8 case 0xFE: { const auto d8 = ReadNextByte(); CP(d8); break; } // RST 38H case 0xFF: RST(0x0038); break; default: break; } return timing * 4; // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) } /// Defines a 16-bit register pair. /// /// The Sharp SM83 fundamentally implements the "general purpose registers" as /// 8-bit bytes. However, these 8-bit bytes can also be addressed at 16-bit /// words in a particular fashion. /// /// There are 8, 8-bit byte general-purpose registers called B, C, D, E, F, /// H, L, A. Technically speaking, the flag register (F) does not count /// since this is not meant to be accessed directly. /// /// These bytes can be combined to form the following 16-bit pairs: BC, DE, /// HL, and AF. Addressing the individual "bytes" of the pair is simple: /// /// \code /// where pair = BC /// B = most significant 8-bits of the 16-bit word /// C = least significant 8-bits of the 16-bit word /// \endcode struct RegisterPair { /// Sets the value of the register pair. /// /// \param value The value to set the register pair to. void SetValue(const int value) __restrict__ noexcept { hi = value >> 8; lo = value & 0x00FF; } /// Increments the register pair value by 1. void Increment() __restrict__ noexcept { auto value = GetValue(); value++; SetValue(value); } /// Decrements the register pair value by 1. void Decrement() __restrict__ noexcept { auto value = GetValue(); value--; SetValue(value); } /// Retrieves the value of the 16-bit register pair after assembly. /// /// \returns The 16-bit register pair value. [[nodiscard]] auto GetValue() __restrict__ const noexcept -> Word { return (hi << 8) | lo; } /// The most significant 8 bits of the register pair. Byte hi; /// The least significant 8 bits of the register pair. Byte lo; }; /// Defines the register layout. struct { RegisterPair bc, de, hl, af; uint16_t pc, sp; } reg; /// The current instruction being executed. uint8_t instruction; private: /// The values of the registers after the boot ROM has finished executing. enum PostBootROMRegisterValues { kBC = 0x0013, kDE = 0x00D8, kHL = 0x014D, kAF = 0x01B0, kPC = 0x0100, kSP = 0xFFFE }; /// Sets the Zero flag of the Flag (F) register if the value is zero, or /// clears it otherwise. /// /// \param value The value to compare against zero. void SetZeroFlag(Byte value) __restrict__ noexcept { gameboy::utility::SetOrClearBitIf(reg.af.lo, Flag::kZero, value == 0); } /// Sets the Zero flag of the Flag (F) register if the condition is met, or /// clears it otherwise. /// /// \param condition The condition as a result of a boolean operation. void SetZeroFlag(bool condition) __restrict__ noexcept { gameboy::utility::SetOrClearBitIf(reg.af.lo, Flag::kZero, condition); } /// Sets the Subtract flag of the Flag (F) register if the condition is /// met, or clears it otherwise. /// /// \param condition The condition as a result of a boolean operation. void SetSubtractFlag(bool condition) __restrict__ noexcept { gameboy::utility::SetOrClearBitIf(reg.af.lo, Flag::kSubtract, condition); } /// Sets the half-carry flag of the Flag (F) register if the condition is /// met, or clears it otherwise. /// /// \param condition The condition as a result of a boolean operation. void SetHalfCarryFlag(bool condition) __restrict__ noexcept { gameboy::utility::SetOrClearBitIf(reg.af.lo, Flag::kHalfCarry, condition); } /// Sets the carry flag of the Flag (F) register if the condition is met, /// or clears it otherwise. /// /// \param condition The condition as a result of a boolean operation. void SetCarryFlag(bool condition) __restrict__ noexcept { gameboy::utility::SetOrClearBitIf(reg.af.lo, Flag::kCarry, condition); } /// Checks if the Zero flag is set in the Flag register (F). /// /// \return `true` if the Zero flag is set, or `false` otherwise. [[nodiscard]] auto ZeroFlagIsSet() __restrict__ const noexcept -> bool { return gameboy::utility::BitTest(reg.af.lo, Flag::kZero); } /// Checks if the Subtract flag is set in the Flag register (F). /// /// \return `true` if the Subtract flag is set, or `false` otherwise. [[nodiscard]] auto SubtractFlagIsSet() __restrict__ const noexcept -> bool { return gameboy::utility::BitTest(reg.af.lo, Flag::kSubtract); } /// Checks if the half carry flag is set in the Flag register (F). /// /// \return `true` if the half carry flag is set, or `false` otherwise. [[nodiscard]] auto HalfCarryFlagIsSet() __restrict__ const noexcept -> bool { return gameboy::utility::BitTest(reg.af.lo, Flag::kHalfCarry); } /// Checks if the carry flag is set in the Flag register (F). /// /// \return `true` if the carry flag is set, or `false` otherwise. [[nodiscard]] auto CarryFlagIsSet() __restrict__ const noexcept -> bool { return gameboy::utility::BitTest(reg.af.lo, Flag::kCarry); } /// Pops two bytes from the stack and forms a 16-bit word. /// /// \return The 16-bit word formed from the two bytes from the stack. [[nodiscard]] auto PopFromStack() __restrict__ noexcept -> Word { const auto lsb = mem_read_func_(reg.sp++); const auto msb = mem_read_func_(reg.sp++); return (msb << 8) | lsb; } /// Pushes a register pair to the stack. /// /// \param pair The pair to push to the stack. void PushToStack(const RegisterPair& pair) __restrict__ noexcept { mem_write_func_(--reg.sp, pair.hi); mem_write_func_(--reg.sp, pair.lo); } /// Pushes two arbitrary bytes to the stack. /// /// This is only really useful for the CALL instruction, where the program /// counter (PC) has to be pushed to the stack. The program counter is not /// a pair. /// /// \param msb The most significant 8-bit byte of the 16-bit word. /// \param lsb The least significant 8-bit byte of the 16-bit word. void PushToStack(Byte msb, Byte lsb) __restrict__ noexcept { mem_write_func_(--reg.sp, msb); mem_write_func_(--reg.sp, lsb); } /// Retrieves the byte from memory referenced by the current program /// counter, then increments the program counter (PC) by 1. /// /// \return The byte from memory referenced by the current program counter. [[nodiscard]] auto ReadNextByte() __restrict__ noexcept -> Byte { return mem_read_func_(reg.pc++); } /// Retrieves the next two bytes from memory referenced by the current program /// counter, increments the program counter by two and forms a 16-bit word. /// /// \return The 16-bit word formed from the two bytes in memory. [[nodiscard]] auto ReadNextWord() __restrict__ noexcept -> Word { const auto lsb = ReadNextByte(); const auto msb = ReadNextByte(); return (msb << 8) | lsb; } /// Implements the `INC n` instruction. /// /// \param value The value to increment by 1. /// \return The incremented value. [[nodiscard]] auto INC(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag((value & 0x0F) == 0x0F); value++; SetZeroFlag(value); return value; } /// Implements the `DEC n` instruction. /// /// \param value The value to decrement by 1. /// \return The decremented value. [[nodiscard]] auto DEC(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(true); SetHalfCarryFlag((value & 0x0F) == 0); value--; SetZeroFlag(value); return value; } /// Implements the logic for the rotate left operation. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto DoRLC(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((value & 0x80) != 0); return (value << 1) | (value >> 7); } /// Implements the logic for the rotate right operation. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto DoRRC(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((value & 1) != 0); return (value >> 1) | (value << 7); } /// Implements the `RLC n` instruction. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto RLC(Byte value) __restrict__ noexcept -> Byte { value = DoRLC(value); SetZeroFlag(value); return value; } /// Implements the logic for the rotate left through carry instruction. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto DoRL(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); const auto old_carry_flag_value = static_cast<int>(CarryFlagIsSet()); SetCarryFlag((value & 0x80) != 0); return (value << 1) | old_carry_flag_value; } /// Implements the `RL n` instruction. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto RL(Byte value) __restrict__ noexcept -> Byte { value = DoRL(value); SetZeroFlag(value); return value; } /// Implements the `RRC n` instruction. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto RRC(Byte value) __restrict__ noexcept -> Byte { value = DoRRC(value); SetZeroFlag(value); return value; } /// Implements the `SLA n` instruction. /// /// \param value The value to shift. /// \return The shifted value. [[nodiscard]] auto SLA(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((value & 0x80) != 0); value <<= 1; SetZeroFlag(value); return value; } /// Implements the `SRA n` instruction. /// /// \param value The value to shift. /// \return The shifted value. [[nodiscard]] auto SRA(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((value & 1) != 0); value = (value >> 1) | (value & 0x80); SetZeroFlag(value); return value; } /// Implements the `SWAP n` instruction. /// /// \param value The value to swap. /// \return The swapped value. [[nodiscard]] auto SWAP(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag(false); value = ((value & 0x0F) << 4) | (value >> 4); SetZeroFlag(value); return value; } /// Implements the logic for the `ADD HL, xx` instruction. /// /// \param addend The value to add to the HL register pair. void DoADD_HL(Word addend) __restrict__ noexcept { const auto hl = reg.hl.GetValue(); const auto sum = hl + addend; SetSubtractFlag(false); SetHalfCarryFlag(((hl ^ addend ^ sum) & 0x1000) != 0); SetCarryFlag(sum > 0xFFFF); reg.hl.SetValue(sum); } /// Implements the `ADD HL, xx` instruction. /// /// \param pair The register pair to add to HL. void ADD_HL(const RegisterPair& pair) __restrict__ noexcept { DoADD_HL(pair.GetValue()); } /// Implements the `ADD HL, xx` instruction for naked 16-bit words. /// /// \param pair The 16-bit word to add to HL. void ADD_HL(Word addend) __restrict__ noexcept { DoADD_HL(addend); } /// Implements the `JR (cond,)r8` instruction. /// /// \param condition_met The condition as a result of a boolean operation. /// /// \return The number of T-cycles taken by this instruction. This is /// dependent on whether or not the branch is taken. [[nodiscard]] auto JR(bool condition_met) __restrict__ noexcept -> int { const auto r8 = static_cast<int8_t>(ReadNextByte()); if (condition_met) { reg.pc += r8; return 1; } return 4; } /// Handles the logic for the rotate right through carry operation. /// /// This method does not alter the Zero flag in any way since this method /// is valid for both the `RRA` and `RR n` instructions, however RRA sets /// the Zero flag to `false` in all cases, whereas `RR` conditionally does so. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto DoRR(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); const auto old_carry_flag_value = CarryFlagIsSet() ? 0x80 : 0x00; SetCarryFlag((value & 1) != 0); value >>= 1; value |= old_carry_flag_value; return value; } /// Handles the `RR n` instruction. /// /// \param value The value to rotate. /// \return The rotated value. [[nodiscard]] auto RR(Byte value) __restrict__ noexcept -> Byte { value = DoRR(value); SetZeroFlag(value); return value; } /// Handles the `BIT n,r` instruction. /// /// \param bit The bit to test. /// \param n The register to test the bit. void BIT(int bit, Byte n) __restrict__ noexcept { SetSubtractFlag(false); SetHalfCarryFlag(true); const auto result = !gameboy::utility::BitTest(n, bit); SetZeroFlag(result); } /// Handles the `RES n,r` instruction. /// /// \param bit The bit to reset. /// \param n The register to reset the bit. [[nodiscard]] auto RES(int bit, Byte n) __restrict__ noexcept -> Byte { gameboy::utility::BitClear(n, bit); return n; } /// Handles the `SET n,r` instruction. /// /// \param bit The bit to set. /// \param n The register to set the bit. [[nodiscard]] auto SET(int bit, Byte n) __restrict__ noexcept -> Byte { gameboy::utility::BitSet(n, bit); return n; } /// Handles the logic for the addition (with carry) operations. /// /// \param addend The value to add to the Accumulator (register A). /// \param carry_flag Set to `0` if this method is intended to be used to /// perform the `ADD` instruction, or the current value of the carry flag /// otherwise to perform the `ADC` instruction. void DoAdd(Byte addend, int carry_flag) __restrict__ noexcept { const auto sum = reg.af.hi + addend + carry_flag; const auto result = static_cast<Byte>(sum); SetZeroFlag(result); SetSubtractFlag(false); SetHalfCarryFlag(((reg.af.hi ^ addend ^ sum) & 0x10) != 0); SetCarryFlag(sum > 0xFF); reg.af.hi = result; } /// Handles the `ADD A, n` instruction. /// /// \param value The value to add to the Accumulator (register A). void ADD(Byte value) __restrict__ noexcept { DoAdd(value, 0); } /// Handles the `ADC A, n` instruction. /// /// \param value The value to add to the Accumulator (register A). void ADC(Byte value) __restrict__ noexcept { const auto carry_flag = static_cast<int>(CarryFlagIsSet()); DoAdd(value, carry_flag); } /// Handles the logic for the subtract (with carry) operations. /// /// This method does not store the result of the operation into the /// Accumulator, since this method can also be used to implement the `CP n` /// instruction. It is perfectly safe to ignore the return value in this case. /// /// \param subtrahend The value to subtract from the Accumulator (register /// A). /// \param carry_flag Set to `0` if this method is intended to be used to /// perform the `SUB` instruction, or the current value of the carry flag /// otherwise to perform the `SBC` instruction. /// \return The difference between the Accumulator, subtrahend, and the /// carry flag value. [[maybe_unused]] auto DoSubtract(Byte subtrahend, int carry_flag) __restrict__ noexcept -> Byte { const auto diff = reg.af.hi - subtrahend - carry_flag; const auto result = static_cast<Byte>(diff); SetZeroFlag(result); SetSubtractFlag(true); SetHalfCarryFlag(((reg.af.hi ^ subtrahend ^ diff) & 0x10) != 0); SetCarryFlag(diff < 0); return result; } /// Handles the `SUB n` instruction. /// /// \param subtrahend The value to subtract from the Accumulator (register A). void SUB(Byte subtrahend) __restrict__ noexcept { reg.af.hi = DoSubtract(subtrahend, 0); } /// Handles the `SBC A, n` instruction. /// /// \param subtrahend The value to subtract from the Accumulator (register A). void SBC(Byte subtrahend) __restrict__ noexcept { const auto carry_flag = static_cast<int>(CarryFlagIsSet()); reg.af.hi = DoSubtract(subtrahend, carry_flag); } /// Handles the `AND n` instruction. /// /// \param value The value to bitwise AND against the Accumulator (register // A). void AND(Byte value) __restrict__ noexcept { reg.af.hi &= value; reg.af.lo = (reg.af.hi == 0) ? 0xA0 : 0x20; } /// Handles the `XOR n` instruction. /// /// \param value The value to bitwise XOR against the Accumulator (register // A). void XOR(Byte value) __restrict__ noexcept { reg.af.hi ^= value; reg.af.lo = (reg.af.hi == 0) ? 0x80 : 0x00; } /// Handles the `OR n` instruction. /// /// \param value The value to bitwise OR against the Accumulator (register // A). void OR(Byte value) __restrict__ noexcept { reg.af.hi |= value; reg.af.lo = (reg.af.hi == 0) ? 0x80 : 0x00; } /// Handles the `CP n` instruction. /// /// \param subtrahend The value to subtract from the Accumulator (register // A). void CP(Byte subtrahend) __restrict__ noexcept { // Just set the flags, do not store the results into the accumulator. DoSubtract(subtrahend, 0); } /// Handles the `RET (cond)` instruction. /// /// \param condition_met The condition as a result of a boolean operation. /// /// The number of T-cycles taken by this instruction. This is dependent on /// whether or not the branch is taken. [[nodiscard]] auto RET(bool condition_met) __restrict__ noexcept -> int { if (condition_met) { reg.pc = PopFromStack(); return 20; } return 8; } /// Handles the `POP nn` instruction. /// /// \param pair The register pair to store the two bytes. void POP(RegisterPair& pair) __restrict__ noexcept { const auto data = PopFromStack(); pair.hi = data >> 8; pair.lo = data & 0x00FF; } /// Handles the `JP (cond,)a16` instruction. /// /// \param condition_met The condition as a result of a boolean operation. /// /// \return [[nodiscard]] auto JP(bool condition_met) __restrict__ noexcept -> int { const auto a16 = ReadNextWord(); if (condition_met) { reg.pc = a16; return 1; } return 2; } /// Handles the `CALL (cond,)a16` instruction. /// /// \param condition_met The condition as a result of a boolean operation. /// /// \return The number of T-cycles taken by this instruction. This is /// dependent on whether or not the branch is taken. [[nodiscard]] auto CALL(bool condition_met) __restrict__ noexcept -> int { const auto a16 = ReadNextWord(); if (condition_met) { PushToStack(reg.pc >> 8, reg.pc & 0x00FF); reg.pc = a16; return 24; } return 16; } /// Handles the `PUSH nn` instruction. /// /// \param pair The register pair to push to the stack. void PUSH(const RegisterPair& pair) __restrict__ noexcept { PushToStack(pair); } /// Handles the `SRL nn` instruction. /// /// \param value The value to rotate right into Carry. /// \return The rotated value. [[nodiscard]] auto SRL(Byte value) __restrict__ noexcept -> Byte { SetSubtractFlag(false); SetHalfCarryFlag(false); SetCarryFlag((value & 1) != 0); value >>= 1; SetZeroFlag(value); return value; } /// Handles the `RST n` instruction. /// /// \param reset_vector The reset vector. void RST(Word reset_vector) __restrict__ noexcept { PushToStack(reg.pc >> 8, reg.pc & 0x00FF); reg.pc = reset_vector; } /// Handles both the `ADD SP,r8` and `LD HL,SP+r8` instructions. /// /// These instructions are functionally equivalent, but they can have /// different destinations. /// /// \return The sum of `r8` and the stack pointer (SP). [[nodiscard]] auto Do_ADD_SP_r8() __restrict__ noexcept -> int { const auto r8 = static_cast<int8_t>(ReadNextByte()); const auto sum = reg.sp + r8; const auto result = reg.sp ^ r8 ^ sum; SetZeroFlag(false); SetSubtractFlag(false); SetHalfCarryFlag((result & 0x10) != 0); SetCarryFlag((result & 0x100) != 0); return sum; } /// The function to call when a memory read is required. MemoryReadFunc mem_read_func_; /// The function to call when a memory write is required. MemoryWriteFunc mem_write_func_; }; } // namespace gameboy
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
About the author
Statistics
Changelog
Version tree