Add a draw sprint feature for reducing flicker
This commit is contained in:
parent
f8de8aa2cf
commit
5504050d56
28
README.md
28
README.md
|
@ -34,11 +34,33 @@ Emulation speed
|
|||
---------------
|
||||
Instructions are run 500 times a second
|
||||
|
||||
Draw sprint
|
||||
-----------
|
||||
To reduce sprite flicker, sipsi-8 can run upto two ticks' worth of cycles
|
||||
after a draw call. This is since chip-8 often does drawing in pairs, where
|
||||
the first in the pair erases the old sprite and the next one draws it back.
|
||||
If they happen on different frames, this results in very back flickering.
|
||||
|
||||
To avoid an issue of pairing one sprite's draw to other's erase, a sprint
|
||||
is invalidated if it hits a jump, call, or a return.
|
||||
|
||||
Games improved by draw sprint:
|
||||
* Brix and its descendants
|
||||
* UFO
|
||||
|
||||
Games unaffected by draw sprint:
|
||||
* Pong and Pong2 (Draws are too far from each other)
|
||||
* Tetris (Does jumps inbetween)
|
||||
* Blitz (Does jumps inbetween. If invalidation upon jump is disabled, ends
|
||||
up pairing wrong draw calls)
|
||||
|
||||
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
|
||||
* Keyboard can't be changed
|
||||
* FPS can't be changed
|
||||
* Interpreter speed can't be changed
|
||||
* Window size can't be changed
|
||||
* No error handing on opening a file
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
|
45
sipsi-8.py
45
sipsi-8.py
|
@ -79,9 +79,14 @@ def step():
|
|||
global delay_timer, sound_timer
|
||||
global keys_pressed, waiting_for_keypress, keypress_arrived
|
||||
|
||||
# Keep track of whether we did a draw or a jump, for the draw
|
||||
# sprint feature
|
||||
did_draw = False
|
||||
did_jump = False
|
||||
|
||||
# Don't execute any code in a waitstate, as it'd only end up
|
||||
# busylooping
|
||||
if waiting_for_keypress and not keypress_arrived: return
|
||||
if waiting_for_keypress and not keypress_arrived: return did_draw, did_jump
|
||||
|
||||
high_byte = ram[ip]
|
||||
ip = (ip + 1) & 0xfff
|
||||
|
@ -100,6 +105,7 @@ def step():
|
|||
# 00EE ret
|
||||
elif high_byte == 0x00 and low_byte == 0xEE:
|
||||
ip = stack.pop()
|
||||
did_jump = True
|
||||
|
||||
# 0nnn call_machine
|
||||
elif high_byte >> 4 == 0:
|
||||
|
@ -109,11 +115,13 @@ def step():
|
|||
# 1nnn jmp nnn
|
||||
elif high_byte >> 4 == 1:
|
||||
ip = ((high_byte & 0xf) << 8) | low_byte
|
||||
did_jump = True
|
||||
|
||||
# 2nnn call nnn
|
||||
elif high_byte >> 4 == 2:
|
||||
stack.append(ip)
|
||||
ip = ((high_byte & 0xf) << 8) | low_byte
|
||||
did_jump = True
|
||||
|
||||
# 3xnn skipeq vx, nn
|
||||
elif high_byte >> 4 == 3:
|
||||
|
@ -216,6 +224,7 @@ def step():
|
|||
elif high_byte >> 4 == 0xB:
|
||||
ip = ((high_byte & 0xf) << 8) | low_byte
|
||||
ip = (ip + data_registers[0]) & 0xfff
|
||||
did_jump = True
|
||||
|
||||
# Cxnn maskedrandom vx, nn
|
||||
elif high_byte >> 4 == 0xC:
|
||||
|
@ -242,6 +251,8 @@ def step():
|
|||
# have yet to find any games that break with it, I'm just
|
||||
# skipping pixels that fall outside the screen.
|
||||
|
||||
did_draw = True
|
||||
|
||||
x_start = data_registers[high_byte & 0xf]
|
||||
y_start = data_registers[low_byte >> 4]
|
||||
|
||||
|
@ -362,6 +373,8 @@ def step():
|
|||
else:
|
||||
print('%03x: Unrecognized!' % (ip-2))
|
||||
|
||||
return did_draw, did_jump
|
||||
|
||||
def tick_timers():
|
||||
global delay_timer, sound_timer
|
||||
|
||||
|
@ -391,9 +404,37 @@ def advance_interpreter(dt):
|
|||
tick_timers()
|
||||
|
||||
while cpu_cycles_to_go >= 1:
|
||||
step()
|
||||
did_draw, _ = step()
|
||||
cpu_cycles_to_go -= 1
|
||||
|
||||
# If we did a draw, run upto two ticks' cycles to reach
|
||||
# the next draw. Chip-8 draws are often paired, an erase
|
||||
# and a draw. If we don't hit both in the same frame, it
|
||||
# will cause flicker.
|
||||
# Since this will put the number of cycles to go below zero
|
||||
# if we run out of our own cycles, on average we will run
|
||||
# the right number of cycles per tick
|
||||
# Also, to avoid pairing draw half of one pair and erase
|
||||
# half of another, the sprint is invalidated by a jump, a
|
||||
# call or a ret
|
||||
if did_draw:
|
||||
#sprint_run_out = True#debg
|
||||
for cycle in range(2*cpu_clock // 60):
|
||||
did_draw, did_jump = step()
|
||||
cpu_cycles_to_go -= 1
|
||||
|
||||
# Exit as soon as we hit the other draw
|
||||
if did_draw:
|
||||
#sprint_run_out = False#debg
|
||||
#print('Sprint succeeded! %i' % cycle)#debg
|
||||
break
|
||||
|
||||
if did_jump:
|
||||
#sprint_run_out = False#debg
|
||||
#print('Sprint failed! (jump) %i' % cycle)#debg
|
||||
break
|
||||
#if sprint_run_out: print('Sprint failed! (ran out)')#debg
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue