Add changeable FPS
This commit is contained in:
parent
3df40f58a5
commit
f8de8aa2cf
|
@ -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`)
|
||||||
|
|
58
sipsi-8.py
58
sipsi-8.py
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue