Skip to content

Limit instructions per second #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ An Octo compatible XO Chip, Super Chip, and Chip 8 emulator.
4. [Running](#running)
1. [Running a ROM](#running-a-rom)
2. [Screen Scale](#screen-scale)
3. [Execution Delay](#execution-delay)
3. [Instructions Per Second](#instructions-per-second)
4. [Quirks Modes](#quirks-modes)
1. [Shift Quirks](#shift-quirks)
2. [Index Quirks](#index-quirks)
Expand Down Expand Up @@ -207,18 +207,14 @@ scale is 64 x 32):
The command above will scale the window so that it is 10 times the normal
size.

### Execution Delay
### Instructions Per Second

You may also wish to experiment with the `--delay` switch, which instructs
the emulator to add a delay to every operation that is executed. For example,
The `--ticks` switch will limit the number of instructions per second that the
emulator is allowed to run. By default, the value is set to 1,000. Minimum values
are 200. Use this switch to adjust the running time of ROMs that execute too quickly.
For simplicity, each instruction is assumed to take the same amount of time.

python yac8e.py /path/to/rom/filename --delay 10

The command above will add a 10 ms delay to every opcode that is executed.
This is useful for very fast computers (note that it is difficult to find
information regarding opcode execution times, as such, I have not attempted
any fancy timing mechanisms to ensure that instructions are executed in a
set amount of time).
python yac8e.py /path/to/rom/filename --ticks 2000

### Quirks Modes

Expand Down
16 changes: 15 additions & 1 deletion chip8/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@
jump_quirks=False,
clip_quirks=False,
logic_quirks=False,
mem_size="64K"
mem_size="64K",
max_ticks=1000,
):
"""
Initialize the Chip8 CPU. The only required parameter is a screen
Expand All @@ -98,6 +99,7 @@
:param clip_quirks: enables screen clipping quirks
:param logic_quirks: enables logic quirks
:param mem_size: sets the maximum memory available "4K" or "64K"
:param max_ticks: sets the maximum allowable operations per second
"""
self.last_pc = 0x0000
self.last_op = "None"
Expand All @@ -109,6 +111,11 @@
self.index = 0
self.rpl = [0] * NUM_REGISTERS

self.tick_counter = 0
self.max_ticks = max_ticks
if self.max_ticks < 200:
self.max_ticks = 200

Check warning on line 117 in chip8/cpu.py

View check run for this annotation

Codecov / codecov/patch

chip8/cpu.py#L117

Added line #L117 was not covered by tests

self.pitch = 64
self.playback_rate = 4000
self.audio_pattern_buffer = [0] * 16
Expand Down Expand Up @@ -226,6 +233,9 @@
:param operand: the operand to execute
:return: returns the operand executed
"""
if self.tick_counter > self.max_ticks:
return None

Check warning on line 237 in chip8/cpu.py

View check run for this annotation

Codecov / codecov/patch

chip8/cpu.py#L237

Added line #L237 was not covered by tests

self.last_pc = self.pc
if operand:
self.operand = operand
Expand All @@ -236,6 +246,7 @@
self.pc += 2
operation = (self.operand & 0xF000) >> 12
self.operation_lookup[operation]()
self.tick_counter += 1
return self.operand

def execute_logical_instruction(self):
Expand Down Expand Up @@ -1249,6 +1260,7 @@
self.sound_playing = False
self.sound_waveform = None
self.bitplane = 1
self.tick_counter = 0

def load_rom(self, filename, offset=PROGRAM_COUNTER_START):
"""
Expand All @@ -1269,6 +1281,8 @@
"""
Decrement both the sound and delay timer.
"""
self.tick_counter = 0

self.delay -= 1 if self.delay > 0 else 0
self.sound -= 1 if self.delay > 0 else 0
if self.sound > 0 and not self.sound_playing:
Expand Down
4 changes: 2 additions & 2 deletions chip8/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
:param args: the parsed command-line arguments
"""
delay_timer_event = 24
max_ticks = int(args.ticks / 60)

Check warning on line 25 in chip8/emulator.py

View check run for this annotation

Codecov / codecov/patch

chip8/emulator.py#L25

Added line #L25 was not covered by tests

screen = Chip8Screen(
scale_factor=args.scale,
Expand All @@ -39,6 +40,7 @@
clip_quirks=args.clip_quirks,
logic_quirks=args.logic_quirks,
mem_size=args.mem_size,
max_ticks=max_ticks
)
cpu.load_rom(FONT_FILE, 0)
cpu.load_rom(args.rom)
Expand All @@ -47,8 +49,6 @@
pygame.time.set_timer(delay_timer_event, 17)

while cpu.running:
pygame.time.wait(args.op_delay)

if not cpu.awaiting_keypress:
cpu.execute_instruction()

Expand Down
5 changes: 2 additions & 3 deletions yac8e.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ def parse_arguments():
"--scale", help="the scale factor to apply to the display "
"(default is 5)", type=int, default=5, dest="scale")
parser.add_argument(
"--delay", help="sets the CPU operation to take at least "
"the specified number of milliseconds to execute (default is 1)",
type=int, default=1, dest="op_delay")
"--ticks", help="how many instructions per second are allowed",
type=int, default=1000, dest="ticks")
parser.add_argument(
"--shift_quirks", help="Enable shift quirks",
action="store_true", dest="shift_quirks"
Expand Down
Loading