diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index fa4875550..4d0ec332c 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -46,7 +46,7 @@ jobs: - name: Run PyTest env: PYTEST_SECRETS_KEY: ${{ secrets.PYTEST_SECRETS_KEY }} - TEST_CI: 1 + TEST_VERBOSE_IMAGES: 0 TEST_NO_UI: 1 run: | python -m pytest tests/ -n auto -v @@ -111,7 +111,7 @@ jobs: - name: Run PyTest env: PYTEST_SECRETS_KEY: ${{ secrets.PYTEST_SECRETS_KEY }} - TEST_CI: 1 + TEST_VERBOSE_IMAGES: 0 TEST_NO_UI: 1 run: | pypy3 -m pytest tests/ -n auto -v diff --git a/Makefile b/Makefile index ffa7f541a..4573c5e01 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,11 @@ build: cd ${ROOT_DIR}/extras/bootrom && $(MAKE) CFLAGS=$(CFLAGS) ${PY} setup.py build_ext -j $(shell getconf _NPROCESSORS_ONLN) --inplace -clean: + +clean_jit: + find . -type f -name "*jit_*" -delete + +clean: clean_jit @echo "Cleaning..." cd ${ROOT_DIR}/extras/default_rom && $(MAKE) clean cd ${ROOT_DIR}/extras/bootrom && $(MAKE) clean diff --git a/extras/examples/gamewrapper_tetris.py b/extras/examples/gamewrapper_tetris.py index f0cb1a4e3..a419cf26c 100644 --- a/extras/examples/gamewrapper_tetris.py +++ b/extras/examples/gamewrapper_tetris.py @@ -29,7 +29,7 @@ tetris.start_game(timer_div=0x00) # The timer_div works like a random seed in Tetris tetromino_at_0x00 = tetris.next_tetromino() -assert tetromino_at_0x00 == "Z", tetris.next_tetromino() +assert tetromino_at_0x00 == "O", tetris.next_tetromino() assert tetris.score == 0 assert tetris.level == 0 assert tetris.lines == 0 diff --git a/pyboy/__main__.py b/pyboy/__main__.py index 8c5209fd4..e5d9df765 100644 --- a/pyboy/__main__.py +++ b/pyboy/__main__.py @@ -87,6 +87,7 @@ def valid_file_path(path): parser.add_argument("-s", "--scale", default=defaults["scale"], type=int, help="The scaling multiplier for the window") parser.add_argument("--sound", action="store_true", help="Enable sound (beta)") parser.add_argument("--no-renderer", action="store_true", help="Disable rendering (internal use)") +parser.add_argument("--jit", action="store_true", help="Enable JIT compile (beta)") parser.add_argument( "--gameshark", type=str, @@ -169,7 +170,7 @@ def main(): pyboy.load_state(f) render = not argv.no_renderer - while pyboy._tick(render): + while pyboy.tick(): pass pyboy.stop() diff --git a/pyboy/core/cartridge/base_mbc.pxd b/pyboy/core/cartridge/base_mbc.pxd index 5b8842795..758914914 100644 --- a/pyboy/core/cartridge/base_mbc.pxd +++ b/pyboy/core/cartridge/base_mbc.pxd @@ -36,7 +36,7 @@ cdef class BaseMBC: cdef void save_ram(self, IntIOInterface) noexcept cdef void load_ram(self, IntIOInterface) noexcept cdef void init_rambanks(self, uint8_t) noexcept - cdef str getgamename(self, uint8_t[:,:]) noexcept + cdef str getgamename(self, uint8_t[:,:]) cdef uint8_t getitem(self, uint16_t) noexcept nogil cdef void setitem(self, uint16_t, uint8_t) noexcept nogil diff --git a/pyboy/core/cpu.pxd b/pyboy/core/cpu.pxd index f1cf0d393..1b8a0cb03 100644 --- a/pyboy/core/cpu.pxd +++ b/pyboy/core/cpu.pxd @@ -4,7 +4,7 @@ # -from libc.stdint cimport int16_t, uint8_t, uint16_t, uint32_t, uint64_t +from libc.stdint cimport int16_t, int64_t, uint8_t, uint16_t, int64_t cimport pyboy.core.mb from pyboy.utils cimport IntIOInterface @@ -26,17 +26,21 @@ cdef uint8_t INTR_VBLANK, INTR_LCDC, INTR_TIMER, INTR_SERIAL, INTR_HIGHTOLOW cdef class CPU: cdef bint is_stuck - cdef bint interrupt_master_enable, interrupt_queued, halted, stopped + cdef bint interrupt_master_enable, interrupt_queued, halted, stopped, bail + cdef bint jit_jump cdef uint8_t interrupts_flag, interrupts_enabled, interrupts_flag_register, interrupts_enabled_register + cdef int64_t cycles + cdef inline int check_interrupts(self) noexcept nogil cdef void set_interruptflag(self, int) noexcept nogil cdef bint handle_interrupt(self, uint8_t, uint16_t) noexcept nogil @cython.locals(opcode=uint16_t) cdef inline uint8_t fetch_and_execute(self) noexcept nogil - cdef int tick(self) noexcept nogil + @cython.locals(_cycles0=int64_t) + cdef int tick(self, int64_t) noexcept nogil cdef void save_state(self, IntIOInterface) noexcept cdef void load_state(self, IntIOInterface, int) noexcept @@ -50,4 +54,4 @@ cdef class CPU: cdef pyboy.core.mb.Motherboard mb - cdef dump_state(self, str) noexcept with gil + cdef void dump_state(CPU) noexcept with gil diff --git a/pyboy/core/cpu.py b/pyboy/core/cpu.py index 288e16efd..7632850aa 100644 --- a/pyboy/core/cpu.py +++ b/pyboy/core/cpu.py @@ -5,6 +5,8 @@ import array +import cython + from pyboy import utils from . import opcodes @@ -39,6 +41,9 @@ def __init__(self, mb): self.halted = False self.stopped = False self.is_stuck = False + self.cycles = 0 + + self.jit_jump = False def save_state(self, f): for n in [self.A, self.F, self.B, self.C, self.D, self.E]: @@ -53,6 +58,7 @@ def save_state(self, f): f.write(self.interrupts_enabled_register) f.write(self.interrupt_queued) f.write(self.interrupts_flag_register) + f.write_64bit(self.cycles) def load_state(self, f, state_version): self.A, self.F, self.B, self.C, self.D, self.E = [f.read() for _ in range(6)] @@ -69,45 +75,48 @@ def load_state(self, f, state_version): if state_version >= 8: self.interrupt_queued = f.read() self.interrupts_flag_register = f.read() - logger.debug("State loaded: %s", self.dump_state("")) + if state_version >= 12: + self.cycles = f.read_64bit() + # logger.debug("State loaded: %s", self.dump_state("")) - def dump_state(self, sym_label): - opcode_data = [ - self.mb.getitem(self.mb.cpu.PC + n) for n in range(3) - ] # Max 3 length, then we don't need to backtrack + def dump_state(self): + sym_label = "" + opcode_data = [self.mb.getitem(self.PC + n) for n in range(3)] # Max 3 length, then we don't need to backtrack opcode = opcode_data[0] - opcode_length = opcodes.OPCODE_LENGTHS[opcode] + opcode_length = opcodes.get_length(opcode) opcode_str = f"Opcode: [{opcodes.CPU_COMMANDS[opcode]}]" if opcode == 0xCB: opcode_str += f" {opcodes.CPU_COMMANDS[opcode_data[1]+0x100]}" else: opcode_str += " " + " ".join(f"{d:02X}" for d in opcode_data[1:opcode_length]) - return ( - "\n" - f"A: {self.mb.cpu.A:02X}, F: {self.mb.cpu.F:02X}, B: {self.mb.cpu.B:02X}, " - f"C: {self.mb.cpu.C:02X}, D: {self.mb.cpu.D:02X}, E: {self.mb.cpu.E:02X}, " - f"HL: {self.mb.cpu.HL:04X}, SP: {self.mb.cpu.SP:04X}, PC: {self.mb.cpu.PC:04X} ({sym_label})\n" - f"{opcode_str} " - f"Interrupts - IME: {self.mb.cpu.interrupt_master_enable}, " - f"IE: {self.mb.cpu.interrupts_enabled_register:08b}, " - f"IF: {self.mb.cpu.interrupts_flag_register:08b}\n" - f"LCD Intr.: {self.mb.lcd.cycles_to_interrupt()}, LY:{self.mb.lcd.LY}, LYC:{self.mb.lcd.LYC}\n" - f"Timer Intr.: {self.mb.timer.cycles_to_interrupt()}\n" - f"halted:{self.halted}, " - f"interrupt_queued:{self.interrupt_queued}, " - f"stopped:{self.stopped}\n" + print( + # "\n" + f"A: {self.A:02X}, F: {self.F:02X}, B: {self.B:02X}, " + f"C: {self.C:02X}, D: {self.D:02X}, E: {self.E:02X}, " + f"HL: {self.HL:04X}, SP: {self.SP:04X}, PC: {self.PC:04X} ({sym_label})" #\n" + # f"{opcode_str} " + # f"Interrupts - IME: {self.interrupt_master_enable}, " + # f"IE: {self.interrupts_enabled_register:08b}, " + # f"IF: {self.interrupts_flag_register:08b}\n" + # f"LCD Intr.: {self.mb.lcd.cycles_to_interrupt()}, LY:{self.mb.lcd.LY}, LYC:{self.mb.lcd.LYC}\n" + # f"Timer Intr.: {self.mb.timer.cycles_to_interrupt()}\n" + # f"halted:{self.halted}, " + # f"interrupt_queued:{self.interrupt_queued}, " + # f"stopped:{self.stopped}\n" ) def set_interruptflag(self, flag): self.interrupts_flag_register |= flag - def tick(self): + def tick(self, cycles_target): + _cycles0 = self.cycles + _target = _cycles0 + cycles_target + if self.check_interrupts(): + # TODO: Cycles it took to handle the interrupt? self.halted = False - # TODO: We return with the cycles it took to handle the interrupt - return 0 if self.halted and self.interrupt_queued: # GBCPUman.pdf page 20 @@ -117,62 +126,55 @@ def tick(self): self.PC += 1 self.PC &= 0xFFFF elif self.halted: - return 4 # TODO: Number of cycles for a HALT in effect? - - old_pc = self.PC # If the PC doesn't change, we're likely stuck - old_sp = self.SP # Sometimes a RET can go to the same PC, so we check the SP too. - cycles = self.fetch_and_execute() - if not self.halted and old_pc == self.PC and old_sp == self.SP and not self.is_stuck and not self.mb.breakpoint_singlestep: - logger.debug("CPU is stuck: %s", self.dump_state("")) - self.is_stuck = True + self.cycles += cycles_target # TODO: Number of cycles for a HALT in effect? self.interrupt_queued = False - return cycles + + self.bail = False + while self.cycles < _target: + # TODO: cpu-stuck check for blargg tests? + self.fetch_and_execute() + if self.bail: # Possible cycles-target changes + break def check_interrupts(self): if self.interrupt_queued: # Interrupt already queued. This happens only when using a debugger. return False - if (self.interrupts_flag_register & 0b11111) & (self.interrupts_enabled_register & 0b11111): - if self.handle_interrupt(INTR_VBLANK, 0x0040): - self.interrupt_queued = True - elif self.handle_interrupt(INTR_LCDC, 0x0048): - self.interrupt_queued = True - elif self.handle_interrupt(INTR_TIMER, 0x0050): - self.interrupt_queued = True - elif self.handle_interrupt(INTR_SERIAL, 0x0058): - self.interrupt_queued = True - elif self.handle_interrupt(INTR_HIGHTOLOW, 0x0060): - self.interrupt_queued = True - else: - logger.error("No interrupt triggered, but it should!") - self.interrupt_queued = False - return True - else: - self.interrupt_queued = False - return False - - def handle_interrupt(self, flag, addr): - if (self.interrupts_enabled_register & flag) and (self.interrupts_flag_register & flag): + raised_and_enabled = (self.interrupts_flag_register & 0b11111) & (self.interrupts_enabled_register & 0b11111) + if raised_and_enabled: # Clear interrupt flag if self.halted: self.PC += 1 # Escape HALT on return self.PC &= 0xFFFF - # Handle interrupt vectors if self.interrupt_master_enable: - self.interrupts_flag_register ^= flag # Remove flag - self.mb.setitem((self.SP - 1) & 0xFFFF, self.PC >> 8) # High - self.mb.setitem((self.SP - 2) & 0xFFFF, self.PC & 0xFF) # Low - self.SP -= 2 - self.SP &= 0xFFFF - - self.PC = addr - self.interrupt_master_enable = False - + if raised_and_enabled & INTR_VBLANK: + self.handle_interrupt(INTR_VBLANK, 0x0040) + elif raised_and_enabled & INTR_LCDC: + self.handle_interrupt(INTR_LCDC, 0x0048) + elif raised_and_enabled & INTR_TIMER: + self.handle_interrupt(INTR_TIMER, 0x0050) + elif raised_and_enabled & INTR_SERIAL: + self.handle_interrupt(INTR_SERIAL, 0x0058) + elif raised_and_enabled & INTR_HIGHTOLOW: + self.handle_interrupt(INTR_HIGHTOLOW, 0x0060) + self.interrupt_queued = True return True + else: + self.interrupt_queued = False return False + def handle_interrupt(self, flag, addr): + self.interrupts_flag_register ^= flag # Remove flag + self.mb.setitem((self.SP - 1) & 0xFFFF, self.PC >> 8) # High + self.mb.setitem((self.SP - 2) & 0xFFFF, self.PC & 0xFF) # Low + self.SP -= 2 + self.SP &= 0xFFFF + + self.PC = addr + self.interrupt_master_enable = False + def fetch_and_execute(self): opcode = self.mb.getitem(self.PC) if opcode == 0xCB: # Extension code diff --git a/pyboy/core/jit.pxd b/pyboy/core/jit.pxd new file mode 100644 index 000000000..8e3317a70 --- /dev/null +++ b/pyboy/core/jit.pxd @@ -0,0 +1,68 @@ +# +# License: See LICENSE.md file +# GitHub: https://github.com/Baekalfen/PyBoy +# + +from posix cimport dlfcn + +cimport cython +from libc.stdint cimport int64_t, uint8_t, uint16_t, uint32_t, uint64_t + +cimport pyboy +cimport pyboy.core.cartridge.base_mbc +cimport pyboy.core.cpu +from pyboy.logging.logging cimport Logger + +from . cimport opcodes + + +cdef Logger logger +ctypedef int(*f_type)(pyboy.core.cpu.CPU, int64_t) noexcept nogil + +cdef class JIT: + cdef pyboy.core.cpu.CPU cpu + cdef pyboy.core.cartridge.base_mbc.BaseMBC cartridge + cdef dict queue + cdef bint thread_stop + cdef object thread_queue + cdef object thread + + cdef f_type[0xFFFFFF] array + cdef int[0xFFFFFF] cycles + + cdef inline int load(self, str module_name, str module_path, str file_base, list block_manifest) except -1 with gil: + # logger.debug("JIT LOAD %d", block_id) + cdef void* handle = dlfcn.dlopen(module_path.encode(), dlfcn.RTLD_NOW | dlfcn.RTLD_GLOBAL) # RTLD_LAZY? + if (handle == NULL): + return -1 + dlfcn.dlerror() # Clear error + + cdef f_type execute + for func_name, block_id, block_max_cycles in block_manifest: + execute = dlfcn.dlsym(handle, func_name.encode()) + if (execute == NULL): + print(dlfcn.dlerror()) + + self.array[block_id] = execute + self.cycles[block_id] = block_max_cycles + + cdef inline int execute(self, int block_id, int64_t cycles_target) noexcept nogil: + # logger.debug("JIT EXECUTE %d", block_id) + return self.array[block_id](self.cpu, cycles_target) + + cdef void stop(self) noexcept with gil + + cdef uint8_t getitem_bank(self, uint8_t, uint16_t) noexcept nogil + + cdef void _jit_clear(self) noexcept with gil + cdef tuple get_module_name(self, str) with gil + cdef void gen_files(self, str, str, list) noexcept with gil + cdef void compile(self, str, str, str) noexcept with gil + cdef object emit_code(self, object, str) with gil + # @cython.locals(block_max_cycles=int64_t) + # cdef bint analyze(self, int, int64_t, bint) noexcept with gil + cdef void offload(self, int, int64_t, bint) noexcept with gil + @cython.locals(block_id=int64_t, cycles_target=int64_t, interrupt_master_enable=bint, count=int64_t) + cdef void process(self) noexcept with gil + +cpdef void threaded_processor(JIT) noexcept with gil diff --git a/pyboy/core/jit.py b/pyboy/core/jit.py new file mode 100644 index 000000000..fd5a60602 --- /dev/null +++ b/pyboy/core/jit.py @@ -0,0 +1,524 @@ +# +# License: See LICENSE.md file +# GitHub: https://github.com/Baekalfen/PyBoy +# + +import hashlib +import importlib +import os +import queue +import sysconfig +import threading +import time +from distutils.command.build_ext import build_ext +from distutils.core import Distribution, Extension +from importlib.machinery import ExtensionFileLoader + +from Cython.Build import cythonize +from Cython.Compiler.Nodes import CFuncDefNode + + +# HACK: Disable this check to allow usage of CPU outside of GIL +def patched_validate_type_visibility(self, type, pos, env): + pass + + +CFuncDefNode._validate_type_visibility = patched_validate_type_visibility + +import pyboy +from pyboy import utils +from pyboy.core.opcodes_gen import opcodes as opcodes_gen + +from . import opcodes + +logger = pyboy.logging.get_logger(__name__) + +try: + from cython import compiled + cythonmode = compiled +except ImportError: + cythonmode = False + +EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX") +JIT_EXTENSION = ".pyx" if cythonmode else ".py" + +if not cythonmode: + JIT_PREAMBLE = "FLAGC, FLAGH, FLAGN, FLAGZ = range(4, 8)\n\n" +else: + JIT_PREAMBLE = """ +from libc.stdint cimport uint8_t, uint16_t, int16_t, uint32_t, int64_t +from pyboy.core cimport cpu as _cpu + +""" + +# NOTE: print('\n'.join([f"0x{i:02X}, # {x}" for i,x in enumerate(pyboy.core.opcodes.CPU_COMMANDS) if "JR" in x])) +jr_instruction = [ + 0x18, # JR r8 + 0x20, # JR NZ,r8 + 0x28, # JR Z,r8 + 0x30, # JR NC,r8 + 0x38, # JR C,r8 +] +jp_instruction = [ + 0xC2, # JP NZ,a16 + 0xC3, # JP a16 + 0xCA, # JP Z,a16 + 0xD2, # JP NC,a16 + 0xDA, # JP C,a16 +] +boundary_instruction = [ + 0xC7, # RST 00H + 0xCF, # RST 08H + 0xD7, # RST 10H + 0xDF, # RST 18H + 0xE7, # RST 20H + 0xEF, # RST 28H + 0xF7, # RST 30H + 0xFF, # RST 38H + 0xC4, # CALL NZ,a16 + 0xCC, # CALL Z,a16 + 0xCD, # CALL a16 + 0xD4, # CALL NC,a16 + 0xDC, # CALL C,a16 + 0xC0, # RET NZ + 0xC8, # RET Z + 0xC9, # RET + 0xD0, # RET NC + 0xD8, # RET C + 0xD9, # RETI + 0xE9, # JP (HL) + 0x76, # HALT + 0x10, # STOP + 0xFB, # EI + 0xDB, # Breakpoint/hook +] + + +def threaded_processor(jit): + while not jit.thread_stop: + while not jit.thread_queue.empty(): + message = jit.thread_queue.get() + block_id, cycles_target, interrupt_master_enable = message + + if jit.queue.get(block_id) is None: + jit.queue[block_id] = [] + jit.queue[block_id].append((cycles_target, interrupt_master_enable)) + logger.critical("PROCESSING!") + jit.process() + time.sleep(0.1) + + +class JIT: + def __init__(self, cpu, cartridge): + self.cpu = cpu + self.cartridge = cartridge + self._jit_clear() + self.init_load() + + self.thread_queue = queue.Queue() + self.thread = threading.Thread(target=threaded_processor, args=(self, )) + self.thread_stop = False + self.thread.start() + + def stop(self): + self.thread_stop = True + + def _jit_clear(self): + self.queue = {} + for n in range(0xFFFFFF): + self.cycles[n] = 0 + + # TODO: Taken from https://github.com/cython/cython/blob/4e0eee43210d6b7822859f3001202910888644af/Cython/Build/Inline.py#L95 + def _get_build_extension(self): + dist = Distribution() + # Ensure the build respects distutils configuration by parsing + # the configuration files + config_files = dist.find_config_files() + dist.parse_config_files(config_files) + build_extension = build_ext(dist) + build_extension.finalize_options() + return build_extension + + def get_module_name(self, code_text): + m = hashlib.sha1() + m.update(code_text.encode()) + _hash = m.digest().hex() + + module_name = "jit_" + _hash + module_path = module_name + EXT_SUFFIX #os.path.splitext(jit_file)[0] + '.so' + + # logger.debug("%s %s", self.cartridge.filename, _hash) + file_base = os.path.splitext(self.cartridge.filename)[0].replace(".", "_") + "_jit_" + _hash # Generate name + return module_name, file_base, module_path + + def gen_files(self, code_text, file_base, block_manifest): + # https://github.com/cython/cython/blob/4e0eee43210d6b7822859f3001202910888644af/Cython/Build/Inline.py#L141 + + if not os.path.exists(file_base + JIT_EXTENSION): + # logger.info("Compiling JIT block: %s", module_path) + with open(file_base + JIT_EXTENSION, "w") as f: + f.write(code_text) + + if cythonmode: + jit_file_pxd = file_base + ".pxd" + with open(jit_file_pxd, "w") as f: + f.write("from pyboy.core cimport cpu as _cpu\nfrom libc.stdint cimport int64_t\n\n") + for func_name, _, _ in block_manifest: + f.write(f"cdef public int {func_name}(_cpu.CPU __cpu, int64_t cycles_target) noexcept nogil\n") + + def compile(self, module_name, file_base, module_path): + cythonize_files = [ + Extension( + module_name, + [file_base + JIT_EXTENSION], + extra_compile_args=["-O3", "-march=native", "-mtune=native"], + ) + ] + build_extension = self._get_build_extension() + build_extension.extensions = cythonize( + [*cythonize_files], + nthreads=1, + annotate=False, + gdb_debug=True, + language_level=3, + compiler_directives={ + "boundscheck": False, + "cdivision": True, + "cdivision_warnings": False, + "infer_types": True, + "initializedcheck": False, + "nonecheck": False, + "overflowcheck": False, + # "profile" : True, + "wraparound": False, + "legacy_implicit_noexcept": True, + }, + ) + build_extension.inplace = True + # build_extension.build_temp = "./"# os.path.dirname(jit_file) + build_extension.run() + + def emit_code(self, code_block, func_name): + # TODO: Detect loops + # TODO: memory address (eliminate bail, know if it's a RAM or ROM write, interpolate specific memory pointer and bypass getitem) + # TODO: Easy to detect high RAM LDH + # TODO: Detect memcpy loop and replace. We don't need to LD A, (HL+) and then LD (DE), A. For each byte. This means to allow relative jumps within the block + # TODO: Invalidate JIT block on breakpoint + + code_text = "" + if not cythonmode: + code_text += f"def {func_name}(cpu, cycles_target):\n\t" + code_text += "flag = 0\n\tt = 0\n\tv = 0\n\t_cycles0 = cpu.cycles\n\t_target = _cycles0 + cycles_target" + else: + code_text += f"cdef public void {func_name}(_cpu.CPU cpu, int64_t cycles_target) noexcept nogil:" + code_text += "\n\tcdef uint8_t flag\n\tcdef int t\n\tcdef int v\n\tcdef int64_t _cycles0 = cpu.cycles\n\tcdef int64_t _target = _cycles0 + cycles_target" + code_text += """ +\tcdef uint16_t FLAGC = 4 +\tcdef uint16_t FLAGH = 5 +\tcdef uint16_t FLAGN = 6 +\tcdef uint16_t FLAGZ = 7""" + + def emit_opcode(indent, opcode, length, pc, literal1, literal2): + opcode_handler = opcodes_gen[opcode] + opcode_name = opcode_handler.name.split()[0] + preamble = f"\n\t\n\t" + "# " + opcode_handler.name + f" (PC: 0x{pc:04x})\n\t" + if length == 2: + v = literal1 + preamble += f"v = 0x{v:02x} # {v}\n\t" + elif length == 3: + v = (literal2 << 8) + literal1 + preamble += f"v = 0x{v:04x} # {v}\n\t" + + tmp_code = opcode_handler.functionhandlers[opcode_name]()._code_body() + if "if" in tmp_code: + # Return early on jump + tmp_code = tmp_code.replace("else:", f"\treturn\n\telse:") + elif "cpu.mb.setitem" in tmp_code: + # Return early on state-altering writes + tmp_code += f"\n\tif cpu.bail: return" + return (preamble + tmp_code).replace("\t", indent) + + for i, (opcode, length, pc, literal1, literal2) in enumerate(code_block): + if opcode < 0x200: # Regular opcode + code_text += emit_opcode("\t", opcode, length, pc, literal1, literal2) + elif opcode == 0x200: # Loop body + loop_body_cycles, jump_to, jump_from, _block = length, pc, literal1, literal2 + # breakpoint() + code_text += f"\n\n\twhile True: # Loop body (PC: 0x{jump_to:04X} to 0x{jump_from:04X})" + for i, (opcode, length, pc, literal1, literal2) in enumerate(_block[:-1]): + code_text += emit_opcode("\t\t", opcode, length, pc, literal1, literal2) + + # Loop condition + opcode, length, pc, literal1, literal2 = _block[-1] + loop_condition = emit_opcode("\t\t", opcode, length, pc, literal1, literal2) + loop_condition = loop_condition.replace( + "return", + f'if cpu.cycles + {loop_body_cycles} < _target:\n\t\t\t\t\tcpu.jit_jump=False;continue\n\t\t\t\telse:\n\t\t\t\t\tcpu.jit_jump=False;return' + ) + loop_condition += "\n\t\tbreak" + code_text += loop_condition + elif opcode == 0x201: # Remainder of block + remainder_cycles = length + code_text += f'\n\tif cpu.cycles + {remainder_cycles} < _target: return' + + code_text += "\n\treturn\n\n" + # opcodes[7].functionhandlers[opcodes[7].name.split()[0]]().branch_op + # if .getitem in code, commit timer.tick(cycles); cycles = 0 + + return code_text + + def getitem_bank(self, bank, i): + if 0x0000 <= i < 0x4000: # 16kB ROM bank #0 + return self.cartridge.rombanks[0, i] # TODO: Actually self.cartridge.rombank_selected_low + elif 0x4000 <= i < 0x8000: # 16kB switchable ROM bank + return self.cartridge.rombanks[bank, i - 0x4000] + + def collect_block(self, block_id, cycles_target): + code_block = [] + PC = block_id >> 8 + _PC = PC + assert PC < 0x8000 + rom_bank = block_id & 0xFF + + has_internal_jump = False + block_max_cycles = 0 + while True: + # for _ in range(200): + # while block_max_cycles < 200: + opcode = self.getitem_bank(rom_bank, PC) + if opcode == 0xCB: # Extension code + PC += 1 + opcode = self.getitem_bank(rom_bank, PC) + opcode += 0x100 # Internally shifting look-up table + opcode_length = opcodes.OPCODE_LENGTHS[opcode] + opcode_max_cycles = opcodes.OPCODE_MAX_CYCLES[opcode] + # if (not interrupt_master_enable) and (block_max_cycles + opcode_max_cycles > cycles_target): + if (block_max_cycles + opcode_max_cycles > cycles_target): + break + block_max_cycles += opcode_max_cycles + l1, l2 = self.getitem_bank(rom_bank, PC + 1), self.getitem_bank(rom_bank, PC + 2) + code_block.append((opcode, opcode_length, PC, l1, l2)) + PC += opcode_length + + is_jr = opcode in jr_instruction + is_jp = opcode in jp_instruction + + if opcode in boundary_instruction: + break + elif is_jr or is_jp: + # We assume it's the ending instruction? Or is the validation at the top? + if not has_internal_jump: + if is_jr: + jump_to = PC + ((l1 ^ 0x80) - 0x80) + else: + jump_to = ((l2 << 8) | l1) + + if _PC <= jump_to < PC: # Detect internal jump + has_internal_jump = True + else: + # The jump is to somewhere else + break + else: + # Expected jump away + # TODO: Just one loop? + break + + return code_block, block_max_cycles, has_internal_jump + + def print_block(self, code_block): + def opcode_translate(opcode): + if opcode == 0x200: + return "loop block" + else: + return pyboy.core.opcodes.CPU_COMMANDS[opcode] + + print( + "\n".join( + f"0x{opcode:02X} {opcode_translate(opcode)}\tlen: {opcode_length}\tPC: {pc:04X}\tlit1: {l1:02X}\tlit2: {l2:02X}\tlit: {(l2<<8) | l1:04X}\t r8: {pc + ((l1 ^ 0x80) - 0x80):04X}" + for opcode, opcode_length, pc, l1, l2 in code_block + ) + ) + + def check_no_overlap(self, ranges): + if len(ranges) == 1: + return True + + # Sort the ranges by the starting points + ranges.sort(key=lambda x: x[0]) + + # Traverse through the ranges to check for overlap + for i in range(1, len(ranges)): + # If the start of the current range is less than the end of the previous range, there's an overlap + if ranges[i][0] < ranges[i - 1][1]: + return False + + return True + + def optimize_block(self, raw_code_block, raw_block_max_cycles, has_internal_jump): + if not has_internal_jump: + return raw_code_block + + # _, _, PC, _, _ = raw_code_block[0] + jumps = [] + for opcode, opcode_length, PC, l1, l2 in raw_code_block: + is_jr = opcode in jr_instruction + is_jp = opcode in jp_instruction + if is_jp or is_jr: + if is_jr: + jump_to = PC + ((l1 ^ 0x80) - 0x80) + elif is_jp: + jump_to = ((l2 << 8) | l1) + + jumps.append((jump_to, PC)) # Sorted as (start, end) + + if not self.check_no_overlap(jumps): + return raw_code_block + + new_block = [] + _block = [] + current_jump = jumps.pop() + for i, (opcode, opcode_length, pc, l1, l2) in enumerate(raw_code_block): + if current_jump and current_jump[0] <= pc < current_jump[1]: + # Collect body + _block.append((opcode, opcode_length, pc, l1, l2)) + elif current_jump and pc == current_jump[1]: + # Add loop block + _block.append((opcode, opcode_length, pc, l1, l2)) + loop_body_cycles = sum(opcodes.OPCODE_MAX_CYCLES[opcode] for opcode, _, _, _, _, in _block) + new_block.append((0x200, loop_body_cycles, current_jump[0], current_jump[1], _block)) + _block = [] + current_jump = jumps.pop() if jumps else None + + remainder_cycles = sum(opcodes.OPCODE_MAX_CYCLES[opcode] for opcode, _, _, _, _, in raw_code_block[i:]) + new_block.append((0x201, remainder_cycles, None, None, None)) + else: + # Add regular opcode + new_block.append((opcode, opcode_length, pc, l1, l2)) + return new_block + + def invalidate(self, bank, address): + # Invalidate any JIT block that crosses this bank and adress. + # Used when adding breakpoints and hooks + pass + + def offload(self, block_id, cycles_target, interrupt_master_enable): + if cycles_target < 200: + return + + self.thread_queue.put((block_id, cycles_target, interrupt_master_enable)) + + def init_load(self): + # breakpoint() + logger.critical("initload") + for module_path in os.listdir(): + # logger.critical("file: %s", module_path) + if module_path.startswith("jit_") and module_path.endswith(EXT_SUFFIX): + # logger.critical("match") + module_name = module_path.split(".")[0] + file_base = os.path.splitext(self.cartridge.filename)[0].replace(".", "_") + "_" + module_name + + spec = importlib.util.spec_from_file_location(module_name, module_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + if getattr(module, "cartridge") == self.cartridge.gamename: + block_manifest = getattr(module, "block_manifest") + no_blocks = len(block_manifest) + logger.critical("Loading %d precompiled blocks from %s", no_blocks, file_base) + self.load(module_name, module_path, file_base, block_manifest) + del spec, module + + def process(self): + # TODO: Send cycles_target and which interrupt to jit_analyze. Track interrupt enable and flags on JIT block? + # Interrupts are likely to hit the same rythm -- sync on halt, do hblank, do vblank, etc. + # JIT interrupt routines and just straight to them? + # Predict which interrupt and inline interrupt vector? + + priority_list = [] + for k, v in self.queue.items(): + priority_list.append((k, len(v))) # block_id, number of hits + + block_manifest = [] + code_text = JIT_PREAMBLE + # Pick the 10 most frequent + + no_blocks = 0 + for block_id, count in sorted(priority_list, key=lambda x: x[1], reverse=True): + # if no_blocks >= 500: + # break + # TODO: Currently just picking the first entry! + cycles_target, interrupt_master_enable = self.queue[block_id][0] + + # logger.critical("analyze: %x, %d, %d", block_id, cycles_target, interrupt_master_enable) + + raw_code_block, block_max_cycles, has_internal_jump = self.collect_block(block_id, cycles_target) + code_block = self.optimize_block(raw_code_block, block_max_cycles, has_internal_jump) + + if block_max_cycles < 100: + self.cycles[block_id] = -1 # Don't retry + continue + + no_blocks += 1 + + # if len(code_block) < 25: + # continue + + func_name = f"block_{block_id:08x}" + + # logger.debug("Code block size: %d, block cycles: %d", len(code_block), block_max_cycles) + code_text += self.emit_code(code_block, func_name) + + block_manifest.append((func_name, block_id, block_max_cycles)) + + if no_blocks < 20: + temp_queue = {} + for _, block_id, _ in block_manifest: + temp_queue[block_id] = self.queue[block_id] + self.queue = temp_queue # Clear all unwanted blocks and wait for more blocks to come in + return + else: + self.queue = {} # Throw the rest away to not grow the list indefinitely. Maybe there's a better way. + + logger.critical("processing: %d blocks", no_blocks) + + code_text = "block_manifest = " + str( + block_manifest + ) + "\n" + f"cartridge = '{self.cartridge.gamename}'\n\n" + code_text + + module_name, file_base, module_path = self.get_module_name(code_text) + self.gen_files(code_text, file_base, block_manifest) + if cythonmode: + self.compile(module_name, file_base, module_path) + # logger.debug("Loading: %s %x %d", file_base, block_id, block_max_cycles) + self.load(module_name, module_path, file_base, block_manifest) + + +# Unfortunately CPython/PyPy code has to be hidden in an exec call to +# prevent Cython from trying to parse it. This block provides the +# functions that are otherwise implemented as inlined cdefs in the pxd +if not cythonmode: + exec( + """ +def _load(self, module_name, module_path, file_base, block_manifest): + # spec = importlib.util.spec_from_file_location(module_name, loader=ExtensionFileLoader(module_name, file_base + JIT_EXTENSION)) + spec = importlib.util.spec_from_file_location(module_name, file_base + JIT_EXTENSION) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + for func_name, block_id, block_max_cycles in block_manifest: + self.array[block_id] = getattr(module, func_name) + self.cycles[block_id] = block_max_cycles + +def _jit_clear(self): + self.queue = {} + self.cycles = [0] * 0xFFFFFF + self.array = [None] * 0xFFFFFF + +def _execute(self, block_id, cycles_target): + return self.array[block_id](self.cpu, cycles_target) + +JIT.load = _load +JIT._jit_clear = _jit_clear +JIT.execute = _execute +""", globals(), locals() + ) diff --git a/pyboy/core/lcd.pxd b/pyboy/core/lcd.pxd index 4b4ed074a..9949d23f8 100644 --- a/pyboy/core/lcd.pxd +++ b/pyboy/core/lcd.pxd @@ -48,10 +48,11 @@ cdef class LCD: cdef PaletteRegister OBP1 cdef Renderer renderer cdef uint8_t[144][5] _scanlineparameters + cdef uint64_t last_cycles + cdef int64_t _cycles_to_interrupt, _cycles_to_frame @cython.locals(interrupt_flag=uint8_t,bx=int,by=int,wx=int,wy=int) cdef uint8_t tick(self, int) noexcept nogil - cdef int64_t cycles_to_interrupt(self) noexcept nogil cdef void set_lcdc(self, uint8_t) noexcept nogil cdef uint8_t get_lcdc(self) noexcept nogil @@ -112,7 +113,7 @@ cdef class LCDCRegister: cdef uint16_t backgroundmap_offset cdef uint16_t windowmap_offset - cpdef int _get_sprite_height(self) + cpdef int _get_sprite_height(self) noexcept cdef class Renderer: cdef uint8_t[:] _tilecache0_state, _tilecache1_state, _spritecache0_state, _spritecache1_state diff --git a/pyboy/core/lcd.py b/pyboy/core/lcd.py index 171d114c2..fc548499c 100644 --- a/pyboy/core/lcd.py +++ b/pyboy/core/lcd.py @@ -58,6 +58,9 @@ def __init__(self, cgb, cartridge_cgb, color_palette, cgb_color_palette, randomi self.double_speed = False self.cgb = cgb self._scanlineparameters = [[0, 0, 0, 0, 0] for _ in range(ROWS)] + self.last_cycles = 0 + self._cycles_to_interrupt = 0 + self._cycles_to_frame = FRAME_CYCLES if self.cgb: # Setting for both modes, even though CGB is ignoring them. BGP[0] used in scanline_blank. @@ -103,9 +106,6 @@ def get_stat(self): def set_stat(self, value): self._STAT.set(value) - def cycles_to_interrupt(self): - return self.clock_target - self.clock - def cycles_to_mode0(self): multiplier = 2 if self.double_speed else 1 mode2 = 80 * multiplier @@ -130,10 +130,18 @@ def cycles_to_mode0(self): # logger.critical("Unsupported STAT mode: %d", mode) # return 0 - def tick(self, cycles): + def tick(self, _cycles): + cycles = _cycles - self.last_cycles + if cycles == 0: + return False + self.last_cycles = _cycles + interrupt_flag = 0 self.clock += cycles + # if cycles > 28: + # logger.error("lcd tick cycles=%d", cycles) + if self._LCDC.lcd_enable: if self.clock >= self.clock_target: # Change to next mode @@ -198,13 +206,15 @@ def tick(self, cycles): self.next_stat_mode = 2 else: # See also `self.set_lcdc` - if self.clock >= FRAME_CYCLES: + if self.clock >= FRAME_CYCLES: # TODO: clock_target? self.frame_done = True self.clock %= FRAME_CYCLES # Renderer self.renderer.blank_screen(self) + self._cycles_to_interrupt = self.clock_target - self.clock + self._cycles_to_frame = self.clock - FRAME_CYCLES return interrupt_flag def save_state(self, f): @@ -239,6 +249,7 @@ def save_state(self, f): # CGB f.write(self.cgb) f.write(self.double_speed) + f.write_64bit(self.last_cycles) f.write_64bit(self.clock) f.write_64bit(self.clock_target) f.write(self.next_stat_mode) @@ -293,8 +304,11 @@ def load_state(self, f, state_version): self.cgb = _cgb self.double_speed = f.read() + if state_version >= 12: + self.last_cycles = f.read_64bit() self.clock = f.read_64bit() self.clock_target = f.read_64bit() + self._cycles_to_frame = self.clock - FRAME_CYCLES self.next_stat_mode = f.read() if self.cgb: diff --git a/pyboy/core/mb.pxd b/pyboy/core/mb.pxd index c43f7fb18..77bd9679f 100644 --- a/pyboy/core/mb.pxd +++ b/pyboy/core/mb.pxd @@ -11,6 +11,7 @@ cimport pyboy.core.bootrom cimport pyboy.core.cartridge.base_mbc cimport pyboy.core.cpu cimport pyboy.core.interaction +cimport pyboy.core.jit cimport pyboy.core.lcd cimport pyboy.core.ram cimport pyboy.core.sound @@ -21,12 +22,12 @@ from pyboy.utils cimport IntIOInterface, WindowEvent cdef Logger logger +cdef int64_t MAX_CYCLES cdef uint16_t STAT, LY, LYC cdef short VBLANK, LCDC, TIMER, SERIAL, HIGHTOLOW cdef int INTR_VBLANK, INTR_LCDC, INTR_TIMER, INTR_SERIAL, INTR_HIGHTOLOW cdef int STATE_VERSION - cdef class Motherboard: cdef pyboy.core.interaction.Interaction interaction cdef pyboy.core.bootrom.BootROM bootrom @@ -35,6 +36,8 @@ cdef class Motherboard: cdef pyboy.core.cpu.CPU cpu cdef pyboy.core.timer.Timer timer cdef pyboy.core.sound.Sound sound + cdef bint jit_enabled + cdef pyboy.core.jit.JIT jit cdef pyboy.core.cartridge.base_mbc.BaseMBC cartridge cdef bint bootrom_enabled cdef char[1024] serialbuffer @@ -57,9 +60,11 @@ cdef class Motherboard: cdef inline bint processing_frame(self) noexcept nogil + cdef inline int cpu_pending_interrupt(self) noexcept nogil + cdef void buttonevent(self, WindowEvent) noexcept cdef void stop(self, bint) noexcept - @cython.locals(cycles=int64_t, mode0_cycles=int64_t, breakpoint_index=int64_t) + @cython.locals(cycles=int64_t, cycles_target=int64_t, mode0_cycles=int64_t, breakpoint_index=int64_t) cdef bint tick(self) noexcept nogil cdef void switch_speed(self) noexcept nogil @@ -85,7 +90,7 @@ cdef class HDMA: cdef uint16_t curr_dst cdef void set_hdma5(self, uint8_t, Motherboard) noexcept nogil - cdef int tick(self, Motherboard) noexcept nogil + cdef int64_t tick(self, Motherboard) noexcept nogil cdef void save_state(self, IntIOInterface) noexcept cdef void load_state(self, IntIOInterface, int) noexcept diff --git a/pyboy/core/mb.py b/pyboy/core/mb.py index 78264a527..a044e3eb6 100644 --- a/pyboy/core/mb.py +++ b/pyboy/core/mb.py @@ -3,10 +3,9 @@ # GitHub: https://github.com/Baekalfen/PyBoy # -from pyboy import utils from pyboy.utils import STATE_VERSION -from . import bootrom, cartridge, cpu, interaction, lcd, ram, sound, timer +from . import bootrom, cartridge, cpu, interaction, jit, lcd, ram, sound, timer INTR_VBLANK, INTR_LCDC, INTR_TIMER, INTR_SERIAL, INTR_HIGHTOLOW = [1 << x for x in range(5)] OPCODE_BRK = 0xDB @@ -15,6 +14,8 @@ logger = pyboy.logging.get_logger(__name__) +MAX_CYCLES = 1 << 16 + class Motherboard: def __init__( @@ -26,6 +27,7 @@ def __init__( sound_enabled, sound_emulated, cgb, + jit_enabled, randomize=False, ): if bootrom_file is not None: @@ -42,6 +44,9 @@ def __init__( self.bootrom = bootrom.BootROM(bootrom_file, cgb) self.ram = ram.RAM(cgb, randomize=randomize) self.cpu = cpu.CPU(self) + self.jit_enabled = jit_enabled + if self.jit_enabled: + self.jit = jit.JIT(self.cpu, self.cartridge) if cgb: self.lcd = lcd.CGBLCD( @@ -203,6 +208,8 @@ def buttonevent(self, key): self.cpu.set_interruptflag(INTR_HIGHTOLOW) def stop(self, save): + if self.jit_enabled: + self.jit.stop() self.sound.stop() if save: self.cartridge.stop() @@ -273,14 +280,16 @@ def processing_frame(self): self.lcd.frame_done = False # Clear vblank flag for next iteration return b + def cpu_pending_interrupt(self): + return self.cpu.interrupt_queued or (self.cpu.interrupts_flag_register & + 0b11111) & (self.cpu.interrupts_enabled_register & 0b11111) + def tick(self): while self.processing_frame(): if self.cgb and self.hdma.transfer_active and self.lcd._STAT._mode & 0b11 == 0: - cycles = self.hdma.tick(self) + self.cpu.jit_jump = False + self.cpu.cycles = self.cpu.cycles + self.hdma.tick(self) else: - cycles = self.cpu.tick() - - if self.cpu.halted: # Fast-forward to next interrupt: # As we are halted, we are guaranteed, that our state # cannot be altered by other factors than time. @@ -288,35 +297,53 @@ def tick(self): # it gets triggered mid-frame or by next frame # Serial is not implemented, so this isn't a concern - # Help Cython with types - mode0_cycles = 1 << 32 + mode0_cycles = MAX_CYCLES if self.cgb and self.hdma.transfer_active: mode0_cycles = self.lcd.cycles_to_mode0() - cycles = max( - 0, + cycles_target = max( + 4, min( - self.lcd.cycles_to_interrupt(), - self.timer.cycles_to_interrupt(), + self.timer._cycles_to_interrupt, + self.lcd._cycles_to_interrupt, # TODO: Be more agreesive. Only if actual interrupt enabled. + self.lcd._cycles_to_frame, + self.sound._cycles_to_interrupt, # TODO: Not implemented # self.serial.cycles_to_interrupt(), mode0_cycles ) ) + # Inject special opcode instead? ~~Long immediate as identifier~~ + # Special opcode cannot be more than 1 byte, to avoid jumps to sub-parts of the jit block + # Compile in other thread, acquire memory lock between frames + if self.jit_enabled and self.cpu.PC < 0x8000 and not self.bootrom_enabled: + block_id = (self.cpu.PC << 8) + block_id |= self.cartridge.rombank_selected + + block_max_cycles = self.jit.cycles[block_id] + + # Hot path + if 0 < block_max_cycles and not self.cpu_pending_interrupt(): + self.jit.execute(block_id, cycles_target) + else: + if self.cpu.jit_jump and block_max_cycles == 0: + self.jit.offload(block_id, cycles_target, self.cpu.interrupt_master_enable) + + self.cpu.jit_jump = False + self.cpu.tick(cycles_target) + else: + self.cpu.jit_jump = False + self.cpu.tick(cycles_target) + #TODO: Support General Purpose DMA # https://gbdev.io/pandocs/CGB_Registers.html#bit-7--0---general-purpose-dma - # TODO: Unify interface - sclock = self.sound.clock - if self.cgb and self.double_speed: - self.sound.clock = sclock + cycles//2 - else: - self.sound.clock = sclock + cycles + self.sound.tick(self.cpu.cycles, self.double_speed) - if self.timer.tick(cycles): + if self.timer.tick(self.cpu.cycles): self.cpu.set_interruptflag(INTR_TIMER) - lcd_interrupt = self.lcd.tick(cycles) + lcd_interrupt = self.lcd.tick(self.cpu.cycles) if lcd_interrupt: self.cpu.set_interruptflag(lcd_interrupt) @@ -350,8 +377,7 @@ def getitem(self, i): bank_offset = 0 if self.cgb and 0xD000 <= i: # Find which bank to read from at FF70 - bank = self.getitem(0xFF70) - bank &= 0b111 + bank = self.ram.non_io_internal_ram1[0xFF70 - 0xFF4C] & 0b111 if bank == 0x0: bank = 0x01 bank_offset = (bank-1) * 0x1000 @@ -364,6 +390,10 @@ def getitem(self, i): elif 0xFEA0 <= i < 0xFF00: # Empty but unusable for I/O return self.ram.non_io_internal_ram0[i - 0xFEA0] elif 0xFF00 <= i < 0xFF4C: # I/O ports + # NOTE: A bit ad-hoc, but interrupts can occur right between writes + if self.timer.tick(self.cpu.cycles): + self.cpu.set_interruptflag(INTR_TIMER) + if i == 0xFF04: return self.timer.DIV elif i == 0xFF05: @@ -375,6 +405,7 @@ def getitem(self, i): elif i == 0xFF0F: return self.cpu.interrupts_flag_register elif 0xFF10 <= i < 0xFF40: + self.sound.tick(self.cpu.cycles, self.double_speed) return self.sound.get(i - 0xFF10) elif i == 0xFF40: return self.lcd.get_lcdc() @@ -442,9 +473,11 @@ def setitem(self, i, value): if 0x0000 <= i < 0x4000: # 16kB ROM bank #0 # Doesn't change the data. This is for MBC commands self.cartridge.setitem(i, value) + self.cpu.bail = True # TODO: This is not something to bail for in non-jit mode elif 0x4000 <= i < 0x8000: # 16kB switchable ROM bank # Doesn't change the data. This is for MBC commands self.cartridge.setitem(i, value) + self.cpu.bail = True # TODO: This is not something to bail for in non-jit mode elif 0x8000 <= i < 0xA000: # 8kB Video RAM if not self.cgb or self.lcd.vbk.active_bank == 0: self.lcd.VRAM0[i - 0x8000] = value @@ -475,6 +508,10 @@ def setitem(self, i, value): elif 0xFEA0 <= i < 0xFF00: # Empty but unusable for I/O self.ram.non_io_internal_ram0[i - 0xFEA0] = value elif 0xFF00 <= i < 0xFF4C: # I/O ports + # NOTE: A bit ad-hoc, but interrupts can occur right between writes + if self.timer.tick(self.cpu.cycles): + self.cpu.set_interruptflag(INTR_TIMER) + if i == 0xFF00: self.ram.io_ports[i - 0xFF00] = self.interaction.pull(value) elif i == 0xFF01: @@ -493,6 +530,7 @@ def setitem(self, i, value): elif i == 0xFF0F: self.cpu.interrupts_flag_register = value elif 0xFF10 <= i < 0xFF40: + self.sound.tick(self.cpu.cycles, self.double_speed) self.sound.set(i - 0xFF10, value) elif i == 0xFF40: self.lcd.set_lcdc(value) @@ -526,6 +564,7 @@ def setitem(self, i, value): self.lcd.WX = value else: self.ram.io_ports[i - 0xFF00] = value + self.cpu.bail = True elif 0xFF4C <= i < 0xFF80: # Empty but unusable for I/O if self.bootrom_enabled and i == 0xFF50 and (value == 0x1 or value == 0x11): logger.debug("Bootrom disabled!") @@ -533,6 +572,7 @@ def setitem(self, i, value): # CGB registers elif self.cgb and i == 0xFF4D: self.key1 = value + self.cpu.bail = True elif self.cgb and i == 0xFF4F: self.lcd.vbk.set(value) elif self.cgb and i == 0xFF51: @@ -545,6 +585,7 @@ def setitem(self, i, value): self.hdma.hdma4 = value # & 0xF0 elif self.cgb and i == 0xFF55: self.hdma.set_hdma5(value, self) + self.cpu.bail = True elif self.cgb and i == 0xFF68: self.lcd.bcps.set(value) elif self.cgb and i == 0xFF69: @@ -563,6 +604,7 @@ def setitem(self, i, value): self.ram.internal_ram1[i - 0xFF80] = value elif i == 0xFFFF: # Interrupt Enable Register self.cpu.interrupts_enabled_register = value + self.cpu.bail = True # else: # logger.critical("Memory access violation. Tried to write: 0x%0.2x to 0x%0.4x", value, i) diff --git a/pyboy/core/opcodes.pxd b/pyboy/core/opcodes.pxd index e14b47bea..d0af7016d 100644 --- a/pyboy/core/opcodes.pxd +++ b/pyboy/core/opcodes.pxd @@ -15,6 +15,9 @@ cdef Logger logger cdef uint16_t FLAGC, FLAGH, FLAGN, FLAGZ cdef uint8_t[512] OPCODE_LENGTHS +cdef uint8_t get_length(int) noexcept nogil +cdef uint8_t[512] OPCODE_MAX_CYCLES +cdef uint8_t get_max_cycles(int) noexcept nogil @cython.locals(v=cython.int, a=cython.int, b=cython.int, pc=cython.ushort) cdef int execute_opcode(cpu.CPU, uint16_t) noexcept nogil diff --git a/pyboy/core/opcodes.py b/pyboy/core/opcodes.py index 743036227..ec55f29ff 100644 --- a/pyboy/core/opcodes.py +++ b/pyboy/core/opcodes.py @@ -11,7 +11,14 @@ FLAGC, FLAGH, FLAGN, FLAGZ = range(4, 8) +def get_length(opcode): + return OPCODE_LENGTHS[opcode] + +def get_max_cycles(opcode): + return OPCODE_MAX_CYCLES[opcode] + def BRK(cpu): + cpu.bail = True cpu.mb.breakpoint_singlestep = 1 cpu.mb.breakpoint_singlestep_latch = 0 # NOTE: We do not increment PC @@ -20,7 +27,7 @@ def BRK(cpu): def NOP_00(cpu): # 00 NOP cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_01(cpu, v): # 01 LD BC,d16 @@ -28,14 +35,14 @@ def LD_01(cpu, v): # 01 LD BC,d16 cpu.C = v & 0x00FF cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_02(cpu): # 02 LD (BC),A cpu.mb.setitem(((cpu.B << 8) + cpu.C), cpu.A) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_03(cpu): # 03 INC BC @@ -46,7 +53,7 @@ def INC_03(cpu): # 03 INC BC cpu.C = t & 0x00FF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_04(cpu): # 04 INC B @@ -60,7 +67,7 @@ def INC_04(cpu): # 04 INC B cpu.B = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_05(cpu): # 05 DEC B @@ -74,14 +81,14 @@ def DEC_05(cpu): # 05 DEC B cpu.B = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_06(cpu, v): # 06 LD B,d8 cpu.B = v cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLCA_07(cpu): # 07 RLCA @@ -94,7 +101,7 @@ def RLCA_07(cpu): # 07 RLCA cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_08(cpu, v): # 08 LD (a16),SP @@ -102,7 +109,7 @@ def LD_08(cpu, v): # 08 LD (a16),SP cpu.mb.setitem(v+1, cpu.SP >> 8) cpu.PC += 3 cpu.PC &= 0xFFFF - return 20 + cpu.cycles += 20 def ADD_09(cpu): # 09 ADD HL,BC @@ -116,14 +123,14 @@ def ADD_09(cpu): # 09 ADD HL,BC cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_0A(cpu): # 0A LD A,(BC) cpu.A = cpu.mb.getitem(((cpu.B << 8) + cpu.C)) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DEC_0B(cpu): # 0B DEC BC @@ -134,7 +141,7 @@ def DEC_0B(cpu): # 0B DEC BC cpu.C = t & 0x00FF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_0C(cpu): # 0C INC C @@ -148,7 +155,7 @@ def INC_0C(cpu): # 0C INC C cpu.C = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_0D(cpu): # 0D DEC C @@ -162,14 +169,14 @@ def DEC_0D(cpu): # 0D DEC C cpu.C = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_0E(cpu, v): # 0E LD C,d8 cpu.C = v cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRCA_0F(cpu): # 0F RRCA @@ -182,7 +189,7 @@ def RRCA_0F(cpu): # 0F RRCA cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def STOP_10(cpu, v): # 10 STOP 0 @@ -191,7 +198,7 @@ def STOP_10(cpu, v): # 10 STOP 0 cpu.mb.setitem(0xFF04, 0) cpu.PC += 2 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_11(cpu, v): # 11 LD DE,d16 @@ -199,14 +206,14 @@ def LD_11(cpu, v): # 11 LD DE,d16 cpu.E = v & 0x00FF cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_12(cpu): # 12 LD (DE),A cpu.mb.setitem(((cpu.D << 8) + cpu.E), cpu.A) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_13(cpu): # 13 INC DE @@ -217,7 +224,7 @@ def INC_13(cpu): # 13 INC DE cpu.E = t & 0x00FF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_14(cpu): # 14 INC D @@ -231,7 +238,7 @@ def INC_14(cpu): # 14 INC D cpu.D = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_15(cpu): # 15 DEC D @@ -245,14 +252,14 @@ def DEC_15(cpu): # 15 DEC D cpu.D = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_16(cpu, v): # 16 LD D,d8 cpu.D = v cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLA_17(cpu): # 17 RLA @@ -265,13 +272,14 @@ def RLA_17(cpu): # 17 RLA cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def JR_18(cpu, v): # 18 JR r8 cpu.PC += 2 + ((v ^ 0x80) - 0x80) cpu.PC &= 0xFFFF - return 12 + cpu.jit_jump = True + cpu.cycles += 12 def ADD_19(cpu): # 19 ADD HL,DE @@ -285,14 +293,14 @@ def ADD_19(cpu): # 19 ADD HL,DE cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_1A(cpu): # 1A LD A,(DE) cpu.A = cpu.mb.getitem(((cpu.D << 8) + cpu.E)) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DEC_1B(cpu): # 1B DEC DE @@ -303,7 +311,7 @@ def DEC_1B(cpu): # 1B DEC DE cpu.E = t & 0x00FF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_1C(cpu): # 1C INC E @@ -317,7 +325,7 @@ def INC_1C(cpu): # 1C INC E cpu.E = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_1D(cpu): # 1D DEC E @@ -331,14 +339,14 @@ def DEC_1D(cpu): # 1D DEC E cpu.E = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_1E(cpu, v): # 1E LD E,d8 cpu.E = v cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRA_1F(cpu): # 1F RRA @@ -351,7 +359,7 @@ def RRA_1F(cpu): # 1F RRA cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def JR_20(cpu, v): # 20 JR NZ,r8 @@ -359,17 +367,18 @@ def JR_20(cpu, v): # 20 JR NZ,r8 if ((cpu.F & (1 << FLAGZ)) == 0): cpu.PC += ((v ^ 0x80) - 0x80) cpu.PC &= 0xFFFF - return 12 + cpu.jit_jump = True + cpu.cycles += 12 else: cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_21(cpu, v): # 21 LD HL,d16 cpu.HL = v cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_22(cpu): # 22 LD (HL+),A @@ -378,7 +387,7 @@ def LD_22(cpu): # 22 LD (HL+),A cpu.HL &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_23(cpu): # 23 INC HL @@ -388,7 +397,7 @@ def INC_23(cpu): # 23 INC HL cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_24(cpu): # 24 INC H @@ -402,7 +411,7 @@ def INC_24(cpu): # 24 INC H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_25(cpu): # 25 DEC H @@ -416,14 +425,14 @@ def DEC_25(cpu): # 25 DEC H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_26(cpu, v): # 26 LD H,d8 cpu.HL = (cpu.HL & 0x00FF) | (v << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DAA_27(cpu): # 27 DAA @@ -446,7 +455,7 @@ def DAA_27(cpu): # 27 DAA cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def JR_28(cpu, v): # 28 JR Z,r8 @@ -454,10 +463,11 @@ def JR_28(cpu, v): # 28 JR Z,r8 if ((cpu.F & (1 << FLAGZ)) != 0): cpu.PC += ((v ^ 0x80) - 0x80) cpu.PC &= 0xFFFF - return 12 + cpu.jit_jump = True + cpu.cycles += 12 else: cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def ADD_29(cpu): # 29 ADD HL,HL @@ -471,7 +481,7 @@ def ADD_29(cpu): # 29 ADD HL,HL cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_2A(cpu): # 2A LD A,(HL+) @@ -480,7 +490,7 @@ def LD_2A(cpu): # 2A LD A,(HL+) cpu.HL &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DEC_2B(cpu): # 2B DEC HL @@ -490,7 +500,7 @@ def DEC_2B(cpu): # 2B DEC HL cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_2C(cpu): # 2C INC L @@ -504,7 +514,7 @@ def INC_2C(cpu): # 2C INC L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_2D(cpu): # 2D DEC L @@ -518,14 +528,14 @@ def DEC_2D(cpu): # 2D DEC L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_2E(cpu, v): # 2E LD L,d8 cpu.HL = (cpu.HL & 0xFF00) | (v & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def CPL_2F(cpu): # 2F CPL @@ -535,7 +545,7 @@ def CPL_2F(cpu): # 2F CPL cpu.F |= flag cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def JR_30(cpu, v): # 30 JR NC,r8 @@ -543,17 +553,18 @@ def JR_30(cpu, v): # 30 JR NC,r8 if ((cpu.F & (1 << FLAGC)) == 0): cpu.PC += ((v ^ 0x80) - 0x80) cpu.PC &= 0xFFFF - return 12 + cpu.jit_jump = True + cpu.cycles += 12 else: cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_31(cpu, v): # 31 LD SP,d16 cpu.SP = v cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_32(cpu): # 32 LD (HL-),A @@ -562,7 +573,7 @@ def LD_32(cpu): # 32 LD (HL-),A cpu.HL &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_33(cpu): # 33 INC SP @@ -572,7 +583,7 @@ def INC_33(cpu): # 33 INC SP cpu.SP = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_34(cpu): # 34 INC (HL) @@ -583,10 +594,11 @@ def INC_34(cpu): # 34 INC (HL) cpu.F &= 0b00010000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 8 def DEC_35(cpu): # 35 DEC (HL) @@ -597,17 +609,19 @@ def DEC_35(cpu): # 35 DEC (HL) cpu.F &= 0b00010000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 8 def LD_36(cpu, v): # 36 LD (HL),d8 + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, v) cpu.PC += 2 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 8 def SCF_37(cpu): # 37 SCF @@ -616,7 +630,7 @@ def SCF_37(cpu): # 37 SCF cpu.F |= flag cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def JR_38(cpu, v): # 38 JR C,r8 @@ -624,10 +638,11 @@ def JR_38(cpu, v): # 38 JR C,r8 if ((cpu.F & (1 << FLAGC)) != 0): cpu.PC += ((v ^ 0x80) - 0x80) cpu.PC &= 0xFFFF - return 12 + cpu.jit_jump = True + cpu.cycles += 12 else: cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def ADD_39(cpu): # 39 ADD HL,SP @@ -641,7 +656,7 @@ def ADD_39(cpu): # 39 ADD HL,SP cpu.HL = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_3A(cpu): # 3A LD A,(HL-) @@ -650,7 +665,7 @@ def LD_3A(cpu): # 3A LD A,(HL-) cpu.HL &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DEC_3B(cpu): # 3B DEC SP @@ -660,7 +675,7 @@ def DEC_3B(cpu): # 3B DEC SP cpu.SP = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def INC_3C(cpu): # 3C INC A @@ -674,7 +689,7 @@ def INC_3C(cpu): # 3C INC A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def DEC_3D(cpu): # 3D DEC A @@ -688,14 +703,14 @@ def DEC_3D(cpu): # 3D DEC A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_3E(cpu, v): # 3E LD A,d8 cpu.A = v cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def CCF_3F(cpu): # 3F CCF @@ -704,453 +719,454 @@ def CCF_3F(cpu): # 3F CCF cpu.F |= flag cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_40(cpu): # 40 LD B,B cpu.B = cpu.B cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_41(cpu): # 41 LD B,C cpu.B = cpu.C cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_42(cpu): # 42 LD B,D cpu.B = cpu.D cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_43(cpu): # 43 LD B,E cpu.B = cpu.E cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_44(cpu): # 44 LD B,H cpu.B = (cpu.HL >> 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_45(cpu): # 45 LD B,L cpu.B = (cpu.HL & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_46(cpu): # 46 LD B,(HL) cpu.B = cpu.mb.getitem(cpu.HL) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_47(cpu): # 47 LD B,A cpu.B = cpu.A cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_48(cpu): # 48 LD C,B cpu.C = cpu.B cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_49(cpu): # 49 LD C,C cpu.C = cpu.C cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_4A(cpu): # 4A LD C,D cpu.C = cpu.D cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_4B(cpu): # 4B LD C,E cpu.C = cpu.E cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_4C(cpu): # 4C LD C,H cpu.C = (cpu.HL >> 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_4D(cpu): # 4D LD C,L cpu.C = (cpu.HL & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_4E(cpu): # 4E LD C,(HL) cpu.C = cpu.mb.getitem(cpu.HL) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_4F(cpu): # 4F LD C,A cpu.C = cpu.A cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_50(cpu): # 50 LD D,B cpu.D = cpu.B cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_51(cpu): # 51 LD D,C cpu.D = cpu.C cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_52(cpu): # 52 LD D,D cpu.D = cpu.D cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_53(cpu): # 53 LD D,E cpu.D = cpu.E cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_54(cpu): # 54 LD D,H cpu.D = (cpu.HL >> 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_55(cpu): # 55 LD D,L cpu.D = (cpu.HL & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_56(cpu): # 56 LD D,(HL) cpu.D = cpu.mb.getitem(cpu.HL) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_57(cpu): # 57 LD D,A cpu.D = cpu.A cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_58(cpu): # 58 LD E,B cpu.E = cpu.B cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_59(cpu): # 59 LD E,C cpu.E = cpu.C cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_5A(cpu): # 5A LD E,D cpu.E = cpu.D cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_5B(cpu): # 5B LD E,E cpu.E = cpu.E cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_5C(cpu): # 5C LD E,H cpu.E = (cpu.HL >> 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_5D(cpu): # 5D LD E,L cpu.E = (cpu.HL & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_5E(cpu): # 5E LD E,(HL) cpu.E = cpu.mb.getitem(cpu.HL) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_5F(cpu): # 5F LD E,A cpu.E = cpu.A cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_60(cpu): # 60 LD H,B cpu.HL = (cpu.HL & 0x00FF) | (cpu.B << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_61(cpu): # 61 LD H,C cpu.HL = (cpu.HL & 0x00FF) | (cpu.C << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_62(cpu): # 62 LD H,D cpu.HL = (cpu.HL & 0x00FF) | (cpu.D << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_63(cpu): # 63 LD H,E cpu.HL = (cpu.HL & 0x00FF) | (cpu.E << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_64(cpu): # 64 LD H,H cpu.HL = (cpu.HL & 0x00FF) | ((cpu.HL >> 8) << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_65(cpu): # 65 LD H,L cpu.HL = (cpu.HL & 0x00FF) | ((cpu.HL & 0xFF) << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_66(cpu): # 66 LD H,(HL) cpu.HL = (cpu.HL & 0x00FF) | (cpu.mb.getitem(cpu.HL) << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_67(cpu): # 67 LD H,A cpu.HL = (cpu.HL & 0x00FF) | (cpu.A << 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_68(cpu): # 68 LD L,B cpu.HL = (cpu.HL & 0xFF00) | (cpu.B & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_69(cpu): # 69 LD L,C cpu.HL = (cpu.HL & 0xFF00) | (cpu.C & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_6A(cpu): # 6A LD L,D cpu.HL = (cpu.HL & 0xFF00) | (cpu.D & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_6B(cpu): # 6B LD L,E cpu.HL = (cpu.HL & 0xFF00) | (cpu.E & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_6C(cpu): # 6C LD L,H cpu.HL = (cpu.HL & 0xFF00) | ((cpu.HL >> 8) & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_6D(cpu): # 6D LD L,L cpu.HL = (cpu.HL & 0xFF00) | ((cpu.HL & 0xFF) & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_6E(cpu): # 6E LD L,(HL) cpu.HL = (cpu.HL & 0xFF00) | (cpu.mb.getitem(cpu.HL) & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_6F(cpu): # 6F LD L,A cpu.HL = (cpu.HL & 0xFF00) | (cpu.A & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_70(cpu): # 70 LD (HL),B cpu.mb.setitem(cpu.HL, cpu.B) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_71(cpu): # 71 LD (HL),C cpu.mb.setitem(cpu.HL, cpu.C) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_72(cpu): # 72 LD (HL),D cpu.mb.setitem(cpu.HL, cpu.D) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_73(cpu): # 73 LD (HL),E cpu.mb.setitem(cpu.HL, cpu.E) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_74(cpu): # 74 LD (HL),H cpu.mb.setitem(cpu.HL, (cpu.HL >> 8)) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_75(cpu): # 75 LD (HL),L cpu.mb.setitem(cpu.HL, (cpu.HL & 0xFF)) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def HALT_76(cpu): # 76 HALT cpu.halted = True - return 4 + cpu.bail = True + cpu.cycles += 4 def LD_77(cpu): # 77 LD (HL),A cpu.mb.setitem(cpu.HL, cpu.A) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_78(cpu): # 78 LD A,B cpu.A = cpu.B cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_79(cpu): # 79 LD A,C cpu.A = cpu.C cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_7A(cpu): # 7A LD A,D cpu.A = cpu.D cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_7B(cpu): # 7B LD A,E cpu.A = cpu.E cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_7C(cpu): # 7C LD A,H cpu.A = (cpu.HL >> 8) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_7D(cpu): # 7D LD A,L cpu.A = (cpu.HL & 0xFF) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def LD_7E(cpu): # 7E LD A,(HL) cpu.A = cpu.mb.getitem(cpu.HL) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_7F(cpu): # 7F LD A,A cpu.A = cpu.A cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_80(cpu): # 80 ADD A,B @@ -1165,7 +1181,7 @@ def ADD_80(cpu): # 80 ADD A,B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_81(cpu): # 81 ADD A,C @@ -1180,7 +1196,7 @@ def ADD_81(cpu): # 81 ADD A,C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_82(cpu): # 82 ADD A,D @@ -1195,7 +1211,7 @@ def ADD_82(cpu): # 82 ADD A,D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_83(cpu): # 83 ADD A,E @@ -1210,7 +1226,7 @@ def ADD_83(cpu): # 83 ADD A,E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_84(cpu): # 84 ADD A,H @@ -1225,7 +1241,7 @@ def ADD_84(cpu): # 84 ADD A,H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_85(cpu): # 85 ADD A,L @@ -1240,7 +1256,7 @@ def ADD_85(cpu): # 85 ADD A,L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADD_86(cpu): # 86 ADD A,(HL) @@ -1255,7 +1271,7 @@ def ADD_86(cpu): # 86 ADD A,(HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def ADD_87(cpu): # 87 ADD A,A @@ -1270,7 +1286,7 @@ def ADD_87(cpu): # 87 ADD A,A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_88(cpu): # 88 ADC A,B @@ -1285,7 +1301,7 @@ def ADC_88(cpu): # 88 ADC A,B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_89(cpu): # 89 ADC A,C @@ -1300,7 +1316,7 @@ def ADC_89(cpu): # 89 ADC A,C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_8A(cpu): # 8A ADC A,D @@ -1315,7 +1331,7 @@ def ADC_8A(cpu): # 8A ADC A,D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_8B(cpu): # 8B ADC A,E @@ -1330,7 +1346,7 @@ def ADC_8B(cpu): # 8B ADC A,E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_8C(cpu): # 8C ADC A,H @@ -1345,7 +1361,7 @@ def ADC_8C(cpu): # 8C ADC A,H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_8D(cpu): # 8D ADC A,L @@ -1360,7 +1376,7 @@ def ADC_8D(cpu): # 8D ADC A,L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def ADC_8E(cpu): # 8E ADC A,(HL) @@ -1375,7 +1391,7 @@ def ADC_8E(cpu): # 8E ADC A,(HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def ADC_8F(cpu): # 8F ADC A,A @@ -1390,7 +1406,7 @@ def ADC_8F(cpu): # 8F ADC A,A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_90(cpu): # 90 SUB B @@ -1405,7 +1421,7 @@ def SUB_90(cpu): # 90 SUB B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_91(cpu): # 91 SUB C @@ -1420,7 +1436,7 @@ def SUB_91(cpu): # 91 SUB C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_92(cpu): # 92 SUB D @@ -1435,7 +1451,7 @@ def SUB_92(cpu): # 92 SUB D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_93(cpu): # 93 SUB E @@ -1450,7 +1466,7 @@ def SUB_93(cpu): # 93 SUB E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_94(cpu): # 94 SUB H @@ -1465,7 +1481,7 @@ def SUB_94(cpu): # 94 SUB H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_95(cpu): # 95 SUB L @@ -1480,7 +1496,7 @@ def SUB_95(cpu): # 95 SUB L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SUB_96(cpu): # 96 SUB (HL) @@ -1495,7 +1511,7 @@ def SUB_96(cpu): # 96 SUB (HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SUB_97(cpu): # 97 SUB A @@ -1510,7 +1526,7 @@ def SUB_97(cpu): # 97 SUB A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_98(cpu): # 98 SBC A,B @@ -1525,7 +1541,7 @@ def SBC_98(cpu): # 98 SBC A,B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_99(cpu): # 99 SBC A,C @@ -1540,7 +1556,7 @@ def SBC_99(cpu): # 99 SBC A,C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_9A(cpu): # 9A SBC A,D @@ -1555,7 +1571,7 @@ def SBC_9A(cpu): # 9A SBC A,D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_9B(cpu): # 9B SBC A,E @@ -1570,7 +1586,7 @@ def SBC_9B(cpu): # 9B SBC A,E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_9C(cpu): # 9C SBC A,H @@ -1585,7 +1601,7 @@ def SBC_9C(cpu): # 9C SBC A,H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_9D(cpu): # 9D SBC A,L @@ -1600,7 +1616,7 @@ def SBC_9D(cpu): # 9D SBC A,L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def SBC_9E(cpu): # 9E SBC A,(HL) @@ -1615,7 +1631,7 @@ def SBC_9E(cpu): # 9E SBC A,(HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SBC_9F(cpu): # 9F SBC A,A @@ -1630,7 +1646,7 @@ def SBC_9F(cpu): # 9F SBC A,A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A0(cpu): # A0 AND B @@ -1643,7 +1659,7 @@ def AND_A0(cpu): # A0 AND B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A1(cpu): # A1 AND C @@ -1656,7 +1672,7 @@ def AND_A1(cpu): # A1 AND C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A2(cpu): # A2 AND D @@ -1669,7 +1685,7 @@ def AND_A2(cpu): # A2 AND D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A3(cpu): # A3 AND E @@ -1682,7 +1698,7 @@ def AND_A3(cpu): # A3 AND E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A4(cpu): # A4 AND H @@ -1695,7 +1711,7 @@ def AND_A4(cpu): # A4 AND H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A5(cpu): # A5 AND L @@ -1708,7 +1724,7 @@ def AND_A5(cpu): # A5 AND L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def AND_A6(cpu): # A6 AND (HL) @@ -1721,7 +1737,7 @@ def AND_A6(cpu): # A6 AND (HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def AND_A7(cpu): # A7 AND A @@ -1734,7 +1750,7 @@ def AND_A7(cpu): # A7 AND A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_A8(cpu): # A8 XOR B @@ -1747,7 +1763,7 @@ def XOR_A8(cpu): # A8 XOR B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_A9(cpu): # A9 XOR C @@ -1760,7 +1776,7 @@ def XOR_A9(cpu): # A9 XOR C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_AA(cpu): # AA XOR D @@ -1773,7 +1789,7 @@ def XOR_AA(cpu): # AA XOR D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_AB(cpu): # AB XOR E @@ -1786,7 +1802,7 @@ def XOR_AB(cpu): # AB XOR E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_AC(cpu): # AC XOR H @@ -1799,7 +1815,7 @@ def XOR_AC(cpu): # AC XOR H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_AD(cpu): # AD XOR L @@ -1812,7 +1828,7 @@ def XOR_AD(cpu): # AD XOR L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def XOR_AE(cpu): # AE XOR (HL) @@ -1825,7 +1841,7 @@ def XOR_AE(cpu): # AE XOR (HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def XOR_AF(cpu): # AF XOR A @@ -1838,7 +1854,7 @@ def XOR_AF(cpu): # AF XOR A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B0(cpu): # B0 OR B @@ -1851,7 +1867,7 @@ def OR_B0(cpu): # B0 OR B cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B1(cpu): # B1 OR C @@ -1864,7 +1880,7 @@ def OR_B1(cpu): # B1 OR C cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B2(cpu): # B2 OR D @@ -1877,7 +1893,7 @@ def OR_B2(cpu): # B2 OR D cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B3(cpu): # B3 OR E @@ -1890,7 +1906,7 @@ def OR_B3(cpu): # B3 OR E cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B4(cpu): # B4 OR H @@ -1903,7 +1919,7 @@ def OR_B4(cpu): # B4 OR H cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B5(cpu): # B5 OR L @@ -1916,7 +1932,7 @@ def OR_B5(cpu): # B5 OR L cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def OR_B6(cpu): # B6 OR (HL) @@ -1929,7 +1945,7 @@ def OR_B6(cpu): # B6 OR (HL) cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def OR_B7(cpu): # B7 OR A @@ -1942,7 +1958,7 @@ def OR_B7(cpu): # B7 OR A cpu.A = t cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_B8(cpu): # B8 CP B @@ -1956,7 +1972,7 @@ def CP_B8(cpu): # B8 CP B t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_B9(cpu): # B9 CP C @@ -1970,7 +1986,7 @@ def CP_B9(cpu): # B9 CP C t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_BA(cpu): # BA CP D @@ -1984,7 +2000,7 @@ def CP_BA(cpu): # BA CP D t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_BB(cpu): # BB CP E @@ -1998,7 +2014,7 @@ def CP_BB(cpu): # BB CP E t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_BC(cpu): # BC CP H @@ -2012,7 +2028,7 @@ def CP_BC(cpu): # BC CP H t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_BD(cpu): # BD CP L @@ -2026,7 +2042,7 @@ def CP_BD(cpu): # BD CP L t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_BE(cpu): # BE CP (HL) @@ -2040,7 +2056,7 @@ def CP_BE(cpu): # BE CP (HL) t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def CP_BF(cpu): # BF CP A @@ -2054,7 +2070,7 @@ def CP_BF(cpu): # BF CP A t &= 0xFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def RET_C0(cpu): # C0 RET NZ @@ -2063,11 +2079,12 @@ def RET_C0(cpu): # C0 RET NZ cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 20 + cpu.jit_jump = True + cpu.cycles += 20 else: cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def POP_C1(cpu): # C1 POP BC @@ -2077,22 +2094,23 @@ def POP_C1(cpu): # C1 POP BC cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def JP_C2(cpu, v): # C2 JP NZ,a16 if ((cpu.F & (1 << FLAGZ)) == 0): cpu.PC = v - return 16 + cpu.jit_jump = True + cpu.cycles += 16 else: cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def JP_C3(cpu, v): # C3 JP a16 cpu.PC = v - return 16 + cpu.cycles += 16 def CALL_C4(cpu, v): # C4 CALL NZ,a16 @@ -2104,9 +2122,10 @@ def CALL_C4(cpu, v): # C4 CALL NZ,a16 cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = v - return 24 + cpu.jit_jump = True + cpu.cycles += 24 else: - return 12 + cpu.cycles += 12 def PUSH_C5(cpu): # C5 PUSH BC @@ -2116,7 +2135,7 @@ def PUSH_C5(cpu): # C5 PUSH BC cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 16 def ADD_C6(cpu, v): # C6 ADD A,d8 @@ -2131,7 +2150,7 @@ def ADD_C6(cpu, v): # C6 ADD A,d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_C7(cpu): # C7 RST 00H @@ -2142,7 +2161,8 @@ def RST_C7(cpu): # C7 RST 00H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 0 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def RET_C8(cpu): # C8 RET Z @@ -2151,11 +2171,12 @@ def RET_C8(cpu): # C8 RET Z cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 20 + cpu.jit_jump = True + cpu.cycles += 20 else: cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RET_C9(cpu): # C9 RET @@ -2163,24 +2184,26 @@ def RET_C9(cpu): # C9 RET cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def JP_CA(cpu, v): # CA JP Z,a16 if ((cpu.F & (1 << FLAGZ)) != 0): cpu.PC = v - return 16 + cpu.jit_jump = True + cpu.cycles += 16 else: cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def PREFIX_CB(cpu): # CB PREFIX CB logger.critical('CB cannot be called!') cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CALL_CC(cpu, v): # CC CALL Z,a16 @@ -2192,9 +2215,10 @@ def CALL_CC(cpu, v): # CC CALL Z,a16 cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = v - return 24 + cpu.jit_jump = True + cpu.cycles += 24 else: - return 12 + cpu.cycles += 12 def CALL_CD(cpu, v): # CD CALL a16 @@ -2205,7 +2229,8 @@ def CALL_CD(cpu, v): # CD CALL a16 cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = v - return 24 + cpu.jit_jump = True + cpu.cycles += 24 def ADC_CE(cpu, v): # CE ADC A,d8 @@ -2220,7 +2245,7 @@ def ADC_CE(cpu, v): # CE ADC A,d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_CF(cpu): # CF RST 08H @@ -2231,7 +2256,8 @@ def RST_CF(cpu): # CF RST 08H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 8 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def RET_D0(cpu): # D0 RET NC @@ -2240,11 +2266,12 @@ def RET_D0(cpu): # D0 RET NC cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 20 + cpu.jit_jump = True + cpu.cycles += 20 else: cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def POP_D1(cpu): # D1 POP DE @@ -2254,17 +2281,18 @@ def POP_D1(cpu): # D1 POP DE cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def JP_D2(cpu, v): # D2 JP NC,a16 if ((cpu.F & (1 << FLAGC)) == 0): cpu.PC = v - return 16 + cpu.jit_jump = True + cpu.cycles += 16 else: cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def CALL_D4(cpu, v): # D4 CALL NC,a16 @@ -2276,9 +2304,10 @@ def CALL_D4(cpu, v): # D4 CALL NC,a16 cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = v - return 24 + cpu.jit_jump = True + cpu.cycles += 24 else: - return 12 + cpu.cycles += 12 def PUSH_D5(cpu): # D5 PUSH DE @@ -2288,7 +2317,7 @@ def PUSH_D5(cpu): # D5 PUSH DE cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 16 def SUB_D6(cpu, v): # D6 SUB d8 @@ -2303,7 +2332,7 @@ def SUB_D6(cpu, v): # D6 SUB d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_D7(cpu): # D7 RST 10H @@ -2314,7 +2343,8 @@ def RST_D7(cpu): # D7 RST 10H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 16 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def RET_D8(cpu): # D8 RET C @@ -2323,30 +2353,34 @@ def RET_D8(cpu): # D8 RET C cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 20 + cpu.jit_jump = True + cpu.cycles += 20 else: cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RETI_D9(cpu): # D9 RETI cpu.interrupt_master_enable = True + cpu.bail = (cpu.interrupts_flag_register & 0b11111) & (cpu.interrupts_enabled_register & 0b11111) cpu.PC = cpu.mb.getitem((cpu.SP + 1) & 0xFFFF) << 8 # High cpu.PC |= cpu.mb.getitem(cpu.SP) # Low cpu.SP += 2 cpu.SP &= 0xFFFF - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def JP_DA(cpu, v): # DA JP C,a16 if ((cpu.F & (1 << FLAGC)) != 0): cpu.PC = v - return 16 + cpu.jit_jump = True + cpu.cycles += 16 else: cpu.PC += 3 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def CALL_DC(cpu, v): # DC CALL C,a16 @@ -2358,9 +2392,10 @@ def CALL_DC(cpu, v): # DC CALL C,a16 cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = v - return 24 + cpu.jit_jump = True + cpu.cycles += 24 else: - return 12 + cpu.cycles += 12 def SBC_DE(cpu, v): # DE SBC A,d8 @@ -2375,7 +2410,7 @@ def SBC_DE(cpu, v): # DE SBC A,d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_DF(cpu): # DF RST 18H @@ -2386,14 +2421,16 @@ def RST_DF(cpu): # DF RST 18H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 24 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def LDH_E0(cpu, v): # E0 LDH (a8),A + cpu.cycles += 4 cpu.mb.setitem(v + 0xFF00, cpu.A) cpu.PC += 2 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 8 def POP_E1(cpu): # E1 POP HL @@ -2402,14 +2439,14 @@ def POP_E1(cpu): # E1 POP HL cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_E2(cpu): # E2 LD (C),A cpu.mb.setitem(0xFF00 + cpu.C, cpu.A) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def PUSH_E5(cpu): # E5 PUSH HL @@ -2419,7 +2456,7 @@ def PUSH_E5(cpu): # E5 PUSH HL cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 16 def AND_E6(cpu, v): # E6 AND d8 @@ -2432,7 +2469,7 @@ def AND_E6(cpu, v): # E6 AND d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_E7(cpu): # E7 RST 20H @@ -2443,7 +2480,8 @@ def RST_E7(cpu): # E7 RST 20H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 32 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def ADD_E8(cpu, v): # E8 ADD SP,r8 @@ -2457,19 +2495,20 @@ def ADD_E8(cpu, v): # E8 ADD SP,r8 cpu.SP = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 16 def JP_E9(cpu): # E9 JP (HL) cpu.PC = cpu.HL - return 4 + cpu.cycles += 4 def LD_EA(cpu, v): # EA LD (a16),A + cpu.cycles += 8 cpu.mb.setitem(v, cpu.A) cpu.PC += 3 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def XOR_EE(cpu, v): # EE XOR d8 @@ -2482,7 +2521,7 @@ def XOR_EE(cpu, v): # EE XOR d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_EF(cpu): # EF RST 28H @@ -2493,14 +2532,16 @@ def RST_EF(cpu): # EF RST 28H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 40 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def LDH_F0(cpu, v): # F0 LDH A,(a8) + cpu.cycles += 4 cpu.A = cpu.mb.getitem(v + 0xFF00) cpu.PC += 2 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 8 def POP_F1(cpu): # F1 POP AF @@ -2510,21 +2551,21 @@ def POP_F1(cpu): # F1 POP AF cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_F2(cpu): # F2 LD A,(C) cpu.A = cpu.mb.getitem(0xFF00 + cpu.C) cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def DI_F3(cpu): # F3 DI cpu.interrupt_master_enable = False cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def PUSH_F5(cpu): # F5 PUSH AF @@ -2534,7 +2575,7 @@ def PUSH_F5(cpu): # F5 PUSH AF cpu.SP &= 0xFFFF cpu.PC += 1 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 16 def OR_F6(cpu, v): # F6 OR d8 @@ -2547,7 +2588,7 @@ def OR_F6(cpu, v): # F6 OR d8 cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_F7(cpu): # F7 RST 30H @@ -2558,7 +2599,8 @@ def RST_F7(cpu): # F7 RST 30H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 48 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def LD_F8(cpu, v): # F8 LD HL,SP+r8 @@ -2572,28 +2614,30 @@ def LD_F8(cpu, v): # F8 LD HL,SP+r8 cpu.HL &= 0xFFFF cpu.PC += 2 cpu.PC &= 0xFFFF - return 12 + cpu.cycles += 12 def LD_F9(cpu): # F9 LD SP,HL cpu.SP = cpu.HL cpu.PC += 1 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def LD_FA(cpu, v): # FA LD A,(a16) + cpu.cycles += 8 cpu.A = cpu.mb.getitem(v) cpu.PC += 3 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def EI_FB(cpu): # FB EI cpu.interrupt_master_enable = True + cpu.bail = (cpu.interrupts_flag_register & 0b11111) & (cpu.interrupts_enabled_register & 0b11111) cpu.PC += 1 cpu.PC &= 0xFFFF - return 4 + cpu.cycles += 4 def CP_FE(cpu, v): # FE CP d8 @@ -2607,7 +2651,7 @@ def CP_FE(cpu, v): # FE CP d8 t &= 0xFF cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RST_FF(cpu): # FF RST 38H @@ -2618,7 +2662,8 @@ def RST_FF(cpu): # FF RST 38H cpu.SP -= 2 cpu.SP &= 0xFFFF cpu.PC = 56 - return 16 + cpu.jit_jump = True + cpu.cycles += 16 def RLC_100(cpu): # 100 RLC B @@ -2632,7 +2677,7 @@ def RLC_100(cpu): # 100 RLC B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_101(cpu): # 101 RLC C @@ -2646,7 +2691,7 @@ def RLC_101(cpu): # 101 RLC C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_102(cpu): # 102 RLC D @@ -2660,7 +2705,7 @@ def RLC_102(cpu): # 102 RLC D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_103(cpu): # 103 RLC E @@ -2674,7 +2719,7 @@ def RLC_103(cpu): # 103 RLC E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_104(cpu): # 104 RLC H @@ -2688,7 +2733,7 @@ def RLC_104(cpu): # 104 RLC H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_105(cpu): # 105 RLC L @@ -2702,10 +2747,11 @@ def RLC_105(cpu): # 105 RLC L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RLC_106(cpu): # 106 RLC (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) << 1) + (cpu.mb.getitem(cpu.HL) >> 7) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -2713,10 +2759,11 @@ def RLC_106(cpu): # 106 RLC (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RLC_107(cpu): # 107 RLC A @@ -2730,7 +2777,7 @@ def RLC_107(cpu): # 107 RLC A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_108(cpu): # 108 RRC B @@ -2744,7 +2791,7 @@ def RRC_108(cpu): # 108 RRC B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_109(cpu): # 109 RRC C @@ -2758,7 +2805,7 @@ def RRC_109(cpu): # 109 RRC C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_10A(cpu): # 10A RRC D @@ -2772,7 +2819,7 @@ def RRC_10A(cpu): # 10A RRC D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_10B(cpu): # 10B RRC E @@ -2786,7 +2833,7 @@ def RRC_10B(cpu): # 10B RRC E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_10C(cpu): # 10C RRC H @@ -2800,7 +2847,7 @@ def RRC_10C(cpu): # 10C RRC H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_10D(cpu): # 10D RRC L @@ -2814,10 +2861,11 @@ def RRC_10D(cpu): # 10D RRC L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RRC_10E(cpu): # 10E RRC (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) >> 1) + ((cpu.mb.getitem(cpu.HL) & 1) << 7) + ((cpu.mb.getitem(cpu.HL) & 1) << 8) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -2825,10 +2873,11 @@ def RRC_10E(cpu): # 10E RRC (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RRC_10F(cpu): # 10F RRC A @@ -2842,7 +2891,7 @@ def RRC_10F(cpu): # 10F RRC A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_110(cpu): # 110 RL B @@ -2856,7 +2905,7 @@ def RL_110(cpu): # 110 RL B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_111(cpu): # 111 RL C @@ -2870,7 +2919,7 @@ def RL_111(cpu): # 111 RL C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_112(cpu): # 112 RL D @@ -2884,7 +2933,7 @@ def RL_112(cpu): # 112 RL D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_113(cpu): # 113 RL E @@ -2898,7 +2947,7 @@ def RL_113(cpu): # 113 RL E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_114(cpu): # 114 RL H @@ -2912,7 +2961,7 @@ def RL_114(cpu): # 114 RL H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_115(cpu): # 115 RL L @@ -2926,10 +2975,11 @@ def RL_115(cpu): # 115 RL L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RL_116(cpu): # 116 RL (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) << 1) + ((cpu.F & (1 << FLAGC)) != 0) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -2937,10 +2987,11 @@ def RL_116(cpu): # 116 RL (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RL_117(cpu): # 117 RL A @@ -2954,7 +3005,7 @@ def RL_117(cpu): # 117 RL A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_118(cpu): # 118 RR B @@ -2968,7 +3019,7 @@ def RR_118(cpu): # 118 RR B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_119(cpu): # 119 RR C @@ -2982,7 +3033,7 @@ def RR_119(cpu): # 119 RR C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_11A(cpu): # 11A RR D @@ -2996,7 +3047,7 @@ def RR_11A(cpu): # 11A RR D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_11B(cpu): # 11B RR E @@ -3010,7 +3061,7 @@ def RR_11B(cpu): # 11B RR E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_11C(cpu): # 11C RR H @@ -3024,7 +3075,7 @@ def RR_11C(cpu): # 11C RR H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_11D(cpu): # 11D RR L @@ -3038,10 +3089,11 @@ def RR_11D(cpu): # 11D RR L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RR_11E(cpu): # 11E RR (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) >> 1) + (((cpu.F & (1 << FLAGC)) != 0) << 7) + ((cpu.mb.getitem(cpu.HL) & 1) << 8) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3049,10 +3101,11 @@ def RR_11E(cpu): # 11E RR (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RR_11F(cpu): # 11F RR A @@ -3066,7 +3119,7 @@ def RR_11F(cpu): # 11F RR A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_120(cpu): # 120 SLA B @@ -3080,7 +3133,7 @@ def SLA_120(cpu): # 120 SLA B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_121(cpu): # 121 SLA C @@ -3094,7 +3147,7 @@ def SLA_121(cpu): # 121 SLA C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_122(cpu): # 122 SLA D @@ -3108,7 +3161,7 @@ def SLA_122(cpu): # 122 SLA D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_123(cpu): # 123 SLA E @@ -3122,7 +3175,7 @@ def SLA_123(cpu): # 123 SLA E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_124(cpu): # 124 SLA H @@ -3136,7 +3189,7 @@ def SLA_124(cpu): # 124 SLA H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_125(cpu): # 125 SLA L @@ -3150,10 +3203,11 @@ def SLA_125(cpu): # 125 SLA L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SLA_126(cpu): # 126 SLA (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) << 1) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3161,10 +3215,11 @@ def SLA_126(cpu): # 126 SLA (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SLA_127(cpu): # 127 SLA A @@ -3178,7 +3233,7 @@ def SLA_127(cpu): # 127 SLA A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_128(cpu): # 128 SRA B @@ -3192,7 +3247,7 @@ def SRA_128(cpu): # 128 SRA B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_129(cpu): # 129 SRA C @@ -3206,7 +3261,7 @@ def SRA_129(cpu): # 129 SRA C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_12A(cpu): # 12A SRA D @@ -3220,7 +3275,7 @@ def SRA_12A(cpu): # 12A SRA D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_12B(cpu): # 12B SRA E @@ -3234,7 +3289,7 @@ def SRA_12B(cpu): # 12B SRA E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_12C(cpu): # 12C SRA H @@ -3248,7 +3303,7 @@ def SRA_12C(cpu): # 12C SRA H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_12D(cpu): # 12D SRA L @@ -3262,10 +3317,11 @@ def SRA_12D(cpu): # 12D SRA L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRA_12E(cpu): # 12E SRA (HL) + cpu.cycles += 4 t = ((cpu.mb.getitem(cpu.HL) >> 1) | (cpu.mb.getitem(cpu.HL) & 0x80)) + ((cpu.mb.getitem(cpu.HL) & 1) << 8) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3273,10 +3329,11 @@ def SRA_12E(cpu): # 12E SRA (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SRA_12F(cpu): # 12F SRA A @@ -3290,7 +3347,7 @@ def SRA_12F(cpu): # 12F SRA A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_130(cpu): # 130 SWAP B @@ -3303,7 +3360,7 @@ def SWAP_130(cpu): # 130 SWAP B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_131(cpu): # 131 SWAP C @@ -3316,7 +3373,7 @@ def SWAP_131(cpu): # 131 SWAP C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_132(cpu): # 132 SWAP D @@ -3329,7 +3386,7 @@ def SWAP_132(cpu): # 132 SWAP D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_133(cpu): # 133 SWAP E @@ -3342,7 +3399,7 @@ def SWAP_133(cpu): # 133 SWAP E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_134(cpu): # 134 SWAP H @@ -3355,7 +3412,7 @@ def SWAP_134(cpu): # 134 SWAP H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_135(cpu): # 135 SWAP L @@ -3368,20 +3425,22 @@ def SWAP_135(cpu): # 135 SWAP L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SWAP_136(cpu): # 136 SWAP (HL) + cpu.cycles += 4 t = ((cpu.mb.getitem(cpu.HL) & 0xF0) >> 4) | ((cpu.mb.getitem(cpu.HL) & 0x0F) << 4) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SWAP_137(cpu): # 137 SWAP A @@ -3394,7 +3453,7 @@ def SWAP_137(cpu): # 137 SWAP A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_138(cpu): # 138 SRL B @@ -3408,7 +3467,7 @@ def SRL_138(cpu): # 138 SRL B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_139(cpu): # 139 SRL C @@ -3422,7 +3481,7 @@ def SRL_139(cpu): # 139 SRL C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_13A(cpu): # 13A SRL D @@ -3436,7 +3495,7 @@ def SRL_13A(cpu): # 13A SRL D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_13B(cpu): # 13B SRL E @@ -3450,7 +3509,7 @@ def SRL_13B(cpu): # 13B SRL E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_13C(cpu): # 13C SRL H @@ -3464,7 +3523,7 @@ def SRL_13C(cpu): # 13C SRL H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_13D(cpu): # 13D SRL L @@ -3478,10 +3537,11 @@ def SRL_13D(cpu): # 13D SRL L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SRL_13E(cpu): # 13E SRL (HL) + cpu.cycles += 4 t = (cpu.mb.getitem(cpu.HL) >> 1) + ((cpu.mb.getitem(cpu.HL) & 1) << 8) flag = 0b00000000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3489,10 +3549,11 @@ def SRL_13E(cpu): # 13E SRL (HL) cpu.F &= 0b00000000 cpu.F |= flag t &= 0xFF + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SRL_13F(cpu): # 13F SRL A @@ -3506,7 +3567,7 @@ def SRL_13F(cpu): # 13F SRL A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_140(cpu): # 140 BIT 0,B @@ -3517,7 +3578,7 @@ def BIT_140(cpu): # 140 BIT 0,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_141(cpu): # 141 BIT 0,C @@ -3528,7 +3589,7 @@ def BIT_141(cpu): # 141 BIT 0,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_142(cpu): # 142 BIT 0,D @@ -3539,7 +3600,7 @@ def BIT_142(cpu): # 142 BIT 0,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_143(cpu): # 143 BIT 0,E @@ -3550,7 +3611,7 @@ def BIT_143(cpu): # 143 BIT 0,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_144(cpu): # 144 BIT 0,H @@ -3561,7 +3622,7 @@ def BIT_144(cpu): # 144 BIT 0,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_145(cpu): # 145 BIT 0,L @@ -3572,10 +3633,11 @@ def BIT_145(cpu): # 145 BIT 0,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_146(cpu): # 146 BIT 0,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 0) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3583,7 +3645,7 @@ def BIT_146(cpu): # 146 BIT 0,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_147(cpu): # 147 BIT 0,A @@ -3594,7 +3656,7 @@ def BIT_147(cpu): # 147 BIT 0,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_148(cpu): # 148 BIT 1,B @@ -3605,7 +3667,7 @@ def BIT_148(cpu): # 148 BIT 1,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_149(cpu): # 149 BIT 1,C @@ -3616,7 +3678,7 @@ def BIT_149(cpu): # 149 BIT 1,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_14A(cpu): # 14A BIT 1,D @@ -3627,7 +3689,7 @@ def BIT_14A(cpu): # 14A BIT 1,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_14B(cpu): # 14B BIT 1,E @@ -3638,7 +3700,7 @@ def BIT_14B(cpu): # 14B BIT 1,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_14C(cpu): # 14C BIT 1,H @@ -3649,7 +3711,7 @@ def BIT_14C(cpu): # 14C BIT 1,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_14D(cpu): # 14D BIT 1,L @@ -3660,10 +3722,11 @@ def BIT_14D(cpu): # 14D BIT 1,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_14E(cpu): # 14E BIT 1,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 1) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3671,7 +3734,7 @@ def BIT_14E(cpu): # 14E BIT 1,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_14F(cpu): # 14F BIT 1,A @@ -3682,7 +3745,7 @@ def BIT_14F(cpu): # 14F BIT 1,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_150(cpu): # 150 BIT 2,B @@ -3693,7 +3756,7 @@ def BIT_150(cpu): # 150 BIT 2,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_151(cpu): # 151 BIT 2,C @@ -3704,7 +3767,7 @@ def BIT_151(cpu): # 151 BIT 2,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_152(cpu): # 152 BIT 2,D @@ -3715,7 +3778,7 @@ def BIT_152(cpu): # 152 BIT 2,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_153(cpu): # 153 BIT 2,E @@ -3726,7 +3789,7 @@ def BIT_153(cpu): # 153 BIT 2,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_154(cpu): # 154 BIT 2,H @@ -3737,7 +3800,7 @@ def BIT_154(cpu): # 154 BIT 2,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_155(cpu): # 155 BIT 2,L @@ -3748,10 +3811,11 @@ def BIT_155(cpu): # 155 BIT 2,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_156(cpu): # 156 BIT 2,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 2) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3759,7 +3823,7 @@ def BIT_156(cpu): # 156 BIT 2,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_157(cpu): # 157 BIT 2,A @@ -3770,7 +3834,7 @@ def BIT_157(cpu): # 157 BIT 2,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_158(cpu): # 158 BIT 3,B @@ -3781,7 +3845,7 @@ def BIT_158(cpu): # 158 BIT 3,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_159(cpu): # 159 BIT 3,C @@ -3792,7 +3856,7 @@ def BIT_159(cpu): # 159 BIT 3,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_15A(cpu): # 15A BIT 3,D @@ -3803,7 +3867,7 @@ def BIT_15A(cpu): # 15A BIT 3,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_15B(cpu): # 15B BIT 3,E @@ -3814,7 +3878,7 @@ def BIT_15B(cpu): # 15B BIT 3,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_15C(cpu): # 15C BIT 3,H @@ -3825,7 +3889,7 @@ def BIT_15C(cpu): # 15C BIT 3,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_15D(cpu): # 15D BIT 3,L @@ -3836,10 +3900,11 @@ def BIT_15D(cpu): # 15D BIT 3,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_15E(cpu): # 15E BIT 3,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 3) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3847,7 +3912,7 @@ def BIT_15E(cpu): # 15E BIT 3,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_15F(cpu): # 15F BIT 3,A @@ -3858,7 +3923,7 @@ def BIT_15F(cpu): # 15F BIT 3,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_160(cpu): # 160 BIT 4,B @@ -3869,7 +3934,7 @@ def BIT_160(cpu): # 160 BIT 4,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_161(cpu): # 161 BIT 4,C @@ -3880,7 +3945,7 @@ def BIT_161(cpu): # 161 BIT 4,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_162(cpu): # 162 BIT 4,D @@ -3891,7 +3956,7 @@ def BIT_162(cpu): # 162 BIT 4,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_163(cpu): # 163 BIT 4,E @@ -3902,7 +3967,7 @@ def BIT_163(cpu): # 163 BIT 4,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_164(cpu): # 164 BIT 4,H @@ -3913,7 +3978,7 @@ def BIT_164(cpu): # 164 BIT 4,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_165(cpu): # 165 BIT 4,L @@ -3924,10 +3989,11 @@ def BIT_165(cpu): # 165 BIT 4,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_166(cpu): # 166 BIT 4,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 4) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -3935,7 +4001,7 @@ def BIT_166(cpu): # 166 BIT 4,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_167(cpu): # 167 BIT 4,A @@ -3946,7 +4012,7 @@ def BIT_167(cpu): # 167 BIT 4,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_168(cpu): # 168 BIT 5,B @@ -3957,7 +4023,7 @@ def BIT_168(cpu): # 168 BIT 5,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_169(cpu): # 169 BIT 5,C @@ -3968,7 +4034,7 @@ def BIT_169(cpu): # 169 BIT 5,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_16A(cpu): # 16A BIT 5,D @@ -3979,7 +4045,7 @@ def BIT_16A(cpu): # 16A BIT 5,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_16B(cpu): # 16B BIT 5,E @@ -3990,7 +4056,7 @@ def BIT_16B(cpu): # 16B BIT 5,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_16C(cpu): # 16C BIT 5,H @@ -4001,7 +4067,7 @@ def BIT_16C(cpu): # 16C BIT 5,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_16D(cpu): # 16D BIT 5,L @@ -4012,10 +4078,11 @@ def BIT_16D(cpu): # 16D BIT 5,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_16E(cpu): # 16E BIT 5,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 5) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -4023,7 +4090,7 @@ def BIT_16E(cpu): # 16E BIT 5,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_16F(cpu): # 16F BIT 5,A @@ -4034,7 +4101,7 @@ def BIT_16F(cpu): # 16F BIT 5,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_170(cpu): # 170 BIT 6,B @@ -4045,7 +4112,7 @@ def BIT_170(cpu): # 170 BIT 6,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_171(cpu): # 171 BIT 6,C @@ -4056,7 +4123,7 @@ def BIT_171(cpu): # 171 BIT 6,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_172(cpu): # 172 BIT 6,D @@ -4067,7 +4134,7 @@ def BIT_172(cpu): # 172 BIT 6,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_173(cpu): # 173 BIT 6,E @@ -4078,7 +4145,7 @@ def BIT_173(cpu): # 173 BIT 6,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_174(cpu): # 174 BIT 6,H @@ -4089,7 +4156,7 @@ def BIT_174(cpu): # 174 BIT 6,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_175(cpu): # 175 BIT 6,L @@ -4100,10 +4167,11 @@ def BIT_175(cpu): # 175 BIT 6,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_176(cpu): # 176 BIT 6,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 6) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -4111,7 +4179,7 @@ def BIT_176(cpu): # 176 BIT 6,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_177(cpu): # 177 BIT 6,A @@ -4122,7 +4190,7 @@ def BIT_177(cpu): # 177 BIT 6,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_178(cpu): # 178 BIT 7,B @@ -4133,7 +4201,7 @@ def BIT_178(cpu): # 178 BIT 7,B cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_179(cpu): # 179 BIT 7,C @@ -4144,7 +4212,7 @@ def BIT_179(cpu): # 179 BIT 7,C cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_17A(cpu): # 17A BIT 7,D @@ -4155,7 +4223,7 @@ def BIT_17A(cpu): # 17A BIT 7,D cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_17B(cpu): # 17B BIT 7,E @@ -4166,7 +4234,7 @@ def BIT_17B(cpu): # 17B BIT 7,E cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_17C(cpu): # 17C BIT 7,H @@ -4177,7 +4245,7 @@ def BIT_17C(cpu): # 17C BIT 7,H cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_17D(cpu): # 17D BIT 7,L @@ -4188,10 +4256,11 @@ def BIT_17D(cpu): # 17D BIT 7,L cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def BIT_17E(cpu): # 17E BIT 7,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & (1 << 7) flag = 0b00100000 flag += ((t & 0xFF) == 0) << FLAGZ @@ -4199,7 +4268,7 @@ def BIT_17E(cpu): # 17E BIT 7,(HL) cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def BIT_17F(cpu): # 17F BIT 7,A @@ -4210,7 +4279,7 @@ def BIT_17F(cpu): # 17F BIT 7,A cpu.F |= flag cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_180(cpu): # 180 RES 0,B @@ -4218,7 +4287,7 @@ def RES_180(cpu): # 180 RES 0,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_181(cpu): # 181 RES 0,C @@ -4226,7 +4295,7 @@ def RES_181(cpu): # 181 RES 0,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_182(cpu): # 182 RES 0,D @@ -4234,7 +4303,7 @@ def RES_182(cpu): # 182 RES 0,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_183(cpu): # 183 RES 0,E @@ -4242,7 +4311,7 @@ def RES_183(cpu): # 183 RES 0,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_184(cpu): # 184 RES 0,H @@ -4250,7 +4319,7 @@ def RES_184(cpu): # 184 RES 0,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_185(cpu): # 185 RES 0,L @@ -4258,15 +4327,17 @@ def RES_185(cpu): # 185 RES 0,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_186(cpu): # 186 RES 0,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 0) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_187(cpu): # 187 RES 0,A @@ -4274,7 +4345,7 @@ def RES_187(cpu): # 187 RES 0,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_188(cpu): # 188 RES 1,B @@ -4282,7 +4353,7 @@ def RES_188(cpu): # 188 RES 1,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_189(cpu): # 189 RES 1,C @@ -4290,7 +4361,7 @@ def RES_189(cpu): # 189 RES 1,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_18A(cpu): # 18A RES 1,D @@ -4298,7 +4369,7 @@ def RES_18A(cpu): # 18A RES 1,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_18B(cpu): # 18B RES 1,E @@ -4306,7 +4377,7 @@ def RES_18B(cpu): # 18B RES 1,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_18C(cpu): # 18C RES 1,H @@ -4314,7 +4385,7 @@ def RES_18C(cpu): # 18C RES 1,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_18D(cpu): # 18D RES 1,L @@ -4322,15 +4393,17 @@ def RES_18D(cpu): # 18D RES 1,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_18E(cpu): # 18E RES 1,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 1) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_18F(cpu): # 18F RES 1,A @@ -4338,7 +4411,7 @@ def RES_18F(cpu): # 18F RES 1,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_190(cpu): # 190 RES 2,B @@ -4346,7 +4419,7 @@ def RES_190(cpu): # 190 RES 2,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_191(cpu): # 191 RES 2,C @@ -4354,7 +4427,7 @@ def RES_191(cpu): # 191 RES 2,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_192(cpu): # 192 RES 2,D @@ -4362,7 +4435,7 @@ def RES_192(cpu): # 192 RES 2,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_193(cpu): # 193 RES 2,E @@ -4370,7 +4443,7 @@ def RES_193(cpu): # 193 RES 2,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_194(cpu): # 194 RES 2,H @@ -4378,7 +4451,7 @@ def RES_194(cpu): # 194 RES 2,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_195(cpu): # 195 RES 2,L @@ -4386,15 +4459,17 @@ def RES_195(cpu): # 195 RES 2,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_196(cpu): # 196 RES 2,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 2) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_197(cpu): # 197 RES 2,A @@ -4402,7 +4477,7 @@ def RES_197(cpu): # 197 RES 2,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_198(cpu): # 198 RES 3,B @@ -4410,7 +4485,7 @@ def RES_198(cpu): # 198 RES 3,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_199(cpu): # 199 RES 3,C @@ -4418,7 +4493,7 @@ def RES_199(cpu): # 199 RES 3,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_19A(cpu): # 19A RES 3,D @@ -4426,7 +4501,7 @@ def RES_19A(cpu): # 19A RES 3,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_19B(cpu): # 19B RES 3,E @@ -4434,7 +4509,7 @@ def RES_19B(cpu): # 19B RES 3,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_19C(cpu): # 19C RES 3,H @@ -4442,7 +4517,7 @@ def RES_19C(cpu): # 19C RES 3,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_19D(cpu): # 19D RES 3,L @@ -4450,15 +4525,17 @@ def RES_19D(cpu): # 19D RES 3,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_19E(cpu): # 19E RES 3,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 3) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_19F(cpu): # 19F RES 3,A @@ -4466,7 +4543,7 @@ def RES_19F(cpu): # 19F RES 3,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A0(cpu): # 1A0 RES 4,B @@ -4474,7 +4551,7 @@ def RES_1A0(cpu): # 1A0 RES 4,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A1(cpu): # 1A1 RES 4,C @@ -4482,7 +4559,7 @@ def RES_1A1(cpu): # 1A1 RES 4,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A2(cpu): # 1A2 RES 4,D @@ -4490,7 +4567,7 @@ def RES_1A2(cpu): # 1A2 RES 4,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A3(cpu): # 1A3 RES 4,E @@ -4498,7 +4575,7 @@ def RES_1A3(cpu): # 1A3 RES 4,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A4(cpu): # 1A4 RES 4,H @@ -4506,7 +4583,7 @@ def RES_1A4(cpu): # 1A4 RES 4,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A5(cpu): # 1A5 RES 4,L @@ -4514,15 +4591,17 @@ def RES_1A5(cpu): # 1A5 RES 4,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A6(cpu): # 1A6 RES 4,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 4) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_1A7(cpu): # 1A7 RES 4,A @@ -4530,7 +4609,7 @@ def RES_1A7(cpu): # 1A7 RES 4,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A8(cpu): # 1A8 RES 5,B @@ -4538,7 +4617,7 @@ def RES_1A8(cpu): # 1A8 RES 5,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1A9(cpu): # 1A9 RES 5,C @@ -4546,7 +4625,7 @@ def RES_1A9(cpu): # 1A9 RES 5,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1AA(cpu): # 1AA RES 5,D @@ -4554,7 +4633,7 @@ def RES_1AA(cpu): # 1AA RES 5,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1AB(cpu): # 1AB RES 5,E @@ -4562,7 +4641,7 @@ def RES_1AB(cpu): # 1AB RES 5,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1AC(cpu): # 1AC RES 5,H @@ -4570,7 +4649,7 @@ def RES_1AC(cpu): # 1AC RES 5,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1AD(cpu): # 1AD RES 5,L @@ -4578,15 +4657,17 @@ def RES_1AD(cpu): # 1AD RES 5,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1AE(cpu): # 1AE RES 5,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 5) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_1AF(cpu): # 1AF RES 5,A @@ -4594,7 +4675,7 @@ def RES_1AF(cpu): # 1AF RES 5,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B0(cpu): # 1B0 RES 6,B @@ -4602,7 +4683,7 @@ def RES_1B0(cpu): # 1B0 RES 6,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B1(cpu): # 1B1 RES 6,C @@ -4610,7 +4691,7 @@ def RES_1B1(cpu): # 1B1 RES 6,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B2(cpu): # 1B2 RES 6,D @@ -4618,7 +4699,7 @@ def RES_1B2(cpu): # 1B2 RES 6,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B3(cpu): # 1B3 RES 6,E @@ -4626,7 +4707,7 @@ def RES_1B3(cpu): # 1B3 RES 6,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B4(cpu): # 1B4 RES 6,H @@ -4634,7 +4715,7 @@ def RES_1B4(cpu): # 1B4 RES 6,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B5(cpu): # 1B5 RES 6,L @@ -4642,15 +4723,17 @@ def RES_1B5(cpu): # 1B5 RES 6,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B6(cpu): # 1B6 RES 6,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 6) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_1B7(cpu): # 1B7 RES 6,A @@ -4658,7 +4741,7 @@ def RES_1B7(cpu): # 1B7 RES 6,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B8(cpu): # 1B8 RES 7,B @@ -4666,7 +4749,7 @@ def RES_1B8(cpu): # 1B8 RES 7,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1B9(cpu): # 1B9 RES 7,C @@ -4674,7 +4757,7 @@ def RES_1B9(cpu): # 1B9 RES 7,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1BA(cpu): # 1BA RES 7,D @@ -4682,7 +4765,7 @@ def RES_1BA(cpu): # 1BA RES 7,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1BB(cpu): # 1BB RES 7,E @@ -4690,7 +4773,7 @@ def RES_1BB(cpu): # 1BB RES 7,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1BC(cpu): # 1BC RES 7,H @@ -4698,7 +4781,7 @@ def RES_1BC(cpu): # 1BC RES 7,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1BD(cpu): # 1BD RES 7,L @@ -4706,15 +4789,17 @@ def RES_1BD(cpu): # 1BD RES 7,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def RES_1BE(cpu): # 1BE RES 7,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) & ~(1 << 7) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def RES_1BF(cpu): # 1BF RES 7,A @@ -4722,7 +4807,7 @@ def RES_1BF(cpu): # 1BF RES 7,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C0(cpu): # 1C0 SET 0,B @@ -4730,7 +4815,7 @@ def SET_1C0(cpu): # 1C0 SET 0,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C1(cpu): # 1C1 SET 0,C @@ -4738,7 +4823,7 @@ def SET_1C1(cpu): # 1C1 SET 0,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C2(cpu): # 1C2 SET 0,D @@ -4746,7 +4831,7 @@ def SET_1C2(cpu): # 1C2 SET 0,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C3(cpu): # 1C3 SET 0,E @@ -4754,7 +4839,7 @@ def SET_1C3(cpu): # 1C3 SET 0,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C4(cpu): # 1C4 SET 0,H @@ -4762,7 +4847,7 @@ def SET_1C4(cpu): # 1C4 SET 0,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C5(cpu): # 1C5 SET 0,L @@ -4770,15 +4855,17 @@ def SET_1C5(cpu): # 1C5 SET 0,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C6(cpu): # 1C6 SET 0,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 0) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1C7(cpu): # 1C7 SET 0,A @@ -4786,7 +4873,7 @@ def SET_1C7(cpu): # 1C7 SET 0,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C8(cpu): # 1C8 SET 1,B @@ -4794,7 +4881,7 @@ def SET_1C8(cpu): # 1C8 SET 1,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1C9(cpu): # 1C9 SET 1,C @@ -4802,7 +4889,7 @@ def SET_1C9(cpu): # 1C9 SET 1,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1CA(cpu): # 1CA SET 1,D @@ -4810,7 +4897,7 @@ def SET_1CA(cpu): # 1CA SET 1,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1CB(cpu): # 1CB SET 1,E @@ -4818,7 +4905,7 @@ def SET_1CB(cpu): # 1CB SET 1,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1CC(cpu): # 1CC SET 1,H @@ -4826,7 +4913,7 @@ def SET_1CC(cpu): # 1CC SET 1,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1CD(cpu): # 1CD SET 1,L @@ -4834,15 +4921,17 @@ def SET_1CD(cpu): # 1CD SET 1,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1CE(cpu): # 1CE SET 1,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 1) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1CF(cpu): # 1CF SET 1,A @@ -4850,7 +4939,7 @@ def SET_1CF(cpu): # 1CF SET 1,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D0(cpu): # 1D0 SET 2,B @@ -4858,7 +4947,7 @@ def SET_1D0(cpu): # 1D0 SET 2,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D1(cpu): # 1D1 SET 2,C @@ -4866,7 +4955,7 @@ def SET_1D1(cpu): # 1D1 SET 2,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D2(cpu): # 1D2 SET 2,D @@ -4874,7 +4963,7 @@ def SET_1D2(cpu): # 1D2 SET 2,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D3(cpu): # 1D3 SET 2,E @@ -4882,7 +4971,7 @@ def SET_1D3(cpu): # 1D3 SET 2,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D4(cpu): # 1D4 SET 2,H @@ -4890,7 +4979,7 @@ def SET_1D4(cpu): # 1D4 SET 2,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D5(cpu): # 1D5 SET 2,L @@ -4898,15 +4987,17 @@ def SET_1D5(cpu): # 1D5 SET 2,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D6(cpu): # 1D6 SET 2,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 2) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1D7(cpu): # 1D7 SET 2,A @@ -4914,7 +5005,7 @@ def SET_1D7(cpu): # 1D7 SET 2,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D8(cpu): # 1D8 SET 3,B @@ -4922,7 +5013,7 @@ def SET_1D8(cpu): # 1D8 SET 3,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1D9(cpu): # 1D9 SET 3,C @@ -4930,7 +5021,7 @@ def SET_1D9(cpu): # 1D9 SET 3,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1DA(cpu): # 1DA SET 3,D @@ -4938,7 +5029,7 @@ def SET_1DA(cpu): # 1DA SET 3,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1DB(cpu): # 1DB SET 3,E @@ -4946,7 +5037,7 @@ def SET_1DB(cpu): # 1DB SET 3,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1DC(cpu): # 1DC SET 3,H @@ -4954,7 +5045,7 @@ def SET_1DC(cpu): # 1DC SET 3,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1DD(cpu): # 1DD SET 3,L @@ -4962,15 +5053,17 @@ def SET_1DD(cpu): # 1DD SET 3,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1DE(cpu): # 1DE SET 3,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 3) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1DF(cpu): # 1DF SET 3,A @@ -4978,7 +5071,7 @@ def SET_1DF(cpu): # 1DF SET 3,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E0(cpu): # 1E0 SET 4,B @@ -4986,7 +5079,7 @@ def SET_1E0(cpu): # 1E0 SET 4,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E1(cpu): # 1E1 SET 4,C @@ -4994,7 +5087,7 @@ def SET_1E1(cpu): # 1E1 SET 4,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E2(cpu): # 1E2 SET 4,D @@ -5002,7 +5095,7 @@ def SET_1E2(cpu): # 1E2 SET 4,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E3(cpu): # 1E3 SET 4,E @@ -5010,7 +5103,7 @@ def SET_1E3(cpu): # 1E3 SET 4,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E4(cpu): # 1E4 SET 4,H @@ -5018,7 +5111,7 @@ def SET_1E4(cpu): # 1E4 SET 4,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E5(cpu): # 1E5 SET 4,L @@ -5026,15 +5119,17 @@ def SET_1E5(cpu): # 1E5 SET 4,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E6(cpu): # 1E6 SET 4,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 4) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1E7(cpu): # 1E7 SET 4,A @@ -5042,7 +5137,7 @@ def SET_1E7(cpu): # 1E7 SET 4,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E8(cpu): # 1E8 SET 5,B @@ -5050,7 +5145,7 @@ def SET_1E8(cpu): # 1E8 SET 5,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1E9(cpu): # 1E9 SET 5,C @@ -5058,7 +5153,7 @@ def SET_1E9(cpu): # 1E9 SET 5,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1EA(cpu): # 1EA SET 5,D @@ -5066,7 +5161,7 @@ def SET_1EA(cpu): # 1EA SET 5,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1EB(cpu): # 1EB SET 5,E @@ -5074,7 +5169,7 @@ def SET_1EB(cpu): # 1EB SET 5,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1EC(cpu): # 1EC SET 5,H @@ -5082,7 +5177,7 @@ def SET_1EC(cpu): # 1EC SET 5,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1ED(cpu): # 1ED SET 5,L @@ -5090,15 +5185,17 @@ def SET_1ED(cpu): # 1ED SET 5,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1EE(cpu): # 1EE SET 5,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 5) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1EF(cpu): # 1EF SET 5,A @@ -5106,7 +5203,7 @@ def SET_1EF(cpu): # 1EF SET 5,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F0(cpu): # 1F0 SET 6,B @@ -5114,7 +5211,7 @@ def SET_1F0(cpu): # 1F0 SET 6,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F1(cpu): # 1F1 SET 6,C @@ -5122,7 +5219,7 @@ def SET_1F1(cpu): # 1F1 SET 6,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F2(cpu): # 1F2 SET 6,D @@ -5130,7 +5227,7 @@ def SET_1F2(cpu): # 1F2 SET 6,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F3(cpu): # 1F3 SET 6,E @@ -5138,7 +5235,7 @@ def SET_1F3(cpu): # 1F3 SET 6,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F4(cpu): # 1F4 SET 6,H @@ -5146,7 +5243,7 @@ def SET_1F4(cpu): # 1F4 SET 6,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F5(cpu): # 1F5 SET 6,L @@ -5154,15 +5251,17 @@ def SET_1F5(cpu): # 1F5 SET 6,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F6(cpu): # 1F6 SET 6,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 6) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1F7(cpu): # 1F7 SET 6,A @@ -5170,7 +5269,7 @@ def SET_1F7(cpu): # 1F7 SET 6,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F8(cpu): # 1F8 SET 7,B @@ -5178,7 +5277,7 @@ def SET_1F8(cpu): # 1F8 SET 7,B cpu.B = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1F9(cpu): # 1F9 SET 7,C @@ -5186,7 +5285,7 @@ def SET_1F9(cpu): # 1F9 SET 7,C cpu.C = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1FA(cpu): # 1FA SET 7,D @@ -5194,7 +5293,7 @@ def SET_1FA(cpu): # 1FA SET 7,D cpu.D = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1FB(cpu): # 1FB SET 7,E @@ -5202,7 +5301,7 @@ def SET_1FB(cpu): # 1FB SET 7,E cpu.E = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1FC(cpu): # 1FC SET 7,H @@ -5210,7 +5309,7 @@ def SET_1FC(cpu): # 1FC SET 7,H cpu.HL = (cpu.HL & 0x00FF) | (t << 8) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1FD(cpu): # 1FD SET 7,L @@ -5218,15 +5317,17 @@ def SET_1FD(cpu): # 1FD SET 7,L cpu.HL = (cpu.HL & 0xFF00) | (t & 0xFF) cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def SET_1FE(cpu): # 1FE SET 7,(HL) + cpu.cycles += 4 t = cpu.mb.getitem(cpu.HL) | (1 << 7) + cpu.cycles += 4 cpu.mb.setitem(cpu.HL, t) cpu.PC += 2 cpu.PC &= 0xFFFF - return 16 + cpu.cycles += 8 def SET_1FF(cpu): # 1FF SET 7,A @@ -5234,7 +5335,7 @@ def SET_1FF(cpu): # 1FF SET 7,A cpu.A = t cpu.PC += 2 cpu.PC &= 0xFFFF - return 8 + cpu.cycles += 8 def no_opcode(cpu): @@ -6282,6 +6383,42 @@ def execute_opcode(cpu, opcode): return SET_1FF(cpu) +OPCODE_MAX_CYCLES = array.array("B", [ + 4, 12, 8, 8, 4, 4, 8, 4, 20, 8, 8, 8, 4, 4, 8, 4, + 4, 12, 8, 8, 4, 4, 8, 4, 12, 8, 8, 8, 4, 4, 8, 4, + 12, 12, 8, 8, 4, 4, 8, 4, 12, 8, 8, 8, 4, 4, 8, 4, + 12, 12, 8, 8, 12, 12, 12, 4, 12, 8, 8, 8, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 8, 8, 8, 8, 8, 8, 4, 8, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, + 20, 12, 16, 16, 24, 16, 8, 16, 20, 16, 16, 4, 24, 24, 8, 16, + 20, 12, 16, 0, 24, 16, 8, 16, 20, 16, 16, 0, 24, 0, 8, 16, + 12, 12, 8, 0, 0, 16, 8, 16, 16, 4, 16, 0, 0, 0, 8, 16, + 12, 12, 8, 4, 0, 16, 8, 16, 12, 8, 16, 4, 0, 0, 8, 16, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + 8, 8, 8, 8, 8, 8, 16, 8, 8, 8, 8, 8, 8, 8, 16, 8, + ]) + + OPCODE_LENGTHS = array.array("B", [ 1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, diff --git a/pyboy/core/opcodes_gen.py b/pyboy/core/opcodes_gen.py index 279e94462..17e001db2 100644 --- a/pyboy/core/opcodes_gen.py +++ b/pyboy/core/opcodes_gen.py @@ -3,6 +3,7 @@ # GitHub: https://github.com/Baekalfen/PyBoy # +import os import re from html.parser import HTMLParser from urllib.request import urlopen @@ -25,7 +26,14 @@ FLAGC, FLAGH, FLAGN, FLAGZ = range(4, 8) +def get_length(opcode): + return OPCODE_LENGTHS[opcode] + +def get_max_cycles(opcode): + return OPCODE_MAX_CYCLES[opcode] + def BRK(cpu): + cpu.bail = True cpu.mb.breakpoint_singlestep = 1 cpu.mb.breakpoint_singlestep_latch = 0 # NOTE: We do not increment PC @@ -34,15 +42,21 @@ def BRK(cpu): """ cimports = """ -from . cimport cpu cimport cython from libc.stdint cimport uint8_t, uint16_t, uint32_t from pyboy.logging.logging cimport Logger + +from . cimport cpu + + cdef Logger logger cdef uint16_t FLAGC, FLAGH, FLAGN, FLAGZ cdef uint8_t[512] OPCODE_LENGTHS +cdef uint8_t get_length(int) noexcept nogil +cdef uint8_t[512] OPCODE_MAX_CYCLES +cdef uint8_t get_max_cycles(int) noexcept nogil @cython.locals(v=cython.int, a=cython.int, b=cython.int, pc=cython.ushort) cdef int execute_opcode(cpu.CPU, uint16_t) noexcept nogil @@ -260,13 +274,7 @@ def getcode(self): "def %s_%0.2X(cpu, v): # %0.2X %s" % (self.function_name, self.opcode, self.opcode, self.name) ][self.takes_immediate] code += "\n\t" - - if not self.branch_op: - self.lines.append("cpu.PC += %d" % self.length) - self.lines.append("cpu.PC &= 0xFFFF") - self.lines.append("return " + self.cycles[0]) # Choose the 0th cycle count - - code += "\n\t".join(self.lines) + code += self._code_body() pxd = [ "cdef uint8_t %s_%0.2X(cpu.CPU) noexcept nogil # %0.2X %s" % @@ -284,6 +292,14 @@ def getcode(self): return (pxd, code) + def _code_body(self): + if not self.branch_op: + self.lines.append("cpu.PC += %d" % self.length) + self.lines.append("cpu.PC &= 0xFFFF") + self.lines.append("cpu.cycles += " + self.cycles[0]) # Choose the 0th cycle count + + return "\n\t".join(self.lines) + class OpcodeData: def __init__(self, opcode, name, length, cycles, bit16, flag_z, flag_n, flag_h, flag_c): @@ -350,11 +366,11 @@ def __init__(self, opcode, name, length, cycles, bit16, flag_z, flag_n, flag_h, # yapf: enable def createfunction(self): - text = self.functionhandlers[self.name.split()[0]]() + text = self.functionhandlers[self.name.split()[0]]().getcode() # Compensate for CB operations being "2 bytes long" if self.opcode > 0xFF: self.length -= 1 - return (self.length, "%s_%0.2X" % (self.name.split()[0], self.opcode), self.name), text + return (self.length, self.cycles, "%s_%0.2X" % (self.name.split()[0], self.opcode), self.name), text # Special carry and half-carry for E8 and F8: # http://forums.nesdev.com/viewtopic.php?p=42138 @@ -451,7 +467,7 @@ def handleflags8bit(self, r0, r1, op, carry=False): # def NOP(self): code = Code(self.name.split()[0], self.opcode, self.name, 0, self.length, self.cycles) - return code.getcode() + return code def HALT(self): code = Code(self.name.split()[0], self.opcode, self.name, 0, self.length, self.cycles, branch_op=True) @@ -459,24 +475,28 @@ def HALT(self): # TODO: Implement HALT bug. code.addlines([ "cpu.halted = True", - "return " + self.cycles[0], + "cpu.bail = True", + "cpu.cycles += " + self.cycles[0], ]) - return code.getcode() + return code def CB(self): code = Code(self.name.split()[0], self.opcode, self.name, 0, self.length, self.cycles) code.addline("logger.critical('CB cannot be called!')") - return code.getcode() + return code def EI(self): code = Code(self.name.split()[0], self.opcode, self.name, 0, self.length, self.cycles) - code.addline("cpu.interrupt_master_enable = True") - return code.getcode() + code.addlines([ + "cpu.interrupt_master_enable = True", + "cpu.bail = (cpu.interrupts_flag_register & 0b11111) & (cpu.interrupts_enabled_register & 0b11111)", + ]) + return code def DI(self): code = Code(self.name.split()[0], self.opcode, self.name, 0, self.length, self.cycles) code.addline("cpu.interrupt_master_enable = False") - return code.getcode() + return code def STOP(self): code = Code(self.name.split()[0], self.opcode, self.name, True, self.length, self.cycles) @@ -486,7 +506,7 @@ def STOP(self): " cpu.mb.setitem(0xFF04, 0)", ]) # code.addLine("raise Exception('STOP not implemented!')") - return code.getcode() + return code def DAA(self): left = Operand("A") @@ -513,12 +533,12 @@ def DAA(self): "t &= 0xFF", left.set % "t", ]) - return code.getcode() + return code def SCF(self): code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) code.addlines(self.handleflags8bit(None, None, None)) - return code.getcode() + return code def CCF(self): code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) @@ -527,14 +547,14 @@ def CCF(self): "cpu.F &= 0b10000000", "cpu.F |= flag", ]) - return code.getcode() + return code def CPL(self): left = Operand("A") code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) code.addline(left.set % ("(~%s) & 0xFF" % left.get)) code.addlines(self.handleflags8bit(None, None, None)) - return code.getcode() + return code ################################################################### # @@ -552,6 +572,24 @@ def LD(self): code = Code( self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) + + # These opcodes can be observed reading mid-cycle + if self.opcode == 0x36: + code.addline("cpu.cycles += 4") + code.cycles = (str(int(code.cycles[0]) - 4), ) + elif self.opcode == 0xE0: + code.addline("cpu.cycles += 4") + code.cycles = (str(int(code.cycles[0]) - 4), ) + elif self.opcode == 0xEA: + code.addline("cpu.cycles += 8") + code.cycles = (str(int(code.cycles[0]) - 8), ) + elif self.opcode == 0xF0: + code.addline("cpu.cycles += 4") + code.cycles = (str(int(code.cycles[0]) - 4), ) + elif self.opcode == 0xFA: + code.addline("cpu.cycles += 8") + code.cycles = (str(int(code.cycles[0]) - 8), ) + if self.is16bit and left.immediate and left.pointer: code.addline(left.set % ("%s & 0xFF" % right.get)) a, b = left.set.split(",") @@ -577,7 +615,7 @@ def LD(self): code.addlines(self.handleflags16bit_E8_F8("cpu.SP", "v", "+", False)) code.addline("cpu.HL &= 0xFFFF") - return code.getcode() + return code def LDH(self): return self.LD() @@ -630,7 +668,7 @@ def ADD(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "+")) - return code.getcode() + return code def SUB(self): if self.name.find(",") > 0: @@ -646,7 +684,7 @@ def SUB(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "-")) - return code.getcode() + return code def INC(self): r0 = self.name.split()[1] @@ -657,7 +695,14 @@ def INC(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "+")) - return code.getcode() + + if self.opcode == 0x34: + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(-1, "cpu.cycles += 4") # Inject before read + code.cycles = ("8", ) # 12 - 4 + + return code def DEC(self): r0 = self.name.split()[1] @@ -668,7 +713,14 @@ def DEC(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "-")) - return code.getcode() + + if self.opcode == 0x35: + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(-1, "cpu.cycles += 4") # Inject before write + code.cycles = ("8", ) # 12 - 4 + + return code def ADC(self): if self.name.find(",") > 0: @@ -684,7 +736,7 @@ def ADC(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "+", carry=True)) - return code.getcode() + return code def SBC(self): if self.name.find(",") > 0: @@ -700,7 +752,7 @@ def SBC(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "-", carry=True)) - return code.getcode() + return code def AND(self): if self.name.find(",") > 0: @@ -716,7 +768,7 @@ def AND(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "&")) - return code.getcode() + return code def OR(self): if self.name.find(",") > 0: @@ -732,7 +784,7 @@ def OR(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "|")) - return code.getcode() + return code def XOR(self): if self.name.find(",") > 0: @@ -748,7 +800,7 @@ def XOR(self): self.name.split()[0], self.opcode, self.name, left.immediate or right.immediate, self.length, self.cycles ) code.addlines(self.ALU(left, right, "^")) - return code.getcode() + return code def CP(self): r1 = self.name.split()[1] @@ -761,7 +813,7 @@ def CP(self): # CP is equal to SUB, but without saving the result. # Therefore; we discard the last instruction. code.addlines(self.ALU(left, right, "-")[:-1]) - return code.getcode() + return code ################################################################### # @@ -791,7 +843,7 @@ def PUSH(self): code.addline("cpu.SP -= 2") code.addline("cpu.SP &= 0xFFFF") - return code.getcode() + return code def POP(self): r0 = self.name.split()[1] @@ -819,7 +871,7 @@ def POP(self): code.addline("cpu.SP += 2") code.addline("cpu.SP &= 0xFFFF") - return code.getcode() + return code ################################################################### # @@ -853,19 +905,20 @@ def JP(self): self.name.split()[0], self.opcode, self.name, right.immediate, self.length, self.cycles, branch_op=True ) if left is None: - code.addlines(["cpu.PC = %s" % ("v" if right.immediate else r_code), "return " + self.cycles[0]]) + code.addlines(["cpu.PC = %s" % ("v" if right.immediate else r_code), "cpu.cycles += " + self.cycles[0]]) else: code.addlines([ "if %s:" % l_code, "\tcpu.PC = %s" % ("v" if right.immediate else r_code), - "\treturn " + self.cycles[0], + "\tcpu.jit_jump = True", + "\tcpu.cycles += " + self.cycles[0], "else:", "\tcpu.PC += %s" % self.length, "\tcpu.PC &= 0xFFFF", - "\treturn " + self.cycles[1], + "\tcpu.cycles += " + self.cycles[1], ]) - return code.getcode() + return code def JR(self): if self.name.find(",") > 0: @@ -892,7 +945,8 @@ def JR(self): code.addlines([ "cpu.PC += %d + " % self.length + inline_signed_int8("v"), "cpu.PC &= 0xFFFF", - "return " + self.cycles[0], + "cpu.jit_jump = True", + "cpu.cycles += " + self.cycles[0], ]) else: code.addlines([ @@ -900,13 +954,14 @@ def JR(self): "if %s:" % l_code, "\tcpu.PC += " + inline_signed_int8("v"), "\tcpu.PC &= 0xFFFF", - "\treturn " + self.cycles[0], + "\tcpu.jit_jump = True", + "\tcpu.cycles += " + self.cycles[0], "else:", "\tcpu.PC &= 0xFFFF", - "\treturn " + self.cycles[1], + "\tcpu.cycles += " + self.cycles[1], ]) - return code.getcode() + return code def CALL(self): if self.name.find(",") > 0: @@ -943,7 +998,8 @@ def CALL(self): "cpu.SP -= 2", "cpu.SP &= 0xFFFF", "cpu.PC = %s" % ("v" if right.immediate else right.get), - "return " + self.cycles[0], + "cpu.jit_jump = True", + "cpu.cycles += " + self.cycles[0], ]) else: code.addlines([ @@ -953,12 +1009,13 @@ def CALL(self): "\tcpu.SP -= 2", "\tcpu.SP &= 0xFFFF", "\tcpu.PC = %s" % ("v" if right.immediate else right.get), - "\treturn " + self.cycles[0], + "\tcpu.jit_jump = True", + "\tcpu.cycles += " + self.cycles[0], "else:", - "\treturn " + self.cycles[1], + "\tcpu.cycles += " + self.cycles[1], ]) - return code.getcode() + return code def RET(self): if self.name == "RET": @@ -981,7 +1038,8 @@ def RET(self): "cpu.PC |= cpu.mb.getitem(cpu.SP) # Low", "cpu.SP += 2", "cpu.SP &= 0xFFFF", - "return " + self.cycles[0], + "cpu.jit_jump = True", + "cpu.cycles += " + self.cycles[0], ]) else: code.addlines([ @@ -990,27 +1048,30 @@ def RET(self): "\tcpu.PC |= cpu.mb.getitem(cpu.SP) # Low", "\tcpu.SP += 2", "\tcpu.SP &= 0xFFFF", - "\treturn " + self.cycles[0], + "\tcpu.jit_jump = True", + "\tcpu.cycles += " + self.cycles[0], "else:", "\tcpu.PC += %s" % self.length, "\tcpu.PC &= 0xFFFF", - "\treturn " + self.cycles[1], + "\tcpu.cycles += " + self.cycles[1], ]) - return code.getcode() + return code def RETI(self): code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles, branch_op=True) - code.addline("cpu.interrupt_master_enable = True") code.addlines([ + "cpu.interrupt_master_enable = True", + "cpu.bail = (cpu.interrupts_flag_register & 0b11111) & (cpu.interrupts_enabled_register & 0b11111)", "cpu.PC = cpu.mb.getitem((cpu.SP + 1) & 0xFFFF) << 8 # High", "cpu.PC |= cpu.mb.getitem(cpu.SP) # Low", "cpu.SP += 2", "cpu.SP &= 0xFFFF", - "return " + self.cycles[0], + "cpu.jit_jump = True", + "cpu.cycles += " + self.cycles[0], ]) - return code.getcode() + return code def RST(self): r1 = self.name.split()[1] @@ -1030,10 +1091,11 @@ def RST(self): code.addlines([ "cpu.PC = %s" % (right.code), - "return " + self.cycles[0], + "cpu.jit_jump = True", + "cpu.cycles += " + self.cycles[0], ]) - return code.getcode() + return code ################################################################### # @@ -1049,30 +1111,38 @@ def rotateleft(self, name, left, throughcarry=False): code.addlines(self.handleflags8bit(left.get, None, None, throughcarry)) code.addline("t &= 0xFF") left.assign = True + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") return code def RLA(self): left = Operand("A") code = self.rotateleft(self.name.split()[0], left, throughcarry=True) - return code.getcode() + return code def RLCA(self): left = Operand("A") code = self.rotateleft(self.name.split()[0], left) - return code.getcode() + return code def RLC(self): r0 = self.name.split()[1] left = Operand(r0) code = self.rotateleft(self.name.split()[0], left) - return code.getcode() + return code def RL(self): r0 = self.name.split()[1] left = Operand(r0) code = self.rotateleft(self.name.split()[0], left, throughcarry=True) - return code.getcode() + return code def rotateright(self, name, left, throughcarry=False): code = Code(name, self.opcode, self.name, False, self.length, self.cycles) @@ -1086,30 +1156,38 @@ def rotateright(self, name, left, throughcarry=False): code.addline("t = (%s >> 1) + ((%s & 1) << 7)" % (left.get, left.get) + " + ((%s & 1) << 8)" % (left.get)) code.addlines(self.handleflags8bit(left.get, None, None, throughcarry)) code.addline("t &= 0xFF") + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") return code def RRA(self): left = Operand("A") code = self.rotateright(self.name.split()[0], left, throughcarry=True) - return code.getcode() + return code def RRCA(self): left = Operand("A") code = self.rotateright(self.name.split()[0], left) - return code.getcode() + return code def RRC(self): r0 = self.name.split()[1] left = Operand(r0) code = self.rotateright(self.name.split()[0], left) - return code.getcode() + return code def RR(self): r0 = self.name.split()[1] left = Operand(r0) code = self.rotateright(self.name.split()[0], left, throughcarry=True) - return code.getcode() + return code def SLA(self): r0 = self.name.split()[1] @@ -1118,8 +1196,16 @@ def SLA(self): code.addline("t = (%s << 1)" % left.get) code.addlines(self.handleflags8bit(left.get, None, None, False)) code.addline("t &= 0xFF") + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") - return code.getcode() + return code def SRA(self): r0 = self.name.split()[1] @@ -1131,8 +1217,16 @@ def SRA(self): code.addline("t = ((%s >> 1) | (%s & 0x80)) + ((%s & 1) << 8)" % (left.get, left.get, left.get)) code.addlines(self.handleflags8bit(left.get, None, None, False)) code.addline("t &= 0xFF") + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") - return code.getcode() + return code def SRL(self): r0 = self.name.split()[1] @@ -1142,8 +1236,16 @@ def SRL(self): code.addline("t = (%s >> 1) + ((%s & 1) << 8)" % (left.get, left.get)) code.addlines(self.handleflags8bit(left.get, None, None, False)) code.addline("t &= 0xFF") + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") - return code.getcode() + return code def SWAP(self): r0 = self.name.split()[1] @@ -1152,8 +1254,16 @@ def SWAP(self): code.addline("t = ((%s & 0xF0) >> 4) | ((%s & 0x0F) << 4)" % (left.get, left.get)) code.addlines(self.handleflags8bit(left.get, None, None, False)) code.addline("t &= 0xFF") + + if left.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(left.set % "t") - return code.getcode() + return code ################################################################### # @@ -1164,10 +1274,18 @@ def BIT(self): left = Literal(r0) right = Operand(r1) code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) + + # FIX: Correct cycle count is 12, not 16! + if right.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 12 - 4 + code.addline("t = %s & (1 << %s)" % (right.get, left.get)) code.addlines(self.handleflags8bit(left.get, right.get, None, False)) - return code.getcode() + return code def RES(self): r0, r1 = self.name.split()[1].split(",") @@ -1176,8 +1294,16 @@ def RES(self): code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) code.addline("t = %s & ~(1 << %s)" % (right.get, left.get)) + + if right.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 + code.addline(right.set % "t") - return code.getcode() + return code def SET(self): r0, r1 = self.name.split()[1].split(",") @@ -1185,17 +1311,19 @@ def SET(self): right = Operand(r1) code = Code(self.name.split()[0], self.opcode, self.name, False, self.length, self.cycles) code.addline("t = %s | (1 << %s)" % (right.get, left.get)) - code.addline(right.set % "t") - return code.getcode() + if right.operand == "(HL)": + # HACK: Offset the timing by 4 cycles + # TODO: Probably should be generalized + code.lines.insert(0, "cpu.cycles += 4") # Inject before read + code.addline("cpu.cycles += 4") + code.cycles = ("8", ) # 16 - 4 - 4 -def update(): - response = urlopen("http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html") - html = response.read().replace(b" ", b"
").decode() + code.addline(right.set % "t") + return code - parser = MyHTMLParser() - parser.feed(html) +def update(): opcodefunctions = map(lambda x: (None, None) if x is None else x.createfunction(), opcodes) with open(destination, "w") as f, open(pxd_destination, "w") as f_pxd: @@ -1218,7 +1346,7 @@ def update(): # We create a new opcode to use as a software breakpoint instruction. # I hope the irony of the opcode number is not lost. - lookuplist[0xDB] = (1, "BRK", "Breakpoint/Illegal opcode") + lookuplist[0xDB] = (1, (0, ), "BRK", "Breakpoint/Illegal opcode") f.write("def no_opcode(cpu):\n return 0\n\n\n") @@ -1243,17 +1371,32 @@ def execute_opcode(cpu, opcode): indent = 4 for i, t in enumerate(lookuplist): - t = t if t is not None else (0, "no_opcode", "") + t = t if t is not None else (0, (0, ), "no_opcode", "") + length, cycles, name, text = t f.write( " "*indent + ("if" if i == 0 else "elif") + " opcode == 0x%0.2X:\n"%i + " " * (indent+4) + "return " + - str(t[1]).replace("'", "") + ("(cpu)" if t[0] <= 1 else "(cpu, v)") + "\n" + str(name).replace("'", "") + ("(cpu)" if length <= 1 else "(cpu, v)") + "\n" ) f.write("\n\n") + f.write('OPCODE_MAX_CYCLES = array.array("B", [\n ') + for i, t in enumerate(lookuplist): + t = t if t is not None else (0, (0, ), "no_opcode", "") + length, cycles, name, text = t + f.write(str(cycles[0]).rjust(2).replace("'", "") + ",") + if (i+1) % 16 == 0: + f.write("\n" + " "*4) + else: + f.write(" ") + + f.write("])\n") + f.write("\n\n") + f.write('OPCODE_LENGTHS = array.array("B", [\n ') for i, t in enumerate(lookuplist): - t = t if t is not None else (0, "no_opcode", "") - f.write(str(t[0]).replace("'", "") + ",") + t = t if t is not None else (0, (0, ), "no_opcode", "") + length, cycles, name, text = t + f.write(str(length).replace("'", "") + ",") if (i+1) % 16 == 0: f.write("\n" + " "*4) else: @@ -1264,17 +1407,29 @@ def execute_opcode(cpu, opcode): f.write("\n\n") f.write("CPU_COMMANDS = [\n ") for _, t in enumerate(lookuplist): - t = t if t is not None else (0, "no_opcode", "") - f.write(f"\"{t[2]}\",\n" + " "*4) + t = t if t is not None else (0, (0, ), "no_opcode", "") + length, cycles, name, text = t + f.write(f"\"{text}\",\n" + " "*4) f.write("]\n") def load(): - # if os.path.exists(destination): - # return - update() + cache = "cache.html" + if not os.path.isfile(cache): + response = urlopen("http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html") + html = response.read().replace(b" ", b"
").decode() + + with open(cache, "w") as f: + f.write(html) + else: + with open(cache, "r") as f: + html = f.read() + + parser = MyHTMLParser() + parser.feed(html) +load() if __name__ == "__main__": - load() + update() diff --git a/pyboy/core/sound.pxd b/pyboy/core/sound.pxd index 322ef25df..7b1afde74 100644 --- a/pyboy/core/sound.pxd +++ b/pyboy/core/sound.pxd @@ -4,7 +4,7 @@ # cimport cython -from libc.stdint cimport uint8_t, uint16_t, uint64_t +from libc.stdint cimport int64_t, uint8_t, uint16_t, uint64_t from pyboy.logging.logging cimport Logger from pyboy.utils cimport IntIOInterface @@ -28,6 +28,8 @@ cdef class Sound: cdef object audiobuffer cdef object audiobuffer_p + cdef uint64_t last_cycles + cdef int64_t _cycles_to_interrupt cdef int clock cdef bint poweron @@ -42,6 +44,7 @@ cdef class Sound: cdef uint8_t get(self, uint8_t) noexcept nogil cdef void set(self, uint8_t, uint8_t) noexcept nogil + cdef void tick(self, int64_t, bint) noexcept nogil @cython.locals(nsamples=int, sample=int, i=int) cdef void sync(self) noexcept with gil # TODO: nogil @cython.locals(queued_time=int, samples_per_frame=int) @@ -91,11 +94,7 @@ cdef class SweepChannel(ToneChannel): cdef bint sweepenable # Internal sweep enable flag cdef int shadow # Shadow copy of period register for ignoring writes to sndper - cdef uint8_t getreg(self, uint8_t) noexcept nogil - cdef void setreg(self, uint8_t, uint8_t) noexcept nogil cdef bint sweep(self, bint) noexcept nogil - cdef void trigger(self) noexcept nogil - cdef void tickframe(self) noexcept nogil cdef class WaveChannel: diff --git a/pyboy/core/sound.py b/pyboy/core/sound.py index b69de626c..cd40b2905 100644 --- a/pyboy/core/sound.py +++ b/pyboy/core/sound.py @@ -59,6 +59,8 @@ def __init__(self, enabled, emulate): self.audiobuffer = array("b", [0] * 4096) # Over 2 frames self.audiobuffer_p = c_void_p(self.audiobuffer.buffer_info()[0]) + self.last_cycles = 0 + self._cycles_to_interrupt = 1 << 16 self.clock = 0 self.poweron = True @@ -150,6 +152,15 @@ def set(self, offset, value): else: raise IndexError(f"Attempted to write register {offset} in sound memory") + def tick(self, _cycles, double_speed): + cycles = _cycles - self.last_cycles + self.last_cycles = _cycles + + if double_speed: + self.clock += cycles // 2 + else: + self.clock += cycles + def sync(self): """Run the audio for the number of clock cycles stored in self.clock""" if not self.emulate: diff --git a/pyboy/core/timer.pxd b/pyboy/core/timer.pxd index 37d75b997..b444294e8 100644 --- a/pyboy/core/timer.pxd +++ b/pyboy/core/timer.pxd @@ -18,12 +18,12 @@ cdef class Timer: cdef uint64_t DIV, TIMA, TMA, TAC cdef uint16_t DIV_counter, TIMA_counter cdef uint64_t[4] dividers + cdef int64_t _cycles_to_interrupt + cdef uint64_t last_cycles cdef void reset(self) noexcept nogil @cython.locals(divider=cython.int) cdef bint tick(self, uint64_t) noexcept nogil - @cython.locals(divider=cython.int, cyclesleft=cython.uint) - cdef int64_t cycles_to_interrupt(self) noexcept nogil cdef void save_state(self, IntIOInterface) noexcept cdef void load_state(self, IntIOInterface, int) noexcept diff --git a/pyboy/core/timer.py b/pyboy/core/timer.py index 0f89226f8..16d5cf2b2 100644 --- a/pyboy/core/timer.py +++ b/pyboy/core/timer.py @@ -25,7 +25,9 @@ def __init__(self): self.TIMA_counter = 0 self.TMA = 0 self.TAC = 0 - self.dividers = [1024, 16, 64, 256] + self.dividers = [10, 4, 6, 8] + self._cycles_to_interrupt = 0 + self.last_cycles = 0 def reset(self): # TODO: Should probably one be DIV=0, but this makes a bunch of mooneye tests pass @@ -33,39 +35,35 @@ def reset(self): self.TIMA_counter = 0 self.DIV = 0 - def tick(self, cycles): + def tick(self, _cycles): + cycles = _cycles - self.last_cycles + if cycles == 0: + return False + self.last_cycles = _cycles + self.DIV_counter += cycles self.DIV += (self.DIV_counter >> 8) # Add overflown bits to DIV self.DIV_counter &= 0xFF # Remove the overflown bits self.DIV &= 0xFF if self.TAC & 0b100 == 0: # Check if timer is not enabled + self._cycles_to_interrupt = 1 << 16 return False self.TIMA_counter += cycles divider = self.dividers[self.TAC & 0b11] - if self.TIMA_counter >= divider: - self.TIMA_counter -= divider # Keeps possible remainder + ret = False + while self.TIMA_counter >= (1 << divider): + self.TIMA_counter -= (1 << divider) # Keeps possible remainder self.TIMA += 1 if self.TIMA > 0xFF: self.TIMA = self.TMA self.TIMA &= 0xFF - return True - - return False - - def cycles_to_interrupt(self): - if self.TAC & 0b100 == 0: # Check if timer is not enabled - # Large enough, that 'calculate_cycles' will choose 'x' - return 1 << 16 - - divider = self.dividers[self.TAC & 0b11] - - cyclesleft = ((0x100 - self.TIMA) * divider) - self.TIMA_counter - - return cyclesleft + ret = True + self._cycles_to_interrupt = ((0x100 - self.TIMA) << divider) - self.TIMA_counter + return ret def save_state(self, f): f.write(self.DIV) @@ -74,6 +72,7 @@ def save_state(self, f): f.write_16bit(self.TIMA_counter) f.write(self.TMA) f.write(self.TAC) + f.write_64bit(self.last_cycles) def load_state(self, f, state_version): self.DIV = f.read() @@ -82,3 +81,5 @@ def load_state(self, f, state_version): self.TIMA_counter = f.read_16bit() self.TMA = f.read() self.TAC = f.read() + if state_version >= 12: + self.last_cycles = f.read_64bit() diff --git a/pyboy/plugins/debug.py b/pyboy/plugins/debug.py index 012bbbca6..25520cad7 100644 --- a/pyboy/plugins/debug.py +++ b/pyboy/plugins/debug.py @@ -205,8 +205,14 @@ def handle_events(self, events): return events def stop(self): - if self.sdl2_event_pump: - sdl2.SDL_Quit() + self.tile1.stop() + self.tile2.stop() + self.tiledata0.stop() + if self.cgb: + self.tiledata1.stop() + self.sprite.stop() + self.spriteview.stop() + self.memory.stop() def enabled(self): if self.pyboy_argv.get("debug"): diff --git a/pyboy/plugins/debug_prompt.py b/pyboy/plugins/debug_prompt.py index b57e63f1e..144c16980 100644 --- a/pyboy/plugins/debug_prompt.py +++ b/pyboy/plugins/debug_prompt.py @@ -65,7 +65,7 @@ def handle_breakpoint(self): sym_label = self.rom_symbols.get(bank, {}).get(self.mb.cpu.PC, "") # Print state - print(self.mb.cpu.dump_state(sym_label)) + self.mb.cpu.dump_state() # REPL cmd = input() diff --git a/pyboy/pyboy.pxd b/pyboy/pyboy.pxd index 700647b68..b41afd0fa 100644 --- a/pyboy/pyboy.pxd +++ b/pyboy/pyboy.pxd @@ -43,9 +43,8 @@ cdef class PyBoy: cdef readonly str gamerom cdef readonly bint paused - cdef double avg_pre cdef double avg_tick - cdef double avg_post + cdef double avg_emu cdef readonly list events cdef list queued_input @@ -70,9 +69,10 @@ cdef class PyBoy: cdef list recorded_input cdef list external_input + cpdef tuple _single_step(self) noexcept @cython.locals(t_start=int64_t, t_pre=int64_t, t_tick=int64_t, t_post=int64_t, nsecs=int64_t) cpdef bint _tick(self, bint) noexcept - @cython.locals(running=bint) + @cython.locals(running=bint, factor=double) cpdef bint tick(self, count=*, render=*) noexcept cpdef void stop(self, save=*) noexcept cpdef int save_state(self, object) except -1 @@ -84,6 +84,7 @@ cdef class PyBoy: cpdef void _unpause(self) noexcept cdef void _update_window_title(self) noexcept cdef void _post_tick(self) noexcept + cdef void _post_handle_events(self) noexcept cdef dict _hooks cdef object symbols_file @@ -99,3 +100,5 @@ cdef class PyBoy: cpdef object get_sprite(self, int) noexcept cpdef list get_sprite_by_tile_identifier(self, list, on_screen=*) noexcept cpdef object get_tile(self, int) noexcept + + cpdef int64_t _cycles(self) diff --git a/pyboy/pyboy.py b/pyboy/pyboy.py index 5824fcdc3..ec747921f 100644 --- a/pyboy/pyboy.py +++ b/pyboy/pyboy.py @@ -8,7 +8,9 @@ import heapq import os +import queue import re +import threading import time import numpy as np @@ -53,6 +55,7 @@ def __init__( cgb=None, gameshark=None, log_level=defaults["log_level"], + jit=False, **kwargs ): """ @@ -144,6 +147,7 @@ def __init__( sound, sound_emulated, cgb, + jit, randomize=randomize, ) @@ -160,9 +164,8 @@ def __init__( raise KeyError(f"Unknown keyword argument: {k}") # Performance measures - self.avg_pre = 0 self.avg_tick = 0 - self.avg_post = 0 + self.avg_emu = 0 # Absolute frame count of the emulation self.frame_count = 0 @@ -374,13 +377,33 @@ def __init__( self.initialized = True + def _single_step(self): + self.mb.breakpoint_singlestep = True + self.mb.breakpoint_singlestep_latch = True + self.mb.tick() + return ( + self.mb.cpu.A, + self.mb.cpu.F, + self.mb.cpu.B, + self.mb.cpu.C, + self.mb.cpu.D, + self.mb.cpu.E, + self.mb.cpu.HL, + self.mb.cpu.SP, + self.mb.cpu.PC, + # TODO: Could be moved to test + self.mb.getitem(self.mb.cpu.PC + 1), + self.mb.getitem(self.mb.cpu.PC + 2), + self.mb.getitem(self.mb.cpu.HL), + self.mb.getitem(self.mb.cpu.HL + 1), + # Interrupts? + ) + def _tick(self, render): if self.stopped: return False - t_start = time.perf_counter_ns() self._handle_events(self.events) - t_pre = time.perf_counter_ns() if not self.paused: self.gameshark.tick() self.__rendering(render) @@ -407,18 +430,7 @@ def _tick(self, render): self.mb.breakpoint_singlestep = self.mb.breakpoint_singlestep_latch self.frame_count += 1 - t_tick = time.perf_counter_ns() - self._post_tick() - t_post = time.perf_counter_ns() - - nsecs = t_pre - t_start - self.avg_pre = 0.9 * self.avg_pre + (0.1*nsecs/1_000_000_000) - - nsecs = t_tick - t_pre - self.avg_tick = 0.9 * self.avg_tick + (0.1*nsecs/1_000_000_000) - - nsecs = t_post - t_tick - self.avg_post = 0.9 * self.avg_post + (0.1*nsecs/1_000_000_000) + self._post_handle_events() return not self.quitting @@ -467,11 +479,22 @@ def tick(self, count=1, render=True): False if emulation has ended otherwise True """ + _count = count running = False + t_start = time.perf_counter_ns() while count != 0: _render = render and count == 1 # Only render on last tick to improve performance running = self._tick(_render) count -= 1 + t_tick = time.perf_counter_ns() + self._post_tick() + t_post = time.perf_counter_ns() + + if _count > 0: + nsecs = t_tick - t_start + self.avg_tick = 0.9 * (self.avg_tick / _count) + (0.1*nsecs/1_000_000_000) + nsecs = t_post - t_start + self.avg_emu = 0.9 * (self.avg_emu / _count) + (0.1*nsecs/1_000_000_000) return running def _handle_events(self, events): @@ -538,6 +561,7 @@ def _post_tick(self): self._plugin_manager.post_tick() self._plugin_manager.frame_limiter(self.target_emulationspeed) + def _post_handle_events(self): # Prepare an empty list, as the API might be used to send in events between ticks self.events = [] while self.queued_input and self.frame_count == self.queued_input[0][0]: @@ -545,9 +569,8 @@ def _post_tick(self): self.events.append(WindowEvent(_event)) def _update_window_title(self): - avg_emu = self.avg_pre + self.avg_tick + self.avg_post - self.window_title = f"CPU/frame: {(self.avg_pre + self.avg_tick) / SPF * 100:0.2f}%" - self.window_title += f' Emulation: x{(round(SPF / avg_emu) if avg_emu > 0 else "INF")}' + self.window_title = f"CPU/frame: {(self.avg_tick) / SPF * 100:0.2f}%" + self.window_title += f' Emulation: x{(round(SPF / self.avg_emu) if self.avg_emu > 0 else "INF")}' if self.paused: self.window_title += "[PAUSED]" self.window_title += self._plugin_manager.window_title() @@ -1306,6 +1329,9 @@ def rtc_lock_experimental(self, enable): else: raise Exception("There's no RTC for this cartridge type") + def _cycles(self): + return self.mb.cpu.cycles + class PyBoyRegisterFile: """ diff --git a/pyboy/utils.py b/pyboy/utils.py index 8dee8a1bb..168844edc 100644 --- a/pyboy/utils.py +++ b/pyboy/utils.py @@ -5,7 +5,7 @@ __all__ = ["WindowEvent", "dec_to_bcd", "bcd_to_dec"] -STATE_VERSION = 11 +STATE_VERSION = 12 ############################################################## # Buffer classes diff --git a/pyproject.toml b/pyproject.toml index e79828f92..2f1c119a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,3 +82,8 @@ split_penalty_for_added_line_split = 30 split_before_logical_operator = false split_before_bitwise_operator = false arithmetic_precedence_indication = true + +[tool.pytest.ini_options] +filterwarnings = [ + "ignore:.*Using SDL2 binaries from pysdl2-dll.*", +] diff --git a/setup.py b/setup.py index 48c85323e..cb47db52d 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ def initialize_options(self): self.distribution.ext_modules = cythonize( [*cythonize_files], nthreads=thread_count, - annotate=False, + annotate=True, gdb_debug=False, language_level=3, compiler_directives={ @@ -82,7 +82,7 @@ def initialize_options(self): "nonecheck": False, "overflowcheck": False, # "profile" : True, - "wraparound": False, + # "wraparound": False, "legacy_implicit_noexcept": True, }, ) @@ -94,7 +94,7 @@ def prep_pxd_py_files(): # We also yield the py_files that have a .pxd file, as we feed these into the cythonize call. for root, dirs, files in os.walk(ROOT_DIR): for f in files: - if os.path.splitext(f)[1] in [".py", ".pyx"] and f not in ignore_py_files: + if os.path.splitext(f)[1] in [".py", ".pyx"] and f not in ignore_py_files and "_jit_" not in f: yield os.path.join(root, f) if os.path.splitext(f)[1] == ".pxd": py_file = os.path.join(root, os.path.splitext(f)[0]) + ".py" diff --git a/tests/test_acid_cgb.py b/tests/test_acid_cgb.py index 41f21c1c8..1a5e65db7 100644 --- a/tests/test_acid_cgb.py +++ b/tests/test_acid_cgb.py @@ -29,7 +29,7 @@ def test_cgb_acid(cgb_acid_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_acid_dmg.py b/tests/test_acid_dmg.py index adf942af1..08694a0a9 100644 --- a/tests/test_acid_dmg.py +++ b/tests/test_acid_dmg.py @@ -30,7 +30,7 @@ def test_dmg_acid(cgb, dmg_acid_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_basics.py b/tests/test_basics.py index 751f4ad85..88b1c862a 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -47,6 +47,12 @@ def test_log_level_critical(default_rom, capsys): assert captured.out == "" +def test_tick_zero(default_rom): + pyboy = PyBoy(default_rom, window="null") + # Not permitted, but shouldn't crash the emulator either + pyboy.tick(0) + + def test_register_file(default_rom): pyboy = PyBoy(default_rom, window="null") pyboy.set_emulation_speed(0) @@ -284,7 +290,7 @@ def test_all_modes(cgb, _bootrom, frames, rom, any_rom_cgb, boot_cgb_rom): old_image = PIL.Image.open(png_buf).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_blargg.py b/tests/test_blargg.py index eac9731a7..420a93ce9 100644 --- a/tests/test_blargg.py +++ b/tests/test_blargg.py @@ -20,7 +20,7 @@ blargg_json = "tests/test_results/blargg.json" -def run_rom(rom): +def run_rom(rom, max_frames): pyboy = PyBoy(str(rom), window="null", cgb="cgb" in rom, sound_emulated=True) pyboy.set_emulation_speed(0) t = time.time() @@ -31,7 +31,7 @@ def run_rom(rom): result += b t = time.time() - if pyboy._is_cpu_stuck(): + if pyboy._is_cpu_stuck() or pyboy.frame_count > max_frames: break pyboy.tick(10, False) @@ -51,69 +51,69 @@ def run_rom(rom): @pytest.mark.parametrize( - "test_rom", [ - "cgb_sound/cgb_sound.gb", - "cgb_sound/rom_singles/01-registers.gb", - "cgb_sound/rom_singles/02-len ctr.gb", - "cgb_sound/rom_singles/03-trigger.gb", - "cgb_sound/rom_singles/04-sweep.gb", - "cgb_sound/rom_singles/05-sweep details.gb", - "cgb_sound/rom_singles/06-overflow on trigger.gb", - "cgb_sound/rom_singles/07-len sweep period sync.gb", - "cgb_sound/rom_singles/08-len ctr during power.gb", - "cgb_sound/rom_singles/09-wave read while on.gb", - "cgb_sound/rom_singles/10-wave trigger while on.gb", - "cgb_sound/rom_singles/11-regs after power.gb", - "cgb_sound/rom_singles/12-wave.gb", - "cpu_instrs/cpu_instrs.gb", - "cpu_instrs/individual/01-special.gb", - "cpu_instrs/individual/02-interrupts.gb", - "cpu_instrs/individual/03-op sp,hl.gb", - "cpu_instrs/individual/04-op r,imm.gb", - "cpu_instrs/individual/05-op rp.gb", - "cpu_instrs/individual/06-ld r,r.gb", - "cpu_instrs/individual/07-jr,jp,call,ret,rst.gb", - "cpu_instrs/individual/08-misc instrs.gb", - "cpu_instrs/individual/09-op r,r.gb", - "cpu_instrs/individual/10-bit ops.gb", - "cpu_instrs/individual/11-op a,(hl).gb", - "dmg_sound/dmg_sound.gb", - "dmg_sound/rom_singles/01-registers.gb", - "dmg_sound/rom_singles/02-len ctr.gb", - "dmg_sound/rom_singles/03-trigger.gb", - "dmg_sound/rom_singles/04-sweep.gb", - "dmg_sound/rom_singles/05-sweep details.gb", - "dmg_sound/rom_singles/06-overflow on trigger.gb", - "dmg_sound/rom_singles/07-len sweep period sync.gb", - "dmg_sound/rom_singles/08-len ctr during power.gb", - "dmg_sound/rom_singles/09-wave read while on.gb", - "dmg_sound/rom_singles/10-wave trigger while on.gb", - "dmg_sound/rom_singles/11-regs after power.gb", - "dmg_sound/rom_singles/12-wave write while on.gb", - "instr_timing/instr_timing.gb", - "interrupt_time/interrupt_time.gb", - "mem_timing/individual/01-read_timing.gb", - "mem_timing/individual/02-write_timing.gb", - "mem_timing/individual/03-modify_timing.gb", - "mem_timing/mem_timing.gb", - "mem_timing-2/mem_timing.gb", - "mem_timing-2/rom_singles/01-read_timing.gb", - "mem_timing-2/rom_singles/02-write_timing.gb", - "mem_timing-2/rom_singles/03-modify_timing.gb", - "oam_bug/oam_bug.gb", - "oam_bug/rom_singles/1-lcd_sync.gb", - "oam_bug/rom_singles/2-causes.gb", - "oam_bug/rom_singles/3-non_causes.gb", - "oam_bug/rom_singles/4-scanline_timing.gb", - "oam_bug/rom_singles/5-timing_bug.gb", - "oam_bug/rom_singles/6-timing_no_bug.gb", - "oam_bug/rom_singles/7-timing_effect.gb", - "oam_bug/rom_singles/8-instr_effect.gb", + "test_rom, max_frames", [ + ("cgb_sound/cgb_sound.gb", 4_000), + ("cgb_sound/rom_singles/01-registers.gb", 700), + ("cgb_sound/rom_singles/02-len ctr.gb", 700), + ("cgb_sound/rom_singles/03-trigger.gb", 700), + ("cgb_sound/rom_singles/04-sweep.gb", 700), + ("cgb_sound/rom_singles/05-sweep details.gb", 700), + ("cgb_sound/rom_singles/06-overflow on trigger.gb", 700), + ("cgb_sound/rom_singles/07-len sweep period sync.gb", 700), + ("cgb_sound/rom_singles/08-len ctr during power.gb", 700), + ("cgb_sound/rom_singles/09-wave read while on.gb", 700), + ("cgb_sound/rom_singles/10-wave trigger while on.gb", 700), + ("cgb_sound/rom_singles/11-regs after power.gb", 700), + ("cgb_sound/rom_singles/12-wave.gb", 700), + ("cpu_instrs/cpu_instrs.gb", 4_000), + ("cpu_instrs/individual/01-special.gb", 700), + ("cpu_instrs/individual/02-interrupts.gb", 700), + ("cpu_instrs/individual/03-op sp,hl.gb", 700), + ("cpu_instrs/individual/04-op r,imm.gb", 700), + ("cpu_instrs/individual/05-op rp.gb", 700), + ("cpu_instrs/individual/06-ld r,r.gb", 700), + ("cpu_instrs/individual/07-jr,jp,call,ret,rst.gb", 700), + ("cpu_instrs/individual/08-misc instrs.gb", 700), + ("cpu_instrs/individual/09-op r,r.gb", 700), + ("cpu_instrs/individual/10-bit ops.gb", 2_000), + ("cpu_instrs/individual/11-op a,(hl).gb", 2_000), + ("dmg_sound/dmg_sound.gb", 700), + ("dmg_sound/rom_singles/01-registers.gb", 700), + ("dmg_sound/rom_singles/02-len ctr.gb", 700), + ("dmg_sound/rom_singles/03-trigger.gb", 700), + ("dmg_sound/rom_singles/04-sweep.gb", 700), + ("dmg_sound/rom_singles/05-sweep details.gb", 700), + ("dmg_sound/rom_singles/06-overflow on trigger.gb", 700), + ("dmg_sound/rom_singles/07-len sweep period sync.gb", 700), + ("dmg_sound/rom_singles/08-len ctr during power.gb", 700), + ("dmg_sound/rom_singles/09-wave read while on.gb", 700), + ("dmg_sound/rom_singles/10-wave trigger while on.gb", 700), + ("dmg_sound/rom_singles/11-regs after power.gb", 700), + ("dmg_sound/rom_singles/12-wave write while on.gb", 700), + ("instr_timing/instr_timing.gb", 2_000), + ("interrupt_time/interrupt_time.gb", 700), + ("mem_timing/individual/01-read_timing.gb", 700), + ("mem_timing/individual/02-write_timing.gb", 700), + ("mem_timing/individual/03-modify_timing.gb", 700), + ("mem_timing/mem_timing.gb", 700), + ("mem_timing-2/mem_timing.gb", 700), + ("mem_timing-2/rom_singles/01-read_timing.gb", 700), + ("mem_timing-2/rom_singles/02-write_timing.gb", 700), + ("mem_timing-2/rom_singles/03-modify_timing.gb", 700), + ("oam_bug/oam_bug.gb", 2_000), + ("oam_bug/rom_singles/1-lcd_sync.gb", 700), + ("oam_bug/rom_singles/2-causes.gb", 700), + ("oam_bug/rom_singles/3-non_causes.gb", 700), + ("oam_bug/rom_singles/4-scanline_timing.gb", 700), + ("oam_bug/rom_singles/5-timing_bug.gb", 700), + ("oam_bug/rom_singles/6-timing_no_bug.gb", 700), + ("oam_bug/rom_singles/7-timing_effect.gb", 700), + ("oam_bug/rom_singles/8-instr_effect.gb", 700), ] ) -def test_blarggs(test_rom, blargg_dir): +def test_blarggs(test_rom, max_frames, blargg_dir): rom = str(blargg_dir / Path(test_rom)) - result = run_rom(rom) + result = run_rom(rom, max_frames) if os.path.isfile(blargg_json): with open(blargg_json, "r") as f: diff --git a/tests/test_breakpoints.py b/tests/test_breakpoints.py index 10e75b121..52f2512fc 100644 --- a/tests/test_breakpoints.py +++ b/tests/test_breakpoints.py @@ -254,7 +254,7 @@ def test_data_hooking_failure(default_rom): image1 = pyboy1.screen.image.convert("RGB") image2 = pyboy2.screen.image.convert("RGB") diff = PIL.ImageChops.difference(image1, image2) - if not diff.getbbox() and not os.environ.get("TEST_CI"): + if not diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image1.show() image2.show() diff.show() diff --git a/tests/test_game_wrapper_pokemon_pinball.py b/tests/test_game_wrapper_pokemon_pinball.py index bc693f647..c5f142111 100644 --- a/tests/test_game_wrapper_pokemon_pinball.py +++ b/tests/test_game_wrapper_pokemon_pinball.py @@ -67,37 +67,37 @@ def test_pokemon_catch_mode(pokemon_pinball_rom): pokemon_pinball.start_game(stage=Stage.RED_BOTTOM, timer_div=0x00) pyboy.button_press("a") pyboy.button_press("left") - pyboy.tick(50) + pyboy.tick(50, False) pokemon_pinball.start_catch_mode() - pyboy.tick(270) + pyboy.tick(270, False) pyboy.button_release("left") pyboy.button_release("a") pyboy.button("select") - pyboy.tick(20) + pyboy.tick(20, False) pyboy.button_press("left") pyboy.button_press("a") - pyboy.tick(500) + pyboy.tick(500, False) pyboy.button_release("left") - pyboy.tick(21) + pyboy.tick(21, False) pyboy.button_press("left") - pyboy.tick(100) + pyboy.tick(100, False) pyboy.button_release("a") - pyboy.tick(31) + pyboy.tick(31, False) pyboy.button_press("a") - pyboy.tick(200) + pyboy.tick(200, False) pyboy.button_release("left") - pyboy.tick(31) + pyboy.tick(31, False) pyboy.button_press("left") - pyboy.tick(200) + pyboy.tick(200, False) pyboy.button_release("left") - pyboy.tick(31) + pyboy.tick(31, False) pyboy.button_press("left") - pyboy.tick(400) + pyboy.tick(400, False) # NOTE: This sequence broke because of changed instruction timings - assert pokemon_pinball.score == 15635100 - assert pokemon_pinball.has_pokemon(Pokemon.BULBASAUR) - assert pokemon_pinball.has_pokemon(Pokemon.CHARMANDER) == False - assert pokemon_pinball.get_unique_pokemon_caught() == 1 + assert pokemon_pinball.score == 9030100 + assert not pokemon_pinball.has_pokemon(Pokemon.BULBASAUR) + assert not pokemon_pinball.has_pokemon(Pokemon.CHARMANDER) + assert pokemon_pinball.get_unique_pokemon_caught() == 0 pyboy.stop(False) diff --git a/tests/test_jit.py b/tests/test_jit.py new file mode 100644 index 000000000..586559580 --- /dev/null +++ b/tests/test_jit.py @@ -0,0 +1,83 @@ +# +# License: See LICENSE.md file +# GitHub: https://github.com/Baekalfen/PyBoy +# + +from pyboy import PyBoy + + +def test_jit_tick(default_rom): + pyboy = PyBoy(default_rom, window="null", jit=False, debug=False) + pyboy_jit = PyBoy(default_rom, window="null", jit=True, debug=False, log_level="DEBUG") + pyboy_jit.set_emulation_speed(0) + pyboy.set_emulation_speed(0) + + for tick in range(60 * 10): + pyboy_jit.tick() + pyboy.tick() + + fail = False + for addr in range(0xFFFF): + a = pyboy.memory[addr] + b = pyboy_jit.memory[addr] + if a != b: + print(f"{tick=} address: 0x{addr:04x} {a}!={b}") + fail = True + assert pyboy._cycles() == pyboy_jit._cycles(), pyboy._cycles() - pyboy_jit._cycles() + assert not fail + + +# def test_jit_LY(default_rom): +# pyboy_jit = PyBoy(default_rom, window="null", jit=True, debug=False, log_level="ERROR") + +# asm = """ +# spin: +# ld A, (LY) +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# nop +# jp spin +# """ + +# for tick in range(60 * 10): +# pyboy_jit.tick() + + +def _registers(registers): + A, F, B, C, D, E, HL, SP, PC, PC_1, PC_2, HL_0, HL_1 = registers + return f"{A=:02x}, {F=:02x}, {B=:02x}, {C=:02x}, {D=:02x}, {E=:02x}, {HL=:04x}, {SP=:04x}, {PC=:04x}, {PC_1=:04x}, {PC_2=:04x}, {HL_0=:02x}, {HL_1=:02x}" + + +def test_jit_single_step(default_rom): + pyboy = PyBoy(default_rom, window="null", jit=False, debug=False) + pyboy_jit = PyBoy(default_rom, window="null", jit=True, debug=False, log_level="DEBUG") + pyboy_jit.set_emulation_speed(0) + pyboy.set_emulation_speed(0) + + for step in range(1_000_000): + registers_jit = pyboy_jit._single_step() + registers = pyboy._single_step() + + if registers_jit != registers: + print( + "Fixing cycles?", pyboy._cycles(), pyboy_jit._cycles(), "\n", _registers(registers), "\n", + _registers(registers_jit) + ) + # breakpoint() + while pyboy._cycles() < pyboy_jit._cycles(): + registers = pyboy._single_step() + print("Inc non-jit", "\n", _registers(registers)) + assert registers == registers_jit + assert pyboy._cycles() == pyboy_jit._cycles() + else: + print( + "Matching cycles:", pyboy._cycles(), pyboy_jit._cycles(), "\n", _registers(registers), "\n", + _registers(registers_jit) + ) + # breakpoint() diff --git a/tests/test_magen.py b/tests/test_magen.py index e3999bd9f..cee03f386 100644 --- a/tests/test_magen.py +++ b/tests/test_magen.py @@ -29,7 +29,7 @@ def test_magen_test(magen_test_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_mooneye.py b/tests/test_mooneye.py index c5bf9b705..a70655181 100644 --- a/tests/test_mooneye.py +++ b/tests/test_mooneye.py @@ -185,7 +185,7 @@ def test_mooneye(clean, rom, mooneye_dir, default_rom): else: diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_replay.py b/tests/test_replay.py index d855def23..5ccebe3e6 100644 --- a/tests/test_replay.py +++ b/tests/test_replay.py @@ -31,7 +31,7 @@ def verify_screen_image_np(pyboy, saved_array): match = np.all(np.frombuffer(saved_array, dtype=np.uint8).reshape(144, 160, 3) == pyboy.screen.ndarray) - if not match and not os.environ.get("TEST_CI"): + if not match and os.environ.get("TEST_VERBOSE_IMAGES"): from PIL import Image original = Image.frombytes("RGB", (160, 144), np.frombuffer(saved_array, dtype=np.uint8).reshape(144, 160, 3)) original.show() diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_align.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_align.gb.png index e6601e48a..e1323bb00 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_align.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_align.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_align_cpu.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_align_cpu.gb.png index 82a17ed6d..10551d821 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_align_cpu.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_align_cpu.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_delay.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_delay.gb.png index 6b03ae7a0..754f818fb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_duty.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_duty.gb.png index 66d4896a2..254772b1a 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_duty.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_duty.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_duty_delay.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_duty_delay.gb.png index b9f7a35f2..46c209328 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_duty_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_duty_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_extra_length_clocking-cgb0B.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_extra_length_clocking-cgb0B.gb.png index 4770cfb19..9b6c97edd 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_extra_length_clocking-cgb0B.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_extra_length_clocking-cgb0B.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change.gb.png index 897eb9893..abd3dfdae 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-A.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-A.gb.png index cc7ba7e25..19b1c2fa6 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-A.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-A.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgb0BC.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgb0BC.gb.png index cc7ba7e25..19b1c2fa6 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgb0BC.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgb0BC.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgbDE.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgbDE.gb.png index 087098910..4f7355e7b 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgbDE.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_freq_change_timing-cgbDE.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_glitch.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_glitch.gb.png index 9fcc8d1fe..6689f17eb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_glitch.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_glitch.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_speed_change.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_speed_change.gb.png index 955eced02..62cddba8d 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_speed_change.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_nrx2_speed_change.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_restart.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_restart.gb.png index 8999fc17c..0c220dc53 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_restart_nrx2_glitch.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_restart_nrx2_glitch.gb.png index 12fcb0e2f..a5f936def 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_restart_nrx2_glitch.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_restart_nrx2_glitch.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_div.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_div.gb.png index dc54c3cd6..31a526375 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_div.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_restart.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_restart.gb.png index 005fdf1f3..c0bcf5904 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_stop_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep.gb.png index 0c933ecb3..8e38dea72 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart.gb.png index f28a3f197..e51dc2d4f 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart_2.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart_2.gb.png index 3b7ee8746..1980e20ae 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart_2.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_sweep_restart_2.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_volume.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_volume.gb.png index e662d1623..5628be2bb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_volume.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_volume.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_1/channel_1_volume_div.gb.png b/tests/test_results/SameSuite/apu/channel_1/channel_1_volume_div.gb.png index 290698e66..1f34b7ecd 100644 Binary files a/tests/test_results/SameSuite/apu/channel_1/channel_1_volume_div.gb.png and b/tests/test_results/SameSuite/apu/channel_1/channel_1_volume_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_align.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_align.gb.png index e6601e48a..e1323bb00 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_align.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_align.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_align_cpu.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_align_cpu.gb.png index 82a17ed6d..10551d821 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_align_cpu.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_align_cpu.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_delay.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_delay.gb.png index 6b03ae7a0..754f818fb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_duty.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_duty.gb.png index 66d4896a2..254772b1a 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_duty.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_duty.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_duty_delay.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_duty_delay.gb.png index b9f7a35f2..46c209328 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_duty_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_duty_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_extra_length_clocking-cgb0B.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_extra_length_clocking-cgb0B.gb.png index d6e02f5e8..276a0db09 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_extra_length_clocking-cgb0B.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_extra_length_clocking-cgb0B.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_freq_change.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_freq_change.gb.png index 897eb9893..abd3dfdae 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_freq_change.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_freq_change.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_glitch.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_glitch.gb.png index 9fcc8d1fe..6689f17eb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_glitch.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_glitch.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_speed_change.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_speed_change.gb.png index 955eced02..62cddba8d 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_speed_change.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_nrx2_speed_change.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_restart.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_restart.gb.png index 8999fc17c..0c220dc53 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_restart_nrx2_glitch.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_restart_nrx2_glitch.gb.png index 12fcb0e2f..a5f936def 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_restart_nrx2_glitch.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_restart_nrx2_glitch.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_div.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_div.gb.png index 1eb47c878..ce09220cd 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_div.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_restart.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_restart.gb.png index 005fdf1f3..c0bcf5904 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_stop_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_volume.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_volume.gb.png index 03275e104..922d05220 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_volume.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_volume.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_2/channel_2_volume_div.gb.png b/tests/test_results/SameSuite/apu/channel_2/channel_2_volume_div.gb.png index 487a18a96..c1ff595cc 100644 Binary files a/tests/test_results/SameSuite/apu/channel_2/channel_2_volume_div.gb.png and b/tests/test_results/SameSuite/apu/channel_2/channel_2_volume_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_and_glitch.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_and_glitch.gb.png index 6dac40500..f6bdce679 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_and_glitch.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_and_glitch.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_delay.gb.png index 3d42c450c..3f8ee77d5 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgb0.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgb0.gb.png index a486d6c0d..284dd155d 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgb0.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgb0.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgbB.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgbB.gb.png index 6f909c62f..1db7e34b7 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgbB.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_extra_length_clocking-cgbB.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_first_sample.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_first_sample.gb.png index f2ca901cf..4e03aa574 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_first_sample.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_first_sample.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_freq_change_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_freq_change_delay.gb.png index 6f0152dd6..f0b6a9560 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_freq_change_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_freq_change_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_delay.gb.png index 259d9f5ae..c4ee58a79 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_during_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_during_delay.gb.png index 9a1241f81..02ebf2603 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_during_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_during_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_stop_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_stop_delay.gb.png index 3eeb53263..87ed49354 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_stop_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_restart_stop_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_delay.gb.png index 259d9f5ae..c4ee58a79 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_skip_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_skip_delay.gb.png index f2ca901cf..4e03aa574 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_skip_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_shift_skip_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_delay.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_delay.gb.png index 2de250673..d315c9946 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_div.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_div.gb.png index 3353d8224..4a1afadd8 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_div.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_stop_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_dac_on_rw.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_dac_on_rw.gb.png index a84ad4391..1263c3b37 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_dac_on_rw.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_dac_on_rw.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_locked_write.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_locked_write.gb.png index 7310a68b8..1c8338256 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_locked_write.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_locked_write.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_sync.gb.png b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_sync.gb.png index cfbb95cf6..0e22fcadb 100644 Binary files a/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_sync.gb.png and b/tests/test_results/SameSuite/apu/channel_3/channel_3_wave_ram_sync.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_align.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_align.gb.png index 7823f8d1c..c841b25c1 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_align.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_align.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_delay.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_delay.gb.png index e2236b163..62eb51311 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_delay.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_delay.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_equivalent_frequencies.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_equivalent_frequencies.gb.png index 6d032ba3f..ddd9971ee 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_equivalent_frequencies.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_equivalent_frequencies.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_extra_length_clocking-cgb0B.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_extra_length_clocking-cgb0B.gb.png index afdd0f77c..adf5da7d3 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_extra_length_clocking-cgb0B.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_extra_length_clocking-cgb0B.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_freq_change.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_freq_change.gb.png index aa9b9169a..3c7b1be2f 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_freq_change.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_freq_change.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_frequency_alignment.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_frequency_alignment.gb.png index 751a11b29..5d9ee0dec 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_frequency_alignment.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_frequency_alignment.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr.gb.png index 9a4ca6a49..92e5341cd 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr15.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr15.gb.png index 8ea5179f9..101ebe4dd 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr15.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr15.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_15_7.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_15_7.gb.png index 9401ace66..dc1a67d25 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_15_7.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_15_7.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_7_15.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_7_15.gb.png index ec4760642..4368832c3 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_7_15.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_7_15.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart.gb.png index 640db3d07..73ad2b7b7 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart_fast.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart_fast.gb.png index 640db3d07..73ad2b7b7 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart_fast.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_lfsr_restart_fast.gb.png differ diff --git a/tests/test_results/SameSuite/apu/channel_4/channel_4_volume_div.gb.png b/tests/test_results/SameSuite/apu/channel_4/channel_4_volume_div.gb.png index bc1c2885e..596167509 100644 Binary files a/tests/test_results/SameSuite/apu/channel_4/channel_4_volume_div.gb.png and b/tests/test_results/SameSuite/apu/channel_4/channel_4_volume_div.gb.png differ diff --git a/tests/test_results/SameSuite/apu/div_trigger_volume_10.gb.png b/tests/test_results/SameSuite/apu/div_trigger_volume_10.gb.png index ccfcd887a..6067c133f 100644 Binary files a/tests/test_results/SameSuite/apu/div_trigger_volume_10.gb.png and b/tests/test_results/SameSuite/apu/div_trigger_volume_10.gb.png differ diff --git a/tests/test_results/SameSuite/apu/div_write_trigger.gb.png b/tests/test_results/SameSuite/apu/div_write_trigger.gb.png index 6338b9a90..5ee42b547 100644 Binary files a/tests/test_results/SameSuite/apu/div_write_trigger.gb.png and b/tests/test_results/SameSuite/apu/div_write_trigger.gb.png differ diff --git a/tests/test_results/SameSuite/apu/div_write_trigger_10.gb.png b/tests/test_results/SameSuite/apu/div_write_trigger_10.gb.png index cb6b4821b..c62355ca7 100644 Binary files a/tests/test_results/SameSuite/apu/div_write_trigger_10.gb.png and b/tests/test_results/SameSuite/apu/div_write_trigger_10.gb.png differ diff --git a/tests/test_results/SameSuite/apu/div_write_trigger_volume.gb.png b/tests/test_results/SameSuite/apu/div_write_trigger_volume.gb.png index ccfcd887a..6067c133f 100644 Binary files a/tests/test_results/SameSuite/apu/div_write_trigger_volume.gb.png and b/tests/test_results/SameSuite/apu/div_write_trigger_volume.gb.png differ diff --git a/tests/test_results/SameSuite/apu/div_write_trigger_volume_10.gb.png b/tests/test_results/SameSuite/apu/div_write_trigger_volume_10.gb.png index ccfcd887a..6067c133f 100644 Binary files a/tests/test_results/SameSuite/apu/div_write_trigger_volume_10.gb.png and b/tests/test_results/SameSuite/apu/div_write_trigger_volume_10.gb.png differ diff --git a/tests/test_results/blargg.json b/tests/test_results/blargg.json index d82a5c967..3a2ef5e18 100644 --- a/tests/test_results/blargg.json +++ b/tests/test_results/blargg.json @@ -20,20 +20,20 @@ "blargg/dmg_sound/rom_singles/06-overflow on trigger.gb": "06-overflow on trigger\n\n7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF \n8D7112A4 \nFailed\n", "blargg/dmg_sound/rom_singles/07-len sweep period sync.gb": "07-len sweep period sync\n\n\nLength period is wrong\n\nFailed #2\n", "blargg/dmg_sound/rom_singles/08-len ctr during power.gb": "08-len ctr during power\n\n00 00 00 00 2144DF1C \nFailed\n", - "blargg/dmg_sound/rom_singles/09-wave read while on.gb": "09-wave read while on\n\n55 33 11 00 00 FF FF 00 11 11 33 88 BB CC 44 55 BB 11 DD DD 44 33 BB BB DD 77 11 CC CC 33 33 00 EE CC BB 88 77 77 77 88 99 BB DD 00 33 77 BB 00 55 DD 33 AA 99 11 99 22 BB 55 FF AA 55 11 DD 77 44 22 00 11 22 0652FAE5 \nFailed\n", + "blargg/dmg_sound/rom_singles/09-wave read while on.gb": "09-wave read while on\n\n88 33 11 00 FF FF FF 00 11 44 66 99 CC DD 55 AA AA FF 55 22 99 AA AA 33 CC 66 77 CC CC 33 FF CC 99 99 55 55 33 33 33 44 55 77 CC FF 22 66 CC 33 EE 44 11 88 FF 77 FF 88 11 BB 55 00 BB 33 CC 99 88 44 22 00 FF D075E68F \nFailed\n", "blargg/dmg_sound/rom_singles/10-wave trigger while on.gb": "10-wave trigger while on\n\n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99", "blargg/dmg_sound/rom_singles/11-regs after power.gb": "11-regs after power\n\n\nPowering off should clear NR13\n\nFailed #3\n", - "blargg/dmg_sound/rom_singles/12-wave write while on.gb": "12-wave write while on\n\n00 11 22 33 44 F7 66 77 88 99 AA BB CC DD EE FF \n00 11 22 F7 44 55 66 77 88 99 AA BB CC DD EE FF \n00 F7 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \nF7 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 A", - "blargg/instr_timing/instr_timing.gb": "instr_timing\n\n01:255-3 02:254-2 03:254-2 06:254-2 09:254-2 0A:254-2 0B:254-2 0E:254-2 11:255-3 12:254-2 13:254-2 16:254-2 18:255-3 19:254-2 1A:254-2 1B:254-2 1E:254-2 20:254-2 20:255-3 21:255-3 22:254-2 23:254-2 26:254-2 28:254-2 28:255-3 29:254-2 2A:254-2 2B:254-2 2E:254-2 30:254-2 30:255-3 31:255-3 32:254-2 33:254-2 34:255-3 35:255-3 36:255-3 38:254-2 38:255-3 39:254-2 3A:254-2 3B:254-2 3E:254-2 46:254-2 4E:254-2 56:254-2 5E:254-2 66:254-2 6E:254-2 70:254-2 71:254-2 72:254-2 73:254-2 74:254-2 75:254-2 77:254-2 7E:254-2 86:254-2 8E:254-2 96:254-2 9E:254-2 A6:254-2 AE:254-2 B6:254-2 BE:254-2 C0:254-2 C1:255-3 C2:255-3 C4:255-3 C4:2-6 C6:254-2 C8:254-2 CA:255-3 CC:255-3 CC:2-6 CD:2-6 CE:254-2 D0:254-2 D1:255-3 D2:255-3 D4:255-3 D4:2-6 D6:254-2 D8:254-2 DA:255-3 DC:255-3 DC:2-6 DE:254-2 E0:255-3 E1:255-3 E2:254-2 E6:254-2 EE:254-2 F0:255-3 F1:255-3 F2:254-2 F6:254-2 F8:255-3 F9:254-2 FE:254-2 CB 00:254-2 CB 01:254-2 CB 02:254-2 CB 03:254-2 CB 04:254-2 CB 05:254-2 CB 07:254-2 CB 08:254-2 CB 09:254-2 CB 0A:254-2 CB 0B:254-2 CB 0C:254-2 CB 0D:254-2 CB 0F:254-2 CB 10:254-2 CB 11:254-2 CB 12:254-2 CB 13:254-2 CB 14:254-2 CB 15:254-2 CB 17:254-2 CB 18:254-2 CB 19:254-2 CB 1A:254-2 CB 1B:254-2 CB 1C:254-2 CB 1D:254-2 CB 1F:254-2 CB 20:254-2 CB 21:254-2 CB 22:254-2 CB 23:254-2 CB 24:254-2 CB 25:254-2 CB 27:254-2 CB 28:254-2 CB 29:254-2 CB 2A:254-2 CB 2B:254-2 CB 2C:254-2 CB 2D:254-2 CB 2F:254-2 CB 30:254-2 CB 31:254-2 CB 32:254-2 CB 33:254-2 CB 34:254-2 CB 35:254-2 CB 37:254-2 CB 38:254-2 CB 39:254-2 CB 3A:254-2 CB 3B:254-2 CB 3C:254-2 CB 3D:254-2 CB 3F:254-2 CB 40:254-2 CB 41:254-2 CB 42:254-2 CB 43:254-2 CB 44:254-2 CB 45:254-2 CB 46:4-3 CB 47:254-2 CB 48:254-2 CB 49:254-2 CB 4A:254-2 CB 4B:254-2 CB 4C:254-2 CB 4D:254-2 CB 4E:4-3 CB 4F:254-2 CB 50:254-2 CB 51:254-2 CB 52:254-2 CB 53:254-2 CB 54:254-2 CB 55:254-2 CB 56:4-3 CB 57:254-2 CB 58:254-2 CB 59:254-2 CB 5A:254-2 CB 5B:254-2 CB 5C:254-2 CB 5D:254-2 CB 5E:4-3 CB 5F:254-2 CB 60:254-2 CB 61:254-2 CB 62:254-2 CB 63:254-2 CB 64:254-2 CB 65:254-2 CB 66:4-3 CB 67:254-2 CB 68:254-2 CB 69:254-2 CB 6A:254-2 CB 6B:254-2 CB 6C:254-2 CB 6D:254-2 CB 6E:4-3 CB 6F:254-2 CB 70:254-2 CB 71:254-2 CB 72:254-2 CB 73:254-2 CB 74:254-2 CB 75:254-2 CB 76:4-3 CB 77:254-2 CB 78:254-2 CB 79:254-2 CB 7A:254-2 CB 7B:254-2 CB 7C:254-2 CB 7D:254-2 CB 7E:4-3 CB 7F:254-2 CB 80:254-2 CB 81:254-2 CB 82:254-2 CB 83:254-2 CB 84:254-2 CB 85:254-2 CB 87:254-2 CB 88:254-2 CB 89:254-2 CB 8A:254-2 CB 8B:254-2 CB 8C:254-2 CB 8D:254-2 CB 8F:254-2 CB 90:254-2 CB 91:254-2 CB 92:254-2 CB 93:254-2 CB 94:254-2 CB 95:254-2 CB 97:254-2 CB 98:254-2 CB 99:254-2 CB 9A:254-2 CB 9B:254-2 CB 9C:254-2 CB 9D:254-2 CB 9F:254-2 CB A0:254-2 CB A1:254-2 CB A2:254-2 CB A3:254-2 CB A4:254-2 CB A5:254-2 CB A7:254-2 CB A8:254-2 CB A9:254-2 CB AA:254-2 CB AB:254-2 CB AC:254-2 CB AD:254-2 CB AF:254-2 CB B0:254-2 CB B1:254-2 CB B2:254-2 CB B3:254-2 CB B4:254-2 CB B5:254-2 CB B7:254-2 CB B8:254-2 CB B9:254-2 CB BA:254-2 CB BB:254-2 CB BC:254-2 CB BD:254-2 CB BF:254-2 CB C0:254-2 CB C1:254-2 CB C2:254-2 CB C3:254-2 CB C4:254-2 CB C5:254-2 CB C7:254-2 CB C8:254-2 CB C9:254-2 CB CA:254-2 CB CB:254-2 CB CC:254-2 CB CD:254-2 CB CF:254-2 CB D0:254-2 CB D1:254-2 CB D2:254-2 CB D3:254-2 CB D4:254-2 CB D5:254-2 CB D7:254-2 CB D8:254-2 CB D9:254-2 CB DA:254-2 CB DB:254-2 CB DC:254-2 CB DD:254-2 CB DF:254-2 CB E0:254-2 CB E1:254-2 CB E2:254-2 CB E3:254-2 CB E4:254-2 CB E5:254-2 CB E7:254-2 CB E8:254-2 CB E9:254-2 CB EA:254-2 CB EB:254-2 CB EC:254-2 CB ED:254-2 CB EF:254-2 CB F0:254-2 CB F1:254-2 CB F2:254-2 CB F3:254-2 CB F4:254-2 CB F5:254-2 CB F7:254-2 CB F8:254-2 CB F9:254-2 CB FA:254-2 CB FB:254-2 CB FC:254-2 CB FD:254-2 CB FF:254-2 \nFailed\n", - "blargg/interrupt_time/interrupt_time.gb": "interrupt time\n\n00 00 FC \n00 08 04 \n00 00 FC \n00 08 04 \n550B72D0 \nFailed\n", - "blargg/mem_timing/individual/01-read_timing.gb": "01-read_timing\n\nF0:2-3 FA:2-4 CB 46:2-3 CB 4E:2-3 CB 56:2-3 CB 5E:2-3 CB 66:2-3 CB 6E:2-3 CB 76:2-3 CB 7E:2-3 \nFailed\n", - "blargg/mem_timing/individual/02-write_timing.gb": "02-write_timing\n\n36:2-3 E0:2-3 EA:2-4 \nFailed\n", - "blargg/mem_timing/individual/03-modify_timing.gb": "03-modify_timing\n\n35:0/0-2/3\n34:0/0-2/3\nCB 06:0/0-3/4\nCB 0E:0/0-3/4\nCB 16:0/0-3/4\nCB 1E:0/0-3/4\nCB 26:0/0-3/4\nCB 2E:0/0-3/4\nCB 36:0/0-3/4\nCB 3E:0/0-3/4\nCB 86:0/0-3/4\nCB 8E:0/0-3/4\nCB 96:0/0-3/4\nCB 9E:0/0-3/4\nCB A6:0/0-3/4\nCB AE:0/0-3/4\nCB B6:0/0-3/4\nCB BE:0/0-3/4\nCB C6:0/0-3/4\nCB CE:0/0-3/4\nCB D6:0/0-3/4\nCB DE:0/0-3/4\nCB E6:0/0-3/4\nCB EE:0/0-3/4\nCB F6:0/0-3/4\nCB FE:0/0-3/4\n\nFailed\n", - "blargg/mem_timing/mem_timing.gb": "mem_timing\n\n01:01 02:01 03:01 \n\nFailed 3 tests.\n", - "blargg/mem_timing-2/mem_timing.gb": "mem_timing\n\n01:01 02:01 03:01 \n\nRun failed tests\nindividually for\nmore details.\n\nFailed\n", - "blargg/mem_timing-2/rom_singles/01-read_timing.gb": "01-read_timing\n\nF0:2-3 FA:2-4 CB 46:2-3 CB 4E:2-3 CB 56:2-3 CB 5E:2-3 CB 66:2-3 CB 6E:2-3 CB 76:2-3 CB 7E:2-3 \nFailed\n", - "blargg/mem_timing-2/rom_singles/02-write_timing.gb": "02-write_timing\n\n36:2-3 E0:2-3 EA:2-4 \nFailed\n", - "blargg/mem_timing-2/rom_singles/03-modify_timing.gb": "03-modify_timing\n\n35:0/0-2/3\n34:0/0-2/3\nCB 06:0/0-3/4\nCB 0E:0/0-3/4\nCB 16:0/0-3/4\nCB 1E:0/0-3/4\nCB 26:0/0-3/4\nCB 2E:0/0-3/4\nCB 36:0/0-3/4\nCB 3E:0/0-3/4\nCB 86:0/0-3/4\nCB 8E:0/0-3/4\nCB 96:0/0-3/4\nCB 9E:0/0-3/4\nCB A6:0/0-3/4\nCB AE:0/0-3/4\nCB B6:0/0-3/4\nC", + "blargg/dmg_sound/rom_singles/12-wave write while on.gb": "12-wave write while on\n\n00 11 22 33 44 F7 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 F7 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 F7 55 66 77 88 99 AA BB CC DD EE FF \n00 F7 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \nF7 11 22 33 44 55 66 77 88 99 A", + "blargg/instr_timing/instr_timing.gb": "instr_timing\n\n\nPassed\n", + "blargg/interrupt_time/interrupt_time.gb": "interrupt time\n\n00 00 00 \n00 08 08 \n00 00 00 \n00 08 08 \n5DDD9187 \nFailed\n", + "blargg/mem_timing/individual/01-read_timing.gb": "01-read_timing\n\n\nPassed\n", + "blargg/mem_timing/individual/02-write_timing.gb": "02-write_timing\n\n\nPassed\n", + "blargg/mem_timing/individual/03-modify_timing.gb": "03-modify_timing\n\n\nPassed\n", + "blargg/mem_timing/mem_timing.gb": "mem_timing\n\n01:ok 02:ok 03:ok \n\nPassed all tests\n", + "blargg/mem_timing-2/mem_timing.gb": "mem_timing\n\n01:ok 02:ok 03:ok \n\nPassed\n", + "blargg/mem_timing-2/rom_singles/01-read_timing.gb": "01-read_timing\n\n\nPassed\n", + "blargg/mem_timing-2/rom_singles/02-write_timing.gb": "02-write_timing\n\n\nPassed\n", + "blargg/mem_timing-2/rom_singles/03-modify_timing.gb": "03-modify_timing\n\n\nPassed\n", "blargg/oam_bug/oam_bug.gb": "oam_bug\n\n01:03 02:02 03:ok 04:03 05:02 06:ok 07:01 08:02 \n\nRun failed tests\nindividually for\nmore details.\n\nFailed\n", "blargg/oam_bug/rom_singles/1-lcd_sync.gb": "1-lcd_sync\n\n\nTurning LCD on starts too early in scanline\n\nFailed #3\n", "blargg/oam_bug/rom_singles/2-causes.gb": "2-causes\n\n\nLD DE,$FE00 : INC DE\n\nFailed #2\n", @@ -52,7 +52,7 @@ "blargg/cgb_sound/rom_singles/06-overflow on trigger.gb": "06-overflow on trigger\n\n7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF \n8D7112A4 \nFailed\n", "blargg/cgb_sound/rom_singles/07-len sweep period sync.gb": "07-len sweep period sync\n\n\nLength period is wrong\n\nFailed #2\n", "blargg/cgb_sound/rom_singles/08-len ctr during power.gb": "08-len ctr during power\n\n00 00 00 00 2144DF1C \nFailed\n", - "blargg/cgb_sound/rom_singles/09-wave read while on.gb": "09-wave read while on\n\n55 33 11 22 22 11 22 33 44 66 66 BB EE 22 22 77 11 77 88 55 CC CC CC 55 55 88 88 22 88 44 55 EE FF DD CC 99 99 88 88 AA BB DD 22 55 AA EE 22 77 CC 22 88 66 DD DD 55 EE 77 BB 00 66 11 DD 66 33 00 00 DD CC DD CCD5FE43 \nFailed\n", + "blargg/cgb_sound/rom_singles/09-wave read while on.gb": "09-wave read while on\n\n55 33 11 11 00 FF FF FF 00 33 55 88 99 DD 11 AA AA 55 BB 22 99 11 11 99 22 BB FF AA 55 11 11 AA 77 77 66 33 22 22 22 33 44 66 88 BB EE 22 66 BB 00 88 55 CC 33 BB 33 CC 77 11 BB 11 CC 88 44 11 EE CC AA 99 AA B7836723 \nFailed\n", "blargg/cgb_sound/rom_singles/10-wave trigger while on.gb": "10-wave trigger while on\n\n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF \n00 11 22 33 44 55 66 77 88 99", "blargg/cgb_sound/rom_singles/11-regs after power.gb": "11-regs after power\n\n\nPowering off should clear NR13\n\nFailed #3\n", "blargg/cgb_sound/rom_singles/12-wave.gb": "12-wave\n\n\nTimer period or phase resetting is wrong\n\nFailed #2\n" diff --git a/tests/test_results/cgb_whichboot.gb.png b/tests/test_results/cgb_whichboot.gb.png index 9edda33da..57c8411db 100644 Binary files a/tests/test_results/cgb_whichboot.gb.png and b/tests/test_results/cgb_whichboot.gb.png differ diff --git a/tests/test_results/dmg_whichboot.gb.png b/tests/test_results/dmg_whichboot.gb.png index 27f7890bb..e8ce67780 100644 Binary files a/tests/test_results/dmg_whichboot.gb.png and b/tests/test_results/dmg_whichboot.gb.png differ diff --git a/tests/test_rtc3test.py b/tests/test_rtc3test.py index 3118d6536..eb66feb0c 100644 --- a/tests/test_rtc3test.py +++ b/tests/test_rtc3test.py @@ -45,7 +45,7 @@ def test_rtc3test(subtest, rtc3test_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_samesuite.py b/tests/test_samesuite.py index 16e0e5bba..edf36170c 100644 --- a/tests/test_samesuite.py +++ b/tests/test_samesuite.py @@ -155,7 +155,7 @@ def test_samesuite(clean, gb_type, rom, samesuite_dir, boot_cgb_rom, boot_rom, d old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_shonumi.py b/tests/test_shonumi.py index 01adfe01b..e9aaa6e48 100644 --- a/tests/test_shonumi.py +++ b/tests/test_shonumi.py @@ -37,7 +37,7 @@ def test_shonumi(rom, shonumi_dir): old_image = old_image.resize(image.size, resample=PIL.Image.Dither.NONE) diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_which.py b/tests/test_which.py index afa67f46e..2a358afe4 100644 --- a/tests/test_which.py +++ b/tests/test_which.py @@ -30,7 +30,7 @@ def test_which(cgb, which_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show() diff --git a/tests/test_whichboot.py b/tests/test_whichboot.py index 34d5bdb19..31fa35040 100644 --- a/tests/test_whichboot.py +++ b/tests/test_whichboot.py @@ -15,11 +15,10 @@ @pytest.mark.parametrize("cgb", [False, True]) -def test_which(cgb, whichboot_file): - pyboy = PyBoy(whichboot_file, window="null", cgb=cgb) +def test_which(cgb, whichboot_file, boot_rom, boot_cgb_rom): + pyboy = PyBoy(whichboot_file, window="null", cgb=cgb, bootrom=boot_cgb_rom if cgb else boot_rom) pyboy.set_emulation_speed(0) - pyboy.tick(59, True) - pyboy.tick(25, True) + pyboy.tick(1000, True) png_path = Path(f"tests/test_results/{'cgb' if cgb else 'dmg'}_{os.path.basename(whichboot_file)}.png") image = pyboy.screen.image @@ -30,7 +29,7 @@ def test_which(cgb, whichboot_file): # Converting to RGB as ImageChops.difference cannot handle Alpha: https://github.com/python-pillow/Pillow/issues/4849 old_image = PIL.Image.open(png_path).convert("RGB") diff = PIL.ImageChops.difference(image.convert("RGB"), old_image) - if diff.getbbox() and not os.environ.get("TEST_CI"): + if diff.getbbox() and os.environ.get("TEST_VERBOSE_IMAGES"): image.show() old_image.show() diff.show()