pyboy-advance 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. pyboy_advance-0.0.1/LICENSE +21 -0
  2. pyboy_advance-0.0.1/PKG-INFO +81 -0
  3. pyboy_advance-0.0.1/README.md +64 -0
  4. pyboy_advance-0.0.1/pyboy_advance/__init__.py +0 -0
  5. pyboy_advance-0.0.1/pyboy_advance/__main__.py +19 -0
  6. pyboy_advance-0.0.1/pyboy_advance/app/__init__.py +0 -0
  7. pyboy_advance-0.0.1/pyboy_advance/app/window.py +147 -0
  8. pyboy_advance-0.0.1/pyboy_advance/cpu/__init__.py +0 -0
  9. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/__init__.py +0 -0
  10. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/alu.py +260 -0
  11. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/bdt.py +114 -0
  12. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/branch.py +33 -0
  13. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/decode.py +50 -0
  14. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/hwdt.py +80 -0
  15. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/mul.py +114 -0
  16. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/psr.py +71 -0
  17. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/sdt.py +71 -0
  18. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/swi.py +14 -0
  19. pyboy_advance-0.0.1/pyboy_advance/cpu/arm/swp.py +33 -0
  20. pyboy_advance-0.0.1/pyboy_advance/cpu/constants.py +57 -0
  21. pyboy_advance-0.0.1/pyboy_advance/cpu/cpu.py +307 -0
  22. pyboy_advance-0.0.1/pyboy_advance/cpu/registers.py +204 -0
  23. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/__init__.py +0 -0
  24. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/alu.py +247 -0
  25. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/bdt.py +132 -0
  26. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/branch.py +62 -0
  27. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/decode.py +74 -0
  28. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/sdt.py +158 -0
  29. pyboy_advance-0.0.1/pyboy_advance/cpu/thumb/swi.py +14 -0
  30. pyboy_advance-0.0.1/pyboy_advance/gba.py +92 -0
  31. pyboy_advance-0.0.1/pyboy_advance/interrupt_controller.py +110 -0
  32. pyboy_advance-0.0.1/pyboy_advance/keypad.py +113 -0
  33. pyboy_advance-0.0.1/pyboy_advance/memory/__init__.py +0 -0
  34. pyboy_advance-0.0.1/pyboy_advance/memory/constants.py +154 -0
  35. pyboy_advance-0.0.1/pyboy_advance/memory/dma.py +232 -0
  36. pyboy_advance-0.0.1/pyboy_advance/memory/gamepak.py +28 -0
  37. pyboy_advance-0.0.1/pyboy_advance/memory/io.py +264 -0
  38. pyboy_advance-0.0.1/pyboy_advance/memory/memory.py +494 -0
  39. pyboy_advance-0.0.1/pyboy_advance/ppu/__init__.py +0 -0
  40. pyboy_advance-0.0.1/pyboy_advance/ppu/constants.py +56 -0
  41. pyboy_advance-0.0.1/pyboy_advance/ppu/memory.py +106 -0
  42. pyboy_advance-0.0.1/pyboy_advance/ppu/ppu.py +518 -0
  43. pyboy_advance-0.0.1/pyboy_advance/ppu/registers.py +166 -0
  44. pyboy_advance-0.0.1/pyboy_advance/scheduler.py +35 -0
  45. pyboy_advance-0.0.1/pyboy_advance/utils.py +141 -0
  46. pyboy_advance-0.0.1/pyboy_advance.egg-info/PKG-INFO +81 -0
  47. pyboy_advance-0.0.1/pyboy_advance.egg-info/SOURCES.txt +52 -0
  48. pyboy_advance-0.0.1/pyboy_advance.egg-info/dependency_links.txt +1 -0
  49. pyboy_advance-0.0.1/pyboy_advance.egg-info/entry_points.txt +2 -0
  50. pyboy_advance-0.0.1/pyboy_advance.egg-info/requires.txt +2 -0
  51. pyboy_advance-0.0.1/pyboy_advance.egg-info/top_level.txt +1 -0
  52. pyboy_advance-0.0.1/pyproject.toml +45 -0
  53. pyboy_advance-0.0.1/setup.cfg +4 -0
  54. pyboy_advance-0.0.1/tests/test_gba_tests.py +44 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 William Ha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyboy-advance
3
+ Version: 0.0.1
4
+ Summary: Game Boy Advance emulator written in Python
5
+ Author: William Ha
6
+ Keywords: gameboy,game boy,game boy advance,gameboy advance,emulator,pypy
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Topic :: System :: Emulators
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pysdl2
15
+ Requires-Dist: pysdl2-dll
16
+ Dynamic: license-file
17
+
18
+ <p align="center">
19
+ <img src="assets/pyboy_advance.svg" alt="PyBoy Advance" width="240">
20
+ </p>
21
+
22
+ ---
23
+
24
+ PyBoy Advance is a Game Boy Advance emulator written in Python.
25
+
26
+ There are already plenty of fantastic GBA emulators out in the wild,
27
+ most written in languages far better suited than Python for emulator development.
28
+ This project is not meant to compete with those emulators nor match their
29
+ capabilities. In fact, PyBoy Advance is rather crap (and slow!) by comparison. I work on this project purely for fun,
30
+ with my end goal being to offer a highly readable implementation of a GBA emulator for educational purposes.
31
+
32
+ ## Getting started
33
+
34
+ PyBoy Advance is written in pure Python and technically can be run with the standard CPython interpreter, but
35
+ performance will be unacceptably slow. Instead, you will need to run PyBoy Advance with [PyPy](https://pypy.org/),
36
+ a fast implementation of Python (see [Downloading and Installing PyPy](https://doc.pypy.org/en/stable/install.html)).
37
+
38
+ Install PyBoy Advance with `pip`:
39
+
40
+ ```bash
41
+ $ pypy -m pip install pyboy-advance
42
+ ```
43
+
44
+ You will need to provide a Game Boy Advance BIOS. Normatt's open source BIOS is supported and
45
+ available [here](https://github.com/Nebuleon/ReGBA/blob/master/bios/gba_bios.bin).
46
+
47
+ Launch PyBoy Advance from the terminal:
48
+
49
+ ```bash
50
+ $ pyboy_advance --bios /path/to/bios.bin game_rom.gba
51
+ ```
52
+
53
+ Or import and use it in your Python scripts:
54
+
55
+ ```python
56
+ from pyboy_advance import PyBoyAdvance
57
+
58
+ emulator = PyBoyAdvance(rom="game_rom.gba", bios="/path/to/bios.bin")
59
+ emulator.run()
60
+ ```
61
+
62
+ ## Screenshots
63
+
64
+ <p align="center">
65
+ <img src="assets/screenshot_kirby.png" alt="Screenshot of PyBoy Advance running Kirby: Nightmare in Dream Land" width="400">
66
+ </p>
67
+
68
+ ## Acknowledgements
69
+
70
+ - [GBATEK](https://problemkaputt.de/gbatek.htm) by Martin Korth, the king of GBA hardware documentation
71
+ - [ARM7TDMI Technical Reference Manual](https://developer.arm.com/documentation/ddi0210/c/)
72
+ - [CowBite Virtual Hardware Specifications](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm) by Tom Happ
73
+ - [Tonc](https://www.coranac.com/tonc/text/toc.htm) by Jasper Vijn
74
+ - Excellent reference GBA emulators:
75
+ - [mGBA](https://mgba.io/) by Vicki Pfau (endrift)
76
+ - [NanoBoyAdvance](https://github.com/nba-emu/NanoBoyAdvance) by Fleroviux
77
+ - [Hades](https://github.com/hades-emu/Hades) by Arignir
78
+ - [RustBoyAdvance-NG](https://github.com/michelhe/rustboyadvance-ng/) by Michel Heily
79
+ - [GBA Tests](https://github.com/jsmolka/gba-tests) by Julian Smolka
80
+ - [PyBoy](https://github.com/Baekalfen/PyBoy) by Mads Ynddal (Baekalfen), a sister project (Game Boy emulator written in
81
+ Python)
@@ -0,0 +1,64 @@
1
+ <p align="center">
2
+ <img src="assets/pyboy_advance.svg" alt="PyBoy Advance" width="240">
3
+ </p>
4
+
5
+ ---
6
+
7
+ PyBoy Advance is a Game Boy Advance emulator written in Python.
8
+
9
+ There are already plenty of fantastic GBA emulators out in the wild,
10
+ most written in languages far better suited than Python for emulator development.
11
+ This project is not meant to compete with those emulators nor match their
12
+ capabilities. In fact, PyBoy Advance is rather crap (and slow!) by comparison. I work on this project purely for fun,
13
+ with my end goal being to offer a highly readable implementation of a GBA emulator for educational purposes.
14
+
15
+ ## Getting started
16
+
17
+ PyBoy Advance is written in pure Python and technically can be run with the standard CPython interpreter, but
18
+ performance will be unacceptably slow. Instead, you will need to run PyBoy Advance with [PyPy](https://pypy.org/),
19
+ a fast implementation of Python (see [Downloading and Installing PyPy](https://doc.pypy.org/en/stable/install.html)).
20
+
21
+ Install PyBoy Advance with `pip`:
22
+
23
+ ```bash
24
+ $ pypy -m pip install pyboy-advance
25
+ ```
26
+
27
+ You will need to provide a Game Boy Advance BIOS. Normatt's open source BIOS is supported and
28
+ available [here](https://github.com/Nebuleon/ReGBA/blob/master/bios/gba_bios.bin).
29
+
30
+ Launch PyBoy Advance from the terminal:
31
+
32
+ ```bash
33
+ $ pyboy_advance --bios /path/to/bios.bin game_rom.gba
34
+ ```
35
+
36
+ Or import and use it in your Python scripts:
37
+
38
+ ```python
39
+ from pyboy_advance import PyBoyAdvance
40
+
41
+ emulator = PyBoyAdvance(rom="game_rom.gba", bios="/path/to/bios.bin")
42
+ emulator.run()
43
+ ```
44
+
45
+ ## Screenshots
46
+
47
+ <p align="center">
48
+ <img src="assets/screenshot_kirby.png" alt="Screenshot of PyBoy Advance running Kirby: Nightmare in Dream Land" width="400">
49
+ </p>
50
+
51
+ ## Acknowledgements
52
+
53
+ - [GBATEK](https://problemkaputt.de/gbatek.htm) by Martin Korth, the king of GBA hardware documentation
54
+ - [ARM7TDMI Technical Reference Manual](https://developer.arm.com/documentation/ddi0210/c/)
55
+ - [CowBite Virtual Hardware Specifications](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm) by Tom Happ
56
+ - [Tonc](https://www.coranac.com/tonc/text/toc.htm) by Jasper Vijn
57
+ - Excellent reference GBA emulators:
58
+ - [mGBA](https://mgba.io/) by Vicki Pfau (endrift)
59
+ - [NanoBoyAdvance](https://github.com/nba-emu/NanoBoyAdvance) by Fleroviux
60
+ - [Hades](https://github.com/hades-emu/Hades) by Arignir
61
+ - [RustBoyAdvance-NG](https://github.com/michelhe/rustboyadvance-ng/) by Michel Heily
62
+ - [GBA Tests](https://github.com/jsmolka/gba-tests) by Julian Smolka
63
+ - [PyBoy](https://github.com/Baekalfen/PyBoy) by Mads Ynddal (Baekalfen), a sister project (Game Boy emulator written in
64
+ Python)
File without changes
@@ -0,0 +1,19 @@
1
+ import argparse
2
+ import sys
3
+
4
+ from pyboy_advance.gba import PyBoyAdvance
5
+
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser("pyboy_advance")
9
+ parser.add_argument("rom", type=str)
10
+ parser.add_argument("--bios", type=str)
11
+ parser.add_argument("--skip-bios", action="store_true")
12
+ args = parser.parse_args()
13
+
14
+ pyboy_advance = PyBoyAdvance(args.rom, args.bios, skip_bios=args.skip_bios)
15
+ pyboy_advance.run()
16
+
17
+
18
+ if __name__ == "__main__":
19
+ sys.exit(main())
File without changes
@@ -0,0 +1,147 @@
1
+ from __future__ import annotations
2
+
3
+ from ctypes import c_void_p
4
+ from enum import Enum, auto
5
+
6
+ import sdl2.ext
7
+
8
+ from pyboy_advance.ppu.constants import DISPLAY_WIDTH, DISPLAY_HEIGHT, COLOUR_SIZE
9
+
10
+
11
+ class Window:
12
+ DEFAULT_WINDOW_SCALE = 2
13
+
14
+ def __init__(self):
15
+ self._events = []
16
+
17
+ def __enter__(self):
18
+ if sdl2.SDL_InitSubSystem(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_GAMECONTROLLER) < 0:
19
+ raise ValueError(f"SDL_InitSubSystem failed: {sdl2.SDL_GetError().decode()}")
20
+
21
+ self.sdl_window = sdl2.SDL_CreateWindow(
22
+ b"PyBoy Advance",
23
+ sdl2.SDL_WINDOWPOS_CENTERED,
24
+ sdl2.SDL_WINDOWPOS_CENTERED,
25
+ DISPLAY_WIDTH * Window.DEFAULT_WINDOW_SCALE,
26
+ DISPLAY_HEIGHT * Window.DEFAULT_WINDOW_SCALE,
27
+ sdl2.SDL_WINDOW_RESIZABLE,
28
+ )
29
+
30
+ self.sdl_renderer = sdl2.SDL_CreateRenderer(
31
+ self.sdl_window, -1, sdl2.SDL_RENDERER_ACCELERATED
32
+ )
33
+
34
+ sdl2.SDL_RenderSetLogicalSize(self.sdl_renderer, DISPLAY_WIDTH, DISPLAY_HEIGHT)
35
+
36
+ self.sdl_texture_buffer = sdl2.SDL_CreateTexture(
37
+ self.sdl_renderer,
38
+ sdl2.SDL_PIXELFORMAT_BGR555,
39
+ sdl2.SDL_TEXTUREACCESS_STREAMING,
40
+ DISPLAY_WIDTH,
41
+ DISPLAY_HEIGHT,
42
+ )
43
+
44
+ sdl2.SDL_ShowWindow(self.sdl_window)
45
+
46
+ return self
47
+
48
+ def __exit__(self, exc_type, exc_val, exc_tb):
49
+ sdl2.SDL_DestroyWindow(self.sdl_window)
50
+ sdl2.SDL_QuitSubSystem(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_GAMECONTROLLER)
51
+
52
+ @property
53
+ def fullscreen(self) -> bool:
54
+ flags = sdl2.SDL_GetWindowFlags(self.sdl_window)
55
+ return flags & sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP
56
+
57
+ @fullscreen.setter
58
+ def fullscreen(self, value: bool):
59
+ sdl2.SDL_SetWindowFullscreen(
60
+ self.sdl_window,
61
+ sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP if value else 0,
62
+ )
63
+
64
+ def get_events(self) -> list[WindowEvent]:
65
+ self._events.clear()
66
+ for event in sdl2.ext.get_events():
67
+ if event.type == sdl2.SDL_QUIT:
68
+ self._events.append(WindowEvent.QUIT)
69
+ elif event.type == sdl2.SDL_KEYDOWN:
70
+ self._events.append(KeyMapping.KEY_DOWN.get(event.key.keysym.sym, WindowEvent.NONE))
71
+ elif event.type == sdl2.SDL_KEYUP:
72
+ self._events.append(KeyMapping.KEY_UP.get(event.key.keysym.sym, WindowEvent.NONE))
73
+ return self._events
74
+
75
+ def render(self, frame_buffer_ptr: c_void_p):
76
+ sdl2.SDL_UpdateTexture(
77
+ self.sdl_texture_buffer,
78
+ None,
79
+ frame_buffer_ptr,
80
+ DISPLAY_WIDTH * COLOUR_SIZE,
81
+ )
82
+ sdl2.SDL_RenderClear(self.sdl_renderer)
83
+ sdl2.SDL_RenderCopy(self.sdl_renderer, self.sdl_texture_buffer, None, None)
84
+ sdl2.SDL_RenderPresent(self.sdl_renderer)
85
+
86
+
87
+ class WindowEvent(Enum):
88
+ # fmt: off
89
+ NONE = auto()
90
+
91
+ QUIT = auto()
92
+ FULLSCREEN = auto()
93
+
94
+ PRESS_BUTTON_A = auto()
95
+ PRESS_BUTTON_B = auto()
96
+ PRESS_BUTTON_SELECT = auto()
97
+ PRESS_BUTTON_START = auto()
98
+ PRESS_DPAD_RIGHT = auto()
99
+ PRESS_DPAD_LEFT = auto()
100
+ PRESS_DPAD_UP = auto()
101
+ PRESS_DPAD_DOWN = auto()
102
+ PRESS_SHOULDER_RIGHT = auto()
103
+ PRESS_SHOULDER_LEFT = auto()
104
+
105
+ RELEASE_BUTTON_A = auto()
106
+ RELEASE_BUTTON_B = auto()
107
+ RELEASE_BUTTON_SELECT = auto()
108
+ RELEASE_BUTTON_START = auto()
109
+ RELEASE_DPAD_RIGHT = auto()
110
+ RELEASE_DPAD_LEFT = auto()
111
+ RELEASE_DPAD_UP = auto()
112
+ RELEASE_DPAD_DOWN = auto()
113
+ RELEASE_SHOULDER_RIGHT = auto()
114
+ RELEASE_SHOULDER_LEFT = auto()
115
+ # fmt: on
116
+
117
+
118
+ class KeyMapping:
119
+ # fmt: off
120
+ KEY_DOWN = {
121
+ sdl2.SDLK_a : WindowEvent.PRESS_BUTTON_A,
122
+ sdl2.SDLK_s : WindowEvent.PRESS_BUTTON_B,
123
+ sdl2.SDLK_BACKSPACE : WindowEvent.PRESS_BUTTON_SELECT,
124
+ sdl2.SDLK_RETURN : WindowEvent.PRESS_BUTTON_START,
125
+ sdl2.SDLK_RIGHT : WindowEvent.PRESS_DPAD_RIGHT,
126
+ sdl2.SDLK_LEFT : WindowEvent.PRESS_DPAD_LEFT,
127
+ sdl2.SDLK_UP : WindowEvent.PRESS_DPAD_UP,
128
+ sdl2.SDLK_DOWN : WindowEvent.PRESS_DPAD_DOWN,
129
+ sdl2.SDLK_w : WindowEvent.PRESS_SHOULDER_RIGHT,
130
+ sdl2.SDLK_q : WindowEvent.PRESS_SHOULDER_LEFT,
131
+ }
132
+
133
+ KEY_UP = {
134
+ sdl2.SDLK_a : WindowEvent.RELEASE_BUTTON_A,
135
+ sdl2.SDLK_s : WindowEvent.RELEASE_BUTTON_B,
136
+ sdl2.SDLK_BACKSPACE : WindowEvent.RELEASE_BUTTON_SELECT,
137
+ sdl2.SDLK_RETURN : WindowEvent.RELEASE_BUTTON_START,
138
+ sdl2.SDLK_RIGHT : WindowEvent.RELEASE_DPAD_RIGHT,
139
+ sdl2.SDLK_LEFT : WindowEvent.RELEASE_DPAD_LEFT,
140
+ sdl2.SDLK_UP : WindowEvent.RELEASE_DPAD_UP,
141
+ sdl2.SDLK_DOWN : WindowEvent.RELEASE_DPAD_DOWN,
142
+ sdl2.SDLK_w : WindowEvent.RELEASE_SHOULDER_RIGHT,
143
+ sdl2.SDLK_q : WindowEvent.RELEASE_SHOULDER_LEFT,
144
+ sdl2.SDLK_ESCAPE : WindowEvent.QUIT,
145
+ sdl2.SDLK_F11 : WindowEvent.FULLSCREEN,
146
+ }
147
+ # fmt: on
File without changes
File without changes
@@ -0,0 +1,260 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import IntEnum
4
+ from typing import TYPE_CHECKING
5
+
6
+ from pyboy_advance.cpu.registers import Registers
7
+ from pyboy_advance.utils import get_bits, get_bit, ror_32, sign_32, bint
8
+
9
+ if TYPE_CHECKING:
10
+ from pyboy_advance.cpu.cpu import CPU
11
+
12
+
13
+ class ALUOpcode(IntEnum):
14
+ AND = 0x0
15
+ EOR = 0x1
16
+ SUB = 0x2
17
+ RSB = 0x3
18
+ ADD = 0x4
19
+ ADC = 0x5
20
+ SBC = 0x6
21
+ RSC = 0x7
22
+ TST = 0x8
23
+ TEQ = 0x9
24
+ CMP = 0xA
25
+ CMN = 0xB
26
+ ORR = 0xC
27
+ MOV = 0xD
28
+ BIC = 0xE
29
+ MVN = 0xF
30
+
31
+
32
+ def arm_alu(cpu: CPU, instr: int):
33
+ """Execute a Data Processing (ALU) instruction (AND, EOR, ADD, SUB, MOV, etc.)"""
34
+
35
+ rn = get_bits(instr, 16, 19)
36
+ rd = get_bits(instr, 12, 15)
37
+
38
+ shift_carry = cpu.regs.cpsr.carry_flag
39
+
40
+ set_cond_codes = get_bit(instr, 20)
41
+ immediate = get_bit(instr, 25)
42
+
43
+ early_advance_pc = False
44
+
45
+ if immediate: # Immediate value as 2nd operand
46
+ op1 = cpu.regs[rn]
47
+ op2 = get_bits(instr, 0, 7)
48
+ ror_amount = get_bits(instr, 8, 11) * 2
49
+
50
+ if ror_amount > 0:
51
+ shift_carry = get_bit(op2, ror_amount - 1)
52
+ op2 = ror_32(op2, ror_amount)
53
+
54
+ else: # Register value as 2nd operand
55
+ rm = get_bits(instr, 0, 3)
56
+ shift = get_bits(instr, 4, 11)
57
+
58
+ # When using PC as an operand, the returned value depends on the instruction.
59
+ # If shifting by register (shift[0] = 1) and using a register value as op2,
60
+ # then the operand should be PC + 12.
61
+ if get_bit(shift, 0):
62
+ # Advance PC by 4 so that cpu.regs.pc returns PC + 12
63
+ cpu.advance_pc_arm()
64
+ early_advance_pc = True
65
+ cpu.scheduler.idle()
66
+ else:
67
+ # Otherwise, the operand should be PC + 8
68
+ # (which is what cpu.regs.pc returns normally).
69
+ pass
70
+
71
+ op1 = cpu.regs[rn]
72
+ op2, shift_carry = cpu.decode_and_compute_shift(cpu.regs[rm], shift)
73
+
74
+ opcode = ALUOpcode(get_bits(instr, 21, 24))
75
+ if opcode == ALUOpcode.AND:
76
+ arm_alu_and(cpu, op1, op2, rd, set_cond_codes, shift_carry)
77
+ elif opcode == ALUOpcode.EOR:
78
+ arm_alu_eor(cpu, op1, op2, rd, set_cond_codes, shift_carry)
79
+ elif opcode == ALUOpcode.SUB:
80
+ arm_alu_sub(cpu, op1, op2, rd, set_cond_codes)
81
+ elif opcode == ALUOpcode.RSB:
82
+ arm_alu_rsb(cpu, op1, op2, rd, set_cond_codes)
83
+ elif opcode == ALUOpcode.ADD:
84
+ arm_alu_add(cpu, op1, op2, rd, set_cond_codes)
85
+ elif opcode == ALUOpcode.ADC:
86
+ arm_alu_adc(cpu, op1, op2, rd, set_cond_codes)
87
+ elif opcode == ALUOpcode.SBC:
88
+ arm_alu_sbc(cpu, op1, op2, rd, set_cond_codes)
89
+ elif opcode == ALUOpcode.RSC:
90
+ arm_alu_rsc(cpu, op1, op2, rd, set_cond_codes)
91
+ elif opcode == ALUOpcode.TST:
92
+ arm_alu_tst(cpu, op1, op2, shift_carry)
93
+ elif opcode == ALUOpcode.TEQ:
94
+ arm_alu_teq(cpu, op1, op2, shift_carry)
95
+ elif opcode == ALUOpcode.CMP:
96
+ arm_alu_cmp(cpu, op1, op2)
97
+ elif opcode == ALUOpcode.CMN:
98
+ arm_alu_cmn(cpu, op1, op2)
99
+ elif opcode == ALUOpcode.ORR:
100
+ arm_alu_orr(cpu, op1, op2, rd, set_cond_codes, shift_carry)
101
+ elif opcode == ALUOpcode.MOV:
102
+ arm_alu_mov(cpu, op2, rd, set_cond_codes, shift_carry)
103
+ elif opcode == ALUOpcode.BIC:
104
+ arm_alu_bic(cpu, op1, op2, rd, set_cond_codes, shift_carry)
105
+ elif opcode == ALUOpcode.MVN:
106
+ arm_alu_mvn(cpu, op2, rd, set_cond_codes, shift_carry)
107
+
108
+ # When S bit = 1 (set_cond_codes) and Rd = PC, the result of operation is stored in PC
109
+ # and SPSR of the current mode is moved to CPSR
110
+ if rd == Registers.PC:
111
+ if set_cond_codes:
112
+ new_cpsr_reg = cpu.regs.spsr.reg
113
+ new_cpsr_mode = cpu.regs.spsr.mode
114
+ cpu.switch_mode(new_cpsr_mode)
115
+ cpu.regs.cpsr.reg = new_cpsr_reg
116
+
117
+ # Read only operations do not flush the pipeline
118
+ if opcode not in [ALUOpcode.TST, ALUOpcode.TEQ, ALUOpcode.CMP, ALUOpcode.CMN]:
119
+ cpu.flush_pipeline()
120
+
121
+ elif not early_advance_pc:
122
+ cpu.advance_pc_arm()
123
+
124
+
125
+ def arm_alu_and(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
126
+ cpu.regs[rd] = arm_alu_and_impl(cpu, op1, op2, rd, set_cond_codes, shift_carry)
127
+
128
+
129
+ def arm_alu_and_impl(
130
+ cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint
131
+ ) -> int:
132
+ result = op1 & op2
133
+ if set_cond_codes and rd != Registers.PC:
134
+ cpu.regs.cpsr.sign_flag = sign_32(result)
135
+ cpu.regs.cpsr.zero_flag = result == 0
136
+ cpu.regs.cpsr.carry_flag = shift_carry
137
+ return result
138
+
139
+
140
+ def arm_alu_eor(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
141
+ cpu.regs[rd] = arm_alu_eor_impl(cpu, op1, op2, rd, set_cond_codes, shift_carry)
142
+
143
+
144
+ def arm_alu_eor_impl(
145
+ cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint
146
+ ) -> int:
147
+ result = op1 ^ op2
148
+ if set_cond_codes and rd != Registers.PC:
149
+ cpu.regs.cpsr.sign_flag = sign_32(result)
150
+ cpu.regs.cpsr.zero_flag = result == 0
151
+ cpu.regs.cpsr.carry_flag = shift_carry
152
+ return result
153
+
154
+
155
+ def arm_alu_sub(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
156
+ cpu.regs[rd] = arm_alu_sub_impl(cpu, op1, op2, rd, set_cond_codes)
157
+
158
+
159
+ def arm_alu_sub_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint) -> int:
160
+ result = op1 - op2
161
+ truncated_result = result & 0xFFFFFFFF
162
+ if set_cond_codes and rd != Registers.PC:
163
+ cpu.regs.cpsr.sign_flag = sign_32(truncated_result)
164
+ cpu.regs.cpsr.zero_flag = truncated_result == 0
165
+ cpu.regs.cpsr.carry_flag = op1 >= op2 # Carry = no borrow
166
+ cpu.regs.cpsr.overflow_flag = sign_32((op1 ^ op2) & (op1 ^ truncated_result))
167
+ return truncated_result
168
+
169
+
170
+ def arm_alu_rsb(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
171
+ arm_alu_sub(cpu, op2, op1, rd, set_cond_codes)
172
+
173
+
174
+ def arm_alu_add(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
175
+ cpu.regs[rd] = arm_alu_add_impl(cpu, op1, op2, rd, set_cond_codes)
176
+
177
+
178
+ def arm_alu_add_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint) -> int:
179
+ result = op1 + op2
180
+ truncated_result = result & 0xFFFFFFFF
181
+ if set_cond_codes and rd != Registers.PC:
182
+ cpu.regs.cpsr.sign_flag = sign_32(truncated_result)
183
+ cpu.regs.cpsr.zero_flag = truncated_result == 0
184
+ cpu.regs.cpsr.carry_flag = result > 0xFFFFFFFF
185
+ cpu.regs.cpsr.overflow_flag = sign_32(~(op1 ^ op2) & (op2 ^ truncated_result))
186
+ return truncated_result
187
+
188
+
189
+ def arm_alu_adc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
190
+ carry = cpu.regs.cpsr.carry_flag
191
+ result = op1 + op2 + carry
192
+ cpu.regs[rd] = result & 0xFFFFFFFF
193
+ if set_cond_codes and rd != Registers.PC:
194
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
195
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
196
+ cpu.regs.cpsr.carry_flag = result > 0xFFFFFFFF
197
+ cpu.regs.cpsr.overflow_flag = sign_32(~(op1 ^ op2) & (op2 ^ cpu.regs[rd]))
198
+
199
+
200
+ def arm_alu_sbc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
201
+ borrow = 1 - cpu.regs.cpsr.carry_flag # Carry = no borrow, so subtract 0
202
+ result = op1 - op2 - borrow
203
+ cpu.regs[rd] = result & 0xFFFFFFFF
204
+ if set_cond_codes and rd != Registers.PC:
205
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
206
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
207
+ cpu.regs.cpsr.carry_flag = op1 >= (op2 + borrow)
208
+ cpu.regs.cpsr.overflow_flag = sign_32((op1 ^ op2) & (op1 ^ cpu.regs[rd]))
209
+
210
+
211
+ def arm_alu_rsc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
212
+ arm_alu_sbc(cpu, op2, op1, rd, set_cond_codes)
213
+
214
+
215
+ def arm_alu_tst(cpu: CPU, op1: int, op2: int, shift_carry: bint):
216
+ arm_alu_and_impl(cpu, op1, op2, 0, True, shift_carry)
217
+
218
+
219
+ def arm_alu_teq(cpu: CPU, op1: int, op2: int, shift_carry: bint):
220
+ arm_alu_eor_impl(cpu, op1, op2, 0, True, shift_carry)
221
+
222
+
223
+ def arm_alu_cmp(cpu: CPU, op1: int, op2: int):
224
+ arm_alu_sub_impl(cpu, op1, op2, 0, True)
225
+
226
+
227
+ def arm_alu_cmn(cpu: CPU, op1: int, op2: int):
228
+ arm_alu_add_impl(cpu, op1, op2, 0, True)
229
+
230
+
231
+ def arm_alu_orr(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
232
+ cpu.regs[rd] = op1 | op2
233
+ if set_cond_codes and rd != Registers.PC:
234
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
235
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
236
+ cpu.regs.cpsr.carry_flag = shift_carry
237
+
238
+
239
+ def arm_alu_mov(cpu: CPU, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
240
+ cpu.regs[rd] = op2
241
+ if set_cond_codes and rd != Registers.PC:
242
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
243
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
244
+ cpu.regs.cpsr.carry_flag = shift_carry
245
+
246
+
247
+ def arm_alu_bic(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
248
+ cpu.regs[rd] = op1 & ~op2
249
+ if set_cond_codes and rd != Registers.PC:
250
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
251
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
252
+ cpu.regs.cpsr.carry_flag = shift_carry
253
+
254
+
255
+ def arm_alu_mvn(cpu: CPU, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
256
+ cpu.regs[rd] = ~op2 & 0xFFFFFFFF
257
+ if set_cond_codes and rd != Registers.PC:
258
+ cpu.regs.cpsr.sign_flag = sign_32(cpu.regs[rd])
259
+ cpu.regs.cpsr.zero_flag = cpu.regs[rd] == 0
260
+ cpu.regs.cpsr.carry_flag = shift_carry