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

1#!/usr/bin/env python 

2 

3""" 

4 MC6809 - 6809 CPU emulator in Python 

5 ======================================= 

6 

7 6809 is Big-Endian 

8 

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/ 

13 

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. 

16 

17 Based on: 

18 * ApplyPy by James Tauber (MIT license) 

19 * XRoar emulator by Ciaran Anscomb (GPL license) 

20 more info, see README 

21""" 

22 

23 

24import _thread 

25import inspect 

26import queue 

27import threading 

28import time 

29import warnings 

30 

31 

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 """ 

37 

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 

42 

43 self.last_cpu_cycles = None 

44 self.last_cpu_cycle_update = time.time() 

45 

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) 

54 

55 def run(self): 

56 try: 

57 self._run() 

58 except BaseException: 

59 self.cpu.running = False 

60 _thread.interrupt_main() 

61 raise 

62 

63 

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() 

71 

72 

73class CPUTypeAssertMixin: 

74 """ 

75 assert that all attributes of the CPU class will remain as the same. 

76 

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. 

79 

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 = {} 

86 

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 ) 

93 

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) 

99 

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) 

106 

107 

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' 

112 

113 >>> calc_new_count(min_value=0, value=100, max_value=200, trigger=30, target=30) 

114 100 

115 

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 

120 

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 

130 

131 if new_value > max_value: 

132 return max_value 

133 

134 new_value = int((value + new_value) / 2) 

135 if new_value < min_value: 

136 return min_value 

137 return new_value