Skip to content

Commit 5e414e9

Browse files
authored
Limit instructions per second (#50)
* Add tick counter for execution speed control * Update README table of contents
1 parent 460ac4f commit 5e414e9

File tree

4 files changed

+26
-17
lines changed

4 files changed

+26
-17
lines changed

README.md

+7-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ An Octo compatible XO Chip, Super Chip, and Chip 8 emulator.
1818
4. [Running](#running)
1919
1. [Running a ROM](#running-a-rom)
2020
2. [Screen Scale](#screen-scale)
21-
3. [Execution Delay](#execution-delay)
21+
3. [Instructions Per Second](#instructions-per-second)
2222
4. [Quirks Modes](#quirks-modes)
2323
1. [Shift Quirks](#shift-quirks)
2424
2. [Index Quirks](#index-quirks)
@@ -207,18 +207,14 @@ scale is 64 x 32):
207207
The command above will scale the window so that it is 10 times the normal
208208
size.
209209
210-
### Execution Delay
210+
### Instructions Per Second
211211
212-
You may also wish to experiment with the `--delay` switch, which instructs
213-
the emulator to add a delay to every operation that is executed. For example,
212+
The `--ticks` switch will limit the number of instructions per second that the
213+
emulator is allowed to run. By default, the value is set to 1,000. Minimum values
214+
are 200. Use this switch to adjust the running time of ROMs that execute too quickly.
215+
For simplicity, each instruction is assumed to take the same amount of time.
214216
215-
python yac8e.py /path/to/rom/filename --delay 10
216-
217-
The command above will add a 10 ms delay to every opcode that is executed.
218-
This is useful for very fast computers (note that it is difficult to find
219-
information regarding opcode execution times, as such, I have not attempted
220-
any fancy timing mechanisms to ensure that instructions are executed in a
221-
set amount of time).
217+
python yac8e.py /path/to/rom/filename --ticks 2000
222218
223219
### Quirks Modes
224220

chip8/cpu.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def __init__(
8484
jump_quirks=False,
8585
clip_quirks=False,
8686
logic_quirks=False,
87-
mem_size="64K"
87+
mem_size="64K",
88+
max_ticks=1000,
8889
):
8990
"""
9091
Initialize the Chip8 CPU. The only required parameter is a screen
@@ -98,6 +99,7 @@ def __init__(
9899
:param clip_quirks: enables screen clipping quirks
99100
:param logic_quirks: enables logic quirks
100101
:param mem_size: sets the maximum memory available "4K" or "64K"
102+
:param max_ticks: sets the maximum allowable operations per second
101103
"""
102104
self.last_pc = 0x0000
103105
self.last_op = "None"
@@ -109,6 +111,11 @@ def __init__(
109111
self.index = 0
110112
self.rpl = [0] * NUM_REGISTERS
111113

114+
self.tick_counter = 0
115+
self.max_ticks = max_ticks
116+
if self.max_ticks < 200:
117+
self.max_ticks = 200
118+
112119
self.pitch = 64
113120
self.playback_rate = 4000
114121
self.audio_pattern_buffer = [0] * 16
@@ -226,6 +233,9 @@ def execute_instruction(self, operand=None):
226233
:param operand: the operand to execute
227234
:return: returns the operand executed
228235
"""
236+
if self.tick_counter > self.max_ticks:
237+
return None
238+
229239
self.last_pc = self.pc
230240
if operand:
231241
self.operand = operand
@@ -236,6 +246,7 @@ def execute_instruction(self, operand=None):
236246
self.pc += 2
237247
operation = (self.operand & 0xF000) >> 12
238248
self.operation_lookup[operation]()
249+
self.tick_counter += 1
239250
return self.operand
240251

241252
def execute_logical_instruction(self):
@@ -1249,6 +1260,7 @@ def reset(self):
12491260
self.sound_playing = False
12501261
self.sound_waveform = None
12511262
self.bitplane = 1
1263+
self.tick_counter = 0
12521264

12531265
def load_rom(self, filename, offset=PROGRAM_COUNTER_START):
12541266
"""
@@ -1269,6 +1281,8 @@ def decrement_timers(self):
12691281
"""
12701282
Decrement both the sound and delay timer.
12711283
"""
1284+
self.tick_counter = 0
1285+
12721286
self.delay -= 1 if self.delay > 0 else 0
12731287
self.sound -= 1 if self.delay > 0 else 0
12741288
if self.sound > 0 and not self.sound_playing:

chip8/emulator.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def main_loop(args):
2222
:param args: the parsed command-line arguments
2323
"""
2424
delay_timer_event = 24
25+
max_ticks = int(args.ticks / 60)
2526

2627
screen = Chip8Screen(
2728
scale_factor=args.scale,
@@ -39,6 +40,7 @@ def main_loop(args):
3940
clip_quirks=args.clip_quirks,
4041
logic_quirks=args.logic_quirks,
4142
mem_size=args.mem_size,
43+
max_ticks=max_ticks
4244
)
4345
cpu.load_rom(FONT_FILE, 0)
4446
cpu.load_rom(args.rom)
@@ -47,8 +49,6 @@ def main_loop(args):
4749
pygame.time.set_timer(delay_timer_event, 17)
4850

4951
while cpu.running:
50-
pygame.time.wait(args.op_delay)
51-
5252
if not cpu.awaiting_keypress:
5353
cpu.execute_instruction()
5454

yac8e.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ def parse_arguments():
3333
"--scale", help="the scale factor to apply to the display "
3434
"(default is 5)", type=int, default=5, dest="scale")
3535
parser.add_argument(
36-
"--delay", help="sets the CPU operation to take at least "
37-
"the specified number of milliseconds to execute (default is 1)",
38-
type=int, default=1, dest="op_delay")
36+
"--ticks", help="how many instructions per second are allowed",
37+
type=int, default=1000, dest="ticks")
3938
parser.add_argument(
4039
"--shift_quirks", help="Enable shift quirks",
4140
action="store_true", dest="shift_quirks"

0 commit comments

Comments
 (0)