diff --git a/.travis.yml b/.travis.yml index 574a3a01..89c08ab9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ after_success: branches: except: - wip + - WIP - experimental # whitelist diff --git a/README.creole b/README.creole index b221dc37..7eaf748f 100644 --- a/README.creole +++ b/README.creole @@ -9,19 +9,19 @@ The [[https://github.com/6809/MC6809|MC6809]] project is used to emulate the 680 | {{https://requires.io/github/jedie/DragonPy/requirements.svg?branch=master|Requirements Status on requires.io}} | [[https://requires.io/github/jedie/DragonPy/requirements/|requires.io/github/jedie/DragonPy/requirements/]] | -Dragon 32 and 64 ROMs in Text mode: +Dragon 32 with CPython 3 under Linux: -{{http://www.jensdiemer.de/static/jensdiemer.de/screenshots/20140805_DragonPy_Dragon64_01.png|screenshot Dragon 64}} +{{http://www.jensdiemer.de/static/jensdiemer.de/screenshots/20150820_DragonPy_Dragon32_CPython3_Linux_01.png|screenshot Dragon 32}} -CoCo with Extended Color Basic v1.1 in Text mode (running with PyPy): +CoCo with Extended Color Basic v1.1 in Text mode (running with PyPy under Windows): -{{http://www.jensdiemer.de/static/jensdiemer.de/screenshots/20140904_DragonPy_CoCo_ExtendedColorBasic.png|screenshot CoCo Extended Color BASIC}} +{{http://www.jensdiemer.de/static/jensdiemer.de/screenshots/20150820_DragonPy_CoCo2b_CPython2_Win_01.png|screenshot CoCo under Windows}} DragonPy is written in Python. -It's platform independent and runs with Python v2 and v3 and PyPy. -It's tested with Python 2.7.x and 3.4 and PyPy2. +It's platform independent and runs with Python and PyPy under Linux/Windows/OSX/... +It's tested with Python 2.7.x and 3.4, PyPy2 and PyPy3. DragonPy will not be a second XRoar written in Python. This project is primarily to lean and understand. @@ -435,6 +435,8 @@ generic syntax highlighter == History +* [[https://github.com/jedie/DragonPy/compare/v0.5.2...v0.5.3|24.08.2015 - v0.5.3]]: +** Bugfix for "freeze" after "speed limit" was activated * [[https://github.com/jedie/DragonPy/compare/v0.5.1...v0.5.2|20.08.2015 - v0.5.2]]: ** Add run 'MC6809 benchmark' button to 'starter GUI' ** bugfix 'file not found' in 'starter GUI' diff --git a/dragonpy/Dragon32/gui_config.py b/dragonpy/Dragon32/gui_config.py index 8130c28c..0bfa1703 100644 --- a/dragonpy/Dragon32/gui_config.py +++ b/dragonpy/Dragon32/gui_config.py @@ -5,6 +5,9 @@ DragonPy - Dragon 32 emulator in Python ======================================= + TODO: The config / speed limit stuff must be completely refactored! + + :created: 2014 by Jens Diemer - www.jensdiemer.de :copyleft: 2014 by the DragonPy team, see AUTHORS for more details. :license: GNU GPL v3 or above, see LICENSE for more details. @@ -12,6 +15,8 @@ from __future__ import absolute_import, division, print_function import logging +import sys +from MC6809.components.cpu6809 import CPU log = logging.getLogger(__name__) @@ -32,15 +37,33 @@ class RuntimeCfg(object): """ TODO: Load/save to ~/.DragonPy.ini + + TODO: refactor: move code to CPU! """ - speedlimit = False - cycles_per_sec = 888625 # cycles/sec - sync_op_count = 100 - max_run_time = 0.1 - max_burst_count = 1000 + speedlimit = False # run emulation in realtime or as fast as it can be? + + cycles_per_sec = 888625 # target cycles/sec if speed-limit is activated + + max_run_time = 0.01 # target duration of one CPU Op burst run + # important for CPU vs. GUI updates + + # Use the default value from MC6809 class: + min_burst_count = CPU.min_burst_count # minimum outer op count per burst + max_burst_count = CPU.max_burst_count # maximum outer op count per burst + max_delay = CPU.max_delay # maximum time.sleep() value per burst run + inner_burst_op_count = CPU.inner_burst_op_count # How many ops calls, before next sync call + + def __init(self): + is_pypy = hasattr(sys, 'pypy_version_info') + if is_pypy: + # Activate realtime mode if pypy is used + # TODO: add a automatic mode, that activate + # realtime mode if performance has enough reserves. + self.speedlimit = True def __setattr__(self, attr, value): log.critical("Set RuntimeCfg %r to: %r" % (attr, value)) + setattr(CPU, attr, value) # TODO: refactor! return object.__setattr__(self, attr, value) def load(self): @@ -50,6 +73,7 @@ def save(self): raise NotImplementedError("TODO!") + class BaseTkinterGUIConfig(object): """ 14.318180 Mhz crystal / 16 = 0.894886 MHz CPU frequency * 1000000 = 894886 cycles/sec @@ -125,20 +149,20 @@ def __init__(self, gui, runtime_cfg): row += 1 # - # CPU sync OP count - self.runtime_cfg.sync_op_count + # CPU sync OP count - self.runtime_cfg.inner_burst_op_count # - self.sync_op_count_var = tkinter.IntVar( - value=self.runtime_cfg.sync_op_count + self.inner_burst_op_count_var = tkinter.IntVar( + value=self.runtime_cfg.inner_burst_op_count ) - self.sync_op_count_entry = tkinter.Entry(self.root, - textvariable=self.sync_op_count_var, width=8, + self.inner_burst_op_count_entry = tkinter.Entry(self.root, + textvariable=self.inner_burst_op_count_var, width=8, ) - self.sync_op_count_entry.bind('', self.command_sync_op_count) - self.sync_op_count_entry.grid(row=row, column=1) - self.sync_op_count_label = tkinter.Label(self.root, - text="How many Ops should the CPU process before check sync calls e.g. IRQ (sync_op_count)" + self.inner_burst_op_count_entry.bind('', self.command_inner_burst_op_count) + self.inner_burst_op_count_entry.grid(row=row, column=1) + self.inner_burst_op_count_label = tkinter.Label(self.root, + text="How many Ops should the CPU process before check sync calls e.g. IRQ (inner_burst_op_count)" ) - self.sync_op_count_label.grid(row=row, column=2, sticky=tkinter.W) + self.inner_burst_op_count_label.grid(row=row, column=2, sticky=tkinter.W) row += 1 @@ -157,6 +181,24 @@ def __init__(self, gui, runtime_cfg): text="Max CPU op burst count (max_burst_count)" ) self.max_burst_count_label.grid(row=row, column=2, sticky=tkinter.W) + + row += 1 + + # + # max CPU burst delay - self.runtime_cfg.max_delay + # + self.max_delay_var = tkinter.DoubleVar( + value=self.runtime_cfg.max_delay + ) + self.max_delay_entry = tkinter.Entry(self.root, + textvariable=self.max_delay_var, width=8, + ) + self.max_delay_entry.bind('', self.command_max_delay) + self.max_delay_entry.grid(row=row, column=1) + self.max_delay_label = tkinter.Label(self.root, + text="Max CPU op burst delay (max_delay)" + ) + self.max_delay_label.grid(row=row, column=2, sticky=tkinter.W) self.root.update() @@ -164,6 +206,9 @@ def command_checkbutton_speedlimit(self, event=None): self.runtime_cfg.speedlimit = self.check_value_speedlimit.get() def command_cycles_per_sec(self, event=None): + """ + TODO: refactor: move code to CPU! + """ try: cycles_per_sec = self.cycles_per_sec_var.get() except ValueError: @@ -179,18 +224,34 @@ def command_cycles_per_sec(self, event=None): self.runtime_cfg.cycles_per_sec = cycles_per_sec - def command_sync_op_count(self, event=None): - """ CPU burst max running time - self.runtime_cfg.sync_op_count """ + def command_max_delay(self, event=None): + """ CPU burst max running time - self.runtime_cfg.max_delay """ + try: + max_delay = self.max_delay_var.get() + except ValueError: + max_delay = self.runtime_cfg.max_delay + + if max_delay < 0: + max_delay = self.runtime_cfg.max_delay + + if max_delay > 0.1: + max_delay = self.runtime_cfg.max_delay + + self.runtime_cfg.max_delay = max_delay + self.max_delay_var.set(self.runtime_cfg.max_delay) + + def command_inner_burst_op_count(self, event=None): + """ CPU burst max running time - self.runtime_cfg.inner_burst_op_count """ try: - sync_op_count = self.sync_op_count_var.get() + inner_burst_op_count = self.inner_burst_op_count_var.get() except ValueError: - sync_op_count = self.runtime_cfg.sync_op_count + inner_burst_op_count = self.runtime_cfg.inner_burst_op_count - if sync_op_count < 1: - sync_op_count = self.runtime_cfg.sync_op_count + if inner_burst_op_count < 1: + inner_burst_op_count = self.runtime_cfg.inner_burst_op_count - self.runtime_cfg.sync_op_count = sync_op_count - self.sync_op_count_var.set(self.runtime_cfg.sync_op_count) + self.runtime_cfg.inner_burst_op_count = inner_burst_op_count + self.inner_burst_op_count_var.set(self.runtime_cfg.inner_burst_op_count) def command_max_burst_count(self, event=None): """ max CPU burst op count - self.runtime_cfg.max_burst_count """ diff --git a/dragonpy/__init__.py b/dragonpy/__init__.py index 0b4a2587..2ea2ba8e 100644 --- a/dragonpy/__init__.py +++ b/dragonpy/__init__.py @@ -2,7 +2,7 @@ import sys -__version__ = "0.5.2" +__version__ = "0.5.3" # Used in setup.py and starter GUI to find the cli-executeable: diff --git a/dragonpy/core/gui.py b/dragonpy/core/gui.py index a2827e4d..abdf6588 100755 --- a/dragonpy/core/gui.py +++ b/dragonpy/core/gui.py @@ -266,7 +266,6 @@ def cpu_interval(self, interval=None): def update_status_interval(self, interval=500): # Update CPU settings: - self.machine.cpu.sync_op_count = self.runtime_cfg.sync_op_count self.machine.cpu.max_burst_count = self.runtime_cfg.max_burst_count new_cycles = self.machine.cpu.cycles - self.last_cpu_cycles @@ -275,22 +274,27 @@ def update_status_interval(self, interval=500): cycles_per_sec = new_cycles / duration msg = ( - "%s cylces/sec (~%s burst op count)\n" + "%s cylces/sec (burst op count: outer: %s - inner: %s)\n" "%i CPU interval calls" ) % ( locale_format_number(cycles_per_sec), - locale_format_number(self.machine.cpu.burst_op_count), + + locale_format_number(self.machine.cpu.outer_burst_op_count), + locale_format_number(self.machine.cpu.inner_burst_op_count), + self.cpu_interval_calls, ) if self.runtime_cfg.speedlimit: msg += ( - "\ncylces/sec diff: %s" - ) % ( - locale_format_number( - abs(self.runtime_cfg.cycles_per_sec - cycles_per_sec) - ), - ) + " (Burst delay: %f)\nSpeed target: %s cylces/sec - diff: %s cylces/sec" + ) % ( + self.machine.cpu.delay, + locale_format_number(self.runtime_cfg.cycles_per_sec), + locale_format_number( + cycles_per_sec - self.runtime_cfg.cycles_per_sec + ), + ) self.status.set(msg) diff --git a/dragonpy/tests/test_cli.py b/dragonpy/tests/test_cli.py index cd6c5fb1..5dd450ee 100644 --- a/dragonpy/tests/test_cli.py +++ b/dragonpy/tests/test_cli.py @@ -58,12 +58,12 @@ class TestStarter(CliTestCase): """ Test the "starter functions" that invoke DragonPy / MC6809 via subprocess. """ - def _run(self, func, *args, verbose=False): + def _run(self, func, *args, **kwargs): p = func(*args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, - verbose=verbose + **kwargs ) retcode = p.wait() diff --git a/setup.py b/setup.py index 1337612f..ecbe0e8c 100755 --- a/setup.py +++ b/setup.py @@ -207,8 +207,8 @@ def rmtree(path): py_modules=["DragonPy"], provides=["DragonPy"], install_requires=[ - "dragonlib", - "MC6809", + "dragonlib>=0.1.7", + "MC6809>=0.4.6", "pygments", "click", "six",