Coverage for MC6809/components/mc6809_tools.py: 44%
62 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-06 19:50 +0100
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-06 19:50 +0100
1#!/usr/bin/env python
3"""
4 MC6809 - 6809 CPU emulator in Python
5 =======================================
7 6809 is Big-Endian
9 Links:
10 http://dragondata.worldofdragon.org/Publications/inside-dragon.htm
11 http://www.burgins.com/m6809.html
12 http://koti.mbnet.fi/~atjs/mc6809/
14 :copyleft: 2013-2015 by the MC6809 team, see AUTHORS for more details.
15 :license: GNU GPL v3 or above, see LICENSE for more details.
17 Based on:
18 * ApplyPy by James Tauber (MIT license)
19 * XRoar emulator by Ciaran Anscomb (GPL license)
20 more info, see README
21"""
24import _thread
25import inspect
26import queue
27import threading
28import time
29import warnings
32class CPUStatusThread(threading.Thread):
33 """
34 Send cycles/sec information via cpu_status_queue to the GUi main thread.
35 Just ignore if the cpu_status_queue is full.
36 """
38 def __init__(self, cpu, cpu_status_queue):
39 super().__init__(name="CPU-Status-Thread")
40 self.cpu = cpu
41 self.cpu_status_queue = cpu_status_queue
43 self.last_cpu_cycles = None
44 self.last_cpu_cycle_update = time.time()
46 def _run(self):
47 while self.cpu.running:
48 try:
49 self.cpu_status_queue.put(self.cpu.cycles, block=False)
50 except queue.Full:
51 # log.critical("Can't put CPU status: Queue is full.")
52 pass
53 time.sleep(0.5)
55 def run(self):
56 try:
57 self._run()
58 except BaseException:
59 self.cpu.running = False
60 _thread.interrupt_main()
61 raise
64class CPUThreadedStatusMixin:
65 def __init__(self, *args, **kwargs):
66 cpu_status_queue = kwargs.get("cpu_status_queue", None)
67 if cpu_status_queue is not None: 67 ↛ 68line 67 didn't jump to line 68, because the condition on line 67 was never true
68 status_thread = CPUStatusThread(self, cpu_status_queue)
69 status_thread.deamon = True
70 status_thread.start()
73class CPUTypeAssertMixin:
74 """
75 assert that all attributes of the CPU class will remain as the same.
77 We use no property, because it's slower. But without it, it's hard to find
78 if somewhere not .set() or .incement() is used.
80 With this helper a error will raise, if the type of a attribute will be
81 changed, e.g.:
82 cpu.index_x = ValueStorage16Bit(...)
83 cpu.index_x = 0x1234 # will raised a error
84 """
85 __ATTR_DICT = {}
87 def __init__(self, *args, **kwargs):
88 super().__init__(*args, **kwargs)
89 self.__set_attr_dict()
90 warnings.warn(
91 "CPU TypeAssert used! (Should be only activated for debugging!)"
92 )
94 def __set_attr_dict(self):
95 for name, obj in inspect.getmembers(self, lambda x: not (inspect.isroutine(x))):
96 if name.startswith("_") or name == "cfg":
97 continue
98 self.__ATTR_DICT[name] = type(obj)
100 def __setattr__(self, attr, value):
101 if attr in self.__ATTR_DICT:
102 obj = self.__ATTR_DICT[attr]
103 assert isinstance(value, obj), \
104 f"Attribute {attr!r} is no more type {obj} (Is now: {type(obj)})!"
105 return object.__setattr__(self, attr, value)
108def calc_new_count(min_value, value, max_value, trigger, target):
109 """
110 change 'value' between 'min_value' and 'max_value'
111 so that 'trigger' will be match 'target'
113 >>> calc_new_count(min_value=0, value=100, max_value=200, trigger=30, target=30)
114 100
116 >>> calc_new_count(min_value=0, value=100, max_value=200, trigger=50, target=5)
117 55
118 >>> calc_new_count(min_value=60, value=100, max_value=200, trigger=50, target=5)
119 60
121 >>> calc_new_count(min_value=0, value=100, max_value=200, trigger=20, target=40)
122 150
123 >>> calc_new_count(min_value=0, value=100, max_value=125, trigger=20, target=40)
124 125
125 """
126 try:
127 new_value = float(value) / float(trigger) * target
128 except ZeroDivisionError:
129 return value * 2
131 if new_value > max_value:
132 return max_value
134 new_value = int((value + new_value) / 2)
135 if new_value < min_value:
136 return min_value
137 return new_value