diff --git a/README.md b/README.md index 9fa0d77..a0effc3 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,12 @@ Emulation speed --------------- 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 ------------ * pyglet (`pip3 install pyglet`) diff --git a/sipsi-8.py b/sipsi-8.py index bbbb38a..25f58e3 100644 --- a/sipsi-8.py +++ b/sipsi-8.py @@ -40,10 +40,11 @@ def key_released(symbol): break def draw_screen(): + global display_screen to_draw = [] for y in range(32): for x in range(64): - if screen[y * 64 + x]: + if display_screen[y * 64 + x]: to_draw.append((x, y)) screen_points = [] @@ -223,10 +224,24 @@ def step(): # Dxyn draw vx, vy, n elif high_byte >> 4 == 0xD: - # TODO: OSCOM Nano manual (page 38) says "" Investigate how this wrapping - # should be implemented + # OSCOM Nano manual (page 38) says "", but I can't find any info on how to do this + # 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] y_start = data_registers[low_byte >> 4] @@ -239,14 +254,12 @@ def step(): y = y_start + dy # Screen is 32 lines tall if y >= 32: - # TODO: Figure how y >= 32 works continue for dx in range(8): x = x_start + dx # Screen is 64 columns wide if x >= 64: - # TODO: Figure how x >= 64 works continue # Pixels are stored MSB-left @@ -368,16 +381,26 @@ def tick_timers(): sound_timer = 0 def advance_interpreter(dt): - global cpu_speed - global cpu_cycles_to_go - # Each tic (60Hz) we need to run cpu_clock / 60Hz cycles + global cpu_clock + global screen, display_screen, screen_fps + 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 + frames_to_go += screen_fps / 60 tick_timers() while cpu_cycles_to_go >= 1: step() 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(): global ram, font_start ram = [0]*(1<<12) @@ -613,8 +636,11 @@ def initialize_ram(): #ram[0x200:len(test_program) + 0x200] = test_program def initialize_screen(): - global screen + global screen, display_screen, screen_fps screen = [False] * 64 * 32 + display_screen = screen[:] + # TODO: Support changing FPS + screen_fps = 60 def initialize_timers(): global delay_timer, sound_timer @@ -654,10 +680,14 @@ def load_program(f): def main(): global window - global cpu_cycles_to_go - # Don't hardcode the size + global cpu_cycles_to_go, frames_to_go + # TODO: Don't hardcode the size 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 @window.event def on_draw(): @@ -680,10 +710,12 @@ def main(): initialize_keyboard() initialize_cpu() + # TODO: Deal with missing file gracefully with open(sys.argv[1], 'rb') as f: load_program(f) cpu_cycles_to_go = 0 + frames_to_go = 0 # Start the emulation pyglet.clock.schedule_interval(advance_interpreter, 1/60)