Add changeable FPS

This commit is contained in:
Juhani Krekelä 2018-09-22 18:28:01 +03:00
parent 3df40f58a5
commit f8de8aa2cf
2 changed files with 51 additions and 13 deletions

View File

@ -34,6 +34,12 @@ Emulation speed
--------------- ---------------
Instructions are run 500 times a second Instructions are run 500 times a second
Issues
------
* Player sprites tend to flicker. (See e.g. BRIX) Why? Also, this seems to
happen in the haxe-chip-8-emulator too, and lowering the FPS doesn't seem
to help, which suggests something fishy with the programs
Requirements Requirements
------------ ------------
* pyglet (`pip3 install pyglet`) * pyglet (`pip3 install pyglet`)

View File

@ -40,10 +40,11 @@ def key_released(symbol):
break break
def draw_screen(): def draw_screen():
global display_screen
to_draw = [] to_draw = []
for y in range(32): for y in range(32):
for x in range(64): for x in range(64):
if screen[y * 64 + x]: if display_screen[y * 64 + x]:
to_draw.append((x, y)) to_draw.append((x, y))
screen_points = [] screen_points = []
@ -223,10 +224,24 @@ def step():
# Dxyn draw vx, vy, n # Dxyn draw vx, vy, n
elif high_byte >> 4 == 0xD: elif high_byte >> 4 == 0xD:
# TODO: OSCOM Nano manual (page 38) says "<The screen # OSCOM Nano manual (page 38) says "<The screen behaves as
# behaves as if the top was connected to the bottom and # if the top was connected to the bottom and the sides to
# the sides to each other.>" Investigate how this wrapping # each other>", but I can't find any info on how to do this
# should be implemented # wrapping on the generally used sources.
#
# https://github.com/AfBu/haxe-chip-8-emulator looks like
# it wraps the pixels to the next row down, but as it
# doesn't seem to handle drawing out the bottom of the
# screen at all, unsure how intentional this is.
#
# https://github.com/dmatlack/chip8 wraps the pixels on the
# other side of the same line.
#
# The game "Blitz" seems to require draws off the bottom of
# the screen to not result in anything. Therefore, as I
# have yet to find any games that break with it, I'm just
# skipping pixels that fall outside the screen.
x_start = data_registers[high_byte & 0xf] x_start = data_registers[high_byte & 0xf]
y_start = data_registers[low_byte >> 4] y_start = data_registers[low_byte >> 4]
@ -239,14 +254,12 @@ def step():
y = y_start + dy y = y_start + dy
# Screen is 32 lines tall # Screen is 32 lines tall
if y >= 32: if y >= 32:
# TODO: Figure how y >= 32 works
continue continue
for dx in range(8): for dx in range(8):
x = x_start + dx x = x_start + dx
# Screen is 64 columns wide # Screen is 64 columns wide
if x >= 64: if x >= 64:
# TODO: Figure how x >= 64 works
continue continue
# Pixels are stored MSB-left # Pixels are stored MSB-left
@ -368,16 +381,26 @@ def tick_timers():
sound_timer = 0 sound_timer = 0
def advance_interpreter(dt): def advance_interpreter(dt):
global cpu_speed global cpu_clock
global cpu_cycles_to_go global screen, display_screen, screen_fps
# Each tic (60Hz) we need to run cpu_clock / 60Hz cycles global cpu_cycles_to_go, frames_to_go
# Each tic (60Hz) we need to run cpu_clock / 60Hz cycles and
# draw screen_fps / 60 frames
cpu_cycles_to_go += cpu_clock / 60 cpu_cycles_to_go += cpu_clock / 60
frames_to_go += screen_fps / 60
tick_timers() tick_timers()
while cpu_cycles_to_go >= 1: while cpu_cycles_to_go >= 1:
step() step()
cpu_cycles_to_go -= 1 cpu_cycles_to_go -= 1
if frames_to_go >= 1:
# Only draw the latest frame. This means FPS >60 won't work
# but why would you ever want to run chip-8 faster than
# that
display_screen = screen[:]
frames_to_go -= int(frames_to_go)
def initialize_ram(): def initialize_ram():
global ram, font_start global ram, font_start
ram = [0]*(1<<12) ram = [0]*(1<<12)
@ -613,8 +636,11 @@ def initialize_ram():
#ram[0x200:len(test_program) + 0x200] = test_program #ram[0x200:len(test_program) + 0x200] = test_program
def initialize_screen(): def initialize_screen():
global screen global screen, display_screen, screen_fps
screen = [False] * 64 * 32 screen = [False] * 64 * 32
display_screen = screen[:]
# TODO: Support changing FPS
screen_fps = 60
def initialize_timers(): def initialize_timers():
global delay_timer, sound_timer global delay_timer, sound_timer
@ -654,10 +680,14 @@ def load_program(f):
def main(): def main():
global window global window
global cpu_cycles_to_go global cpu_cycles_to_go, frames_to_go
# Don't hardcode the size # TODO: Don't hardcode the size
window = pyglet.window.Window(640, 320, resizable = True) window = pyglet.window.Window(640, 320, resizable = True)
# Don't use pulse driver, as it is really buggy and can crash the
# python process
pyglet.options['audio'] = ('openal', 'directsound', 'silent')
# Hook up our screen drawing routine # Hook up our screen drawing routine
@window.event @window.event
def on_draw(): def on_draw():
@ -680,10 +710,12 @@ def main():
initialize_keyboard() initialize_keyboard()
initialize_cpu() initialize_cpu()
# TODO: Deal with missing file gracefully
with open(sys.argv[1], 'rb') as f: with open(sys.argv[1], 'rb') as f:
load_program(f) load_program(f)
cpu_cycles_to_go = 0 cpu_cycles_to_go = 0
frames_to_go = 0
# Start the emulation # Start the emulation
pyglet.clock.schedule_interval(advance_interpreter, 1/60) pyglet.clock.schedule_interval(advance_interpreter, 1/60)