2 # (c) Copyright 2013 TJ <hacker@iam.tj>
3 # Licensed on the terms of the GNU General Public License version 3 (see COPYING)
5 # Base2 Runner: Circuit design and management
7 # TODO: module in development, not ready for any use yet
10 import os.path, pygame, engine
11 from library import enum
14 """ Models a straight line segment of a complete Trace """
15 # C-style pseudo-enum representing the directions a segment might run
16 directions = enum('N', 'NE', 'E', 'SE', 'S' , 'SW', 'W' , 'NW')
17 angles = list( 0 , 45 , 90, 135, 180, 225, 270, 315)
19 # references to an attached Via and its position
21 via_pos = enum('START', 'MID', 'END')
24 def __init__(self, length=1, direction=TraceSegment.directions.E, via=None, position=TraceSegment.via_pos.START):
25 """ default segment runs for one game tile length left-to-right
26 length: number of game 'tiles' this segment covers
29 # ensure all values are legal before doing any assignment
30 if not direction in TraceSegment.directions.reverse_mapping:
31 # 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle
32 raise IndexError('direction invalid')
33 elif not position in TraceSegment.via_pos:
34 # 'position' is an enum so ensure it is valid
35 raise IndexError('position invalid')
36 elif via and not isinstance(via, Via):
37 raise TypeError('expecting a Via')
40 self.angle = angles[direction]
42 self.via_position = position
44 def via_set(self, via, position):
45 """ link to the related Via
47 position: enum via_pos
49 if via and isinstance(via, Via):
50 if position in via_pos:
51 # FIXME: ensure the line above works for testing position's membership of via_pos
53 self.via_position = position
57 """ Models a single circuit trace made up of multiple segments """
58 segments = list() # ordered list of one or more TraceSegments
60 colours['metal'] = (192, 192, 192) # colours of the trace
61 colours['shadow'] = (64, 64, 64)
62 colours['reflection'] = (255, 255, 255)
64 def check_range(self, colour):
65 """ Ensure a colour value is legal """
66 return colour >= 0 and colour <= 255
68 def __init__(self, metal=None, shadow=None, reflection=None, segments=TraceSegment()):
69 # set the colours of the trace to give a 3D raised appearance
70 if metal and check_range(metal): self.colours['metal'] = metal
71 if shadow and check_range(shadow): self.colours['shadow'] = shadow
72 if reflection and check_range(reflection): self.colours['reflection'] = reflection
74 # initialise the via dictionary
75 with self.segments[0]:
77 via_position = via_pos.START
79 def draw(self, surface):
80 """ Draw the Trace onto pygame.Surface """
83 """ Models a through-hole otherwise known as a Via """
84 trace = None # the Trace this via is attached to
85 linked = None # the Via this via links to
89 """ Group of traces all going in the same direction """
90 width = 8 # default is an 8-bit bus
93 def __init__(self, buswidth=8):
94 """ Create a new bus consisting of 'buswidth' traces """
97 class IntegratedCircuit:
98 """ semiconductor component """
99 signals = None # key = signal name, value = pin number
102 """ populate pseudo-enum with common signal names """
103 if not IntegratedCircuit.signals:
104 signals = ('NC', 'GND', 'Vcc', 'Vdd')
105 # logic gate signal names (covers multi-gate components up to quad-gate)
106 for l in ('A', 'B', 'Y'):
107 for n in range(1, 5):
108 signals.append("%s%s" % (n, l))
110 IntegratedCircuit.signals = enum(signals)
113 class DualInLine(IntegratedCircuit):
114 """ Dual In-Line Integrated Circuit """
119 def __init__(self, pins):
120 if pins > 0 and pins % 2 == 0:
121 for p in range(1, pins+1):
122 pins[p] = IntegratedCircuit.signals.NC
126 """ Models a single logic gate """
132 def __init__(self, type, inputs=None):
133 # populate dictionary with common logic gate types
135 Gate.types = enum('AND', 'OR', 'XOR', 'NOT', 'NAND', 'NOR')
137 if type in Gate.types:
140 if inputs and type(inputs) is list and isinstance(inputs[0], bool):
143 # default to 2 inputs at logic 0
144 self.inputs = (False, False)
146 self.input_max = len(self.inputs)
150 """ Needs to be over-ridden in derived classes """
153 def set_input(self, input, value):
154 if input >= 0 and input < self.input_max:
155 if isinstance(value, bool):
156 self.inputs[input] = value
159 def get_output(self):
162 def min_inputs(self, inputs, minimum):
163 """ enforce a minimum number of logic inputs """
164 return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs))))
167 # Derive each of the common logic gate types
169 def __init__(self, inputs=None):
170 Gate.__init__(Gate.types.AND, self.min_inputs(inputs, 2))
173 self.output = self.inputs[0]
174 for i in range(1, self.input_max):
175 self.output &= self.inputs[i] # AND
179 def __init__(self, inputs=None):
180 Gate.__init__(Gate.types.OR, self.min_inputs(inputs, 2))
183 self.output = self.inputs[0]
184 for i in range (1, self.input_max):
185 self.output |= self.inputs[i] # OR
189 def __init__(self, inputs=None):
190 Gate.__init__(Gate.types.XOR, self.min_inputs(inputs, 2))
193 self.output = self.inputs[0]
194 for i in range (1, self.input_max):
195 self.output ^= self.inputs[i] # XOR
199 def __init__(self, inputs=(False, )):
201 # inverters can only have 1 input
202 inputs = (inputs[0], )
203 Gate.__init__(Gate.types.NOT, inputs)
206 self.output = ~self.inputs[0] # invert
209 class GateNAND(Gate):
210 def __init__(self, inputs=None):
211 Gate.__init__(Gate.types.NAND, self.min_inputs(inputs, 2))
214 self.output = self.inputs[0]
215 for i in range(1, self.input_max):
216 self.output &= self.inputs[i] # AND
217 self.output = ~self.output # invert
221 def __init__(self, inputs=None):
222 Gate.__init__(Gate.types.NOR, self.min_inputs(inputs, 2))
225 self.output = self.inputs[0]
226 for i in range (1, self.input_max):
227 self.output |= self.inputs[i] # OR
228 self.output = ~self.output # invert
232 """ Models an IC that contains one or more digital logic gates """
233 gates = list() # list of gates
236 def __init__(self, gates=(), package=DualInLine(14)):
237 if package: self.package = package
238 if type(gates) is list and type gates[0] is Gate: self.gates = gates
241 if gate and type(gate) is Gate: self.gates.append(gate)
244 class SN7400(ICLogic, DualInLine):
245 """ Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """
248 # call the super classes constructors
250 for i in range(0, 4):
251 gates.append(GateNAND())
252 # TODO: may need to move this functionality into an intermediate super class that combines package and Logic chip
253 ICLogic.__init__(gates)
254 DualInLine.__init(14)
257 class SN741G00(ICLogic, ...):
258 """ Model of a Texas Instruments SN741G00 single 2-input NAND gate """
261 class TSSOP(IntegratedCircuit):
262 """ Thin Shrink Small Outline Package """
266 def __init__(self, pins):
271 """ Models a digital electronics logic circuit printed on a circuit board """
277 def trace_add(self, trace_list):
280 def component_insert(self, component):
281 """ Adds a component to """