]> sigrok.org Git - libsigrokdecode.git/blob - decoders/ieee488/pd.py
ieee488: introduce unified IEEE-488 decoder (supports GPIB and IEC)
[libsigrokdecode.git] / decoders / ieee488 / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
5 ## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
6 ## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
7 ##
8 ## This program is free software; you can redistribute it and/or modify
9 ## it under the terms of the GNU General Public License as published by
10 ## the Free Software Foundation; either version 2 of the License, or
11 ## (at your option) any later version.
12 ##
13 ## This program is distributed in the hope that it will be useful,
14 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ## GNU General Public License for more details.
17 ##
18 ## You should have received a copy of the GNU General Public License
19 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ##
21
22 # This file was created from earlier implementations of the 'gpib' and
23 # the 'iec' protocol decoders. It combines the parallel and the serial
24 # transmission variants in a single instance with optional inputs for
25 # maximum code re-use.
26
27 # TODO
28 # - Extend annotations for improved usability.
29 #   - Keep talkers' data streams on separate annotation rows? Is this useful
30 #     here at the GPIB level, or shall stacked decoders dispatch these? May
31 #     depend on how often captures get inspected which involve multiple peers.
32 #   - Make serial bit annotations optional? Could slow down interactive
33 #     exploration for long captures (see USB).
34 # - Move the inlined Commodore IEC peripherals support to a stacked decoder
35 #   when more peripherals get added.
36 # - SCPI over GPIB may "represent somewhat naturally" here already when
37 #   text lines are a single run of data at the GPIB layer (each line between
38 #   the address spec and either EOI or ATN). So a stacked SCPI decoder may
39 #   only become necessary when the text lines' content shall get inspected.
40
41 import sigrokdecode as srd
42 from common.srdhelper import bitpack
43
44 '''
45 OUTPUT_PYTHON format for stacked decoders:
46
47 General packet format:
48 [<ptype>, <addr>, <pdata>]
49
50 This is the list of <ptype>s and their respective <pdata> values:
51
52 Raw bits and bytes at the physical transport level:
53  - 'IEC_BIT': <addr> is not applicable, <pdata> is the transport's bit value.
54  - 'GPIB_RAW': <addr> is not applicable, <pdata> is the transport's
55    byte value. Data bytes are in the 0x00-0xff range, command/address
56    bytes are in the 0x100-0x1ff range.
57
58 GPIB level byte fields (commands, addresses, pieces of data):
59  - 'COMMAND': <addr> is not applicable, <pdata> is the command's byte value.
60  - 'LISTEN': <addr> is the listener address (0-30), <pdata> is the raw
61    byte value (including the 0x20 offset).
62  - 'TALK': <addr> is the talker address (0-30), <pdata> is the raw byte
63    value (including the 0x40 offset).
64  - 'SECONDARY': <addr> is the secondary address (0-31), <pdata> is the
65    raw byte value (including the 0x60 offset).
66  - 'MSB_SET': <addr> as well as <pdata> are the raw byte value (including
67    the 0x80 offset). This usually does not happen for GPIB bytes with ATN
68    active, but was observed with the IEC bus and Commodore floppy drives,
69    when addressing channels within the device.
70  - 'DATA_BYTE': <addr> is the talker address (when available), <pdata>
71    is the raw data byte (transport layer, ATN inactive).
72
73 Extracted payload information (peers and their communicated data):
74  - 'TALK_LISTEN': <addr> is the current talker, <pdata> is the list of
75    current listeners. These updates for the current "connected peers"
76    are sent when the set of peers changes, i.e. after talkers/listeners
77    got selected or deselected. Of course the data only covers what could
78    be gathered from the input data. Some controllers may not explicitly
79    address themselves, or captures may not include an early setup phase.
80  - 'TALKER_BYTES': <addr> is the talker address (when available), <pdata>
81    is the accumulated byte sequence between addressing a talker and EOI,
82    or the next command/address.
83  - 'TALKER_TEXT': <addr> is the talker address (when available), <pdata>
84    is the accumulated text sequence between addressing a talker and EOI,
85    or the next command/address.
86 '''
87
88 class ChannelError(Exception):
89     pass
90
91 def _format_ann_texts(fmts, **args):
92     if not fmts:
93         return None
94     return [fmt.format(**args) for fmt in fmts]
95
96 _cmd_table = {
97     # Command codes in the 0x00-0x1f range.
98     0x01: ['Go To Local', 'GTL'],
99     0x04: ['Selected Device Clear', 'SDC'],
100     0x05: ['Parallel Poll Configure', 'PPC'],
101     0x08: ['Global Execute Trigger', 'GET'],
102     0x09: ['Take Control', 'TCT'],
103     0x11: ['Local Lock Out', 'LLO'],
104     0x14: ['Device Clear', 'DCL'],
105     0x15: ['Parallel Poll Unconfigure', 'PPU'],
106     0x18: ['Serial Poll Enable', 'SPE'],
107     0x19: ['Serial Poll Disable', 'SPD'],
108     # Unknown type of command.
109     None: ['Unknown command 0x{cmd:02x}', 'command 0x{cmd:02x}', 'cmd {cmd:02x}', 'C{cmd_ord:c}'],
110     # Special listener/talker "addresses" (deselecting previous peers).
111     0x3f: ['Unlisten', 'UNL'],
112     0x5f: ['Untalk', 'UNT'],
113 }
114
115 def _is_command(b):
116     # Returns a tuple of booleans (or None when not applicable) whether
117     # the raw GPIB byte is: a command, an un-listen, an un-talk command.
118     if b in range(0x00, 0x20):
119         return True, None, None
120     if b in range(0x20, 0x40) and (b & 0x1f) == 31:
121         return True, True, False
122     if b in range(0x40, 0x60) and (b & 0x1f) == 31:
123         return True, False, True
124     return False, None, None
125
126 def _is_listen_addr(b):
127     if b in range(0x20, 0x40):
128         return b & 0x1f
129     return None
130
131 def _is_talk_addr(b):
132     if b in range(0x40, 0x60):
133         return b & 0x1f
134     return None
135
136 def _is_secondary_addr(b):
137     if b in range(0x60, 0x80):
138         return b & 0x1f
139     return None
140
141 def _is_msb_set(b):
142     if b & 0x80:
143         return b
144     return None
145
146 def _get_raw_byte(b, atn):
147     # "Decorate" raw byte values for stacked decoders.
148     raw_byte = b
149     if atn:
150         raw_byte |= 0x100
151     return raw_byte
152
153 def _get_raw_text(b, atn):
154     return ['{leader}{data:02x}'.format(leader = '/' if atn else '', data = b)]
155
156 def _get_command_texts(b):
157     fmts = _cmd_table.get(b, None)
158     known = fmts is not None
159     if not fmts:
160         fmts = _cmd_table.get(None, None)
161     if not fmts:
162         return known, None
163     return known, _format_ann_texts(fmts, cmd = b, cmd_ord = ord('0') + b)
164
165 def _get_address_texts(b):
166     laddr = _is_listen_addr(b)
167     taddr = _is_talk_addr(b)
168     saddr = _is_secondary_addr(b)
169     msb = _is_msb_set(b)
170     fmts = None
171     if laddr is not None:
172         fmts = ['Listen {addr:d}', 'L {addr:d}', 'L{addr_ord:c}']
173         addr = laddr
174     elif taddr is not None:
175         fmts = ['Talk {addr:d}', 'T {addr:d}', 'T{addr_ord:c}']
176         addr = taddr
177     elif saddr is not None:
178         fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
179         addr = saddr
180     elif msb is not None: # For IEC bus compat.
181         fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
182         addr = msb
183     return _format_ann_texts(fmts, addr = addr, addr_ord = ord('0') + addr)
184
185 def _get_data_text(b):
186     # TODO Move the table of ASCII control characters to a common location?
187     # TODO Move the "printable with escapes" logic to a common helper?
188     _control_codes = {
189         0x00: 'NUL',
190         0x01: 'SOH',
191         0x02: 'STX',
192         0x03: 'ETX',
193         0x04: 'EOT',
194         0x05: 'ENQ',
195         0x06: 'ACK',
196         0x07: 'BEL',
197         0x08: 'BS',
198         0x09: 'TAB',
199         0x0a: 'LF',
200         0x0b: 'VT',
201         0x0c: 'FF',
202         0x0d: 'CR',
203         0x0e: 'SO',
204         0x0f: 'SI',
205         0x10: 'DLE',
206         0x11: 'DC1',
207         0x12: 'DC2',
208         0x13: 'DC3',
209         0x14: 'DC4',
210         0x15: 'NAK',
211         0x16: 'SYN',
212         0x17: 'ETB',
213         0x18: 'CAN',
214         0x19: 'EM',
215         0x1a: 'SUB',
216         0x1b: 'ESC',
217         0x1c: 'FS',
218         0x1d: 'GS',
219         0x1e: 'RS',
220         0x1f: 'US',
221     }
222     # Yes, exclude 0x7f (DEL) here. It's considered non-printable.
223     if b in range(0x20, 0x7f) and b not in ('[', ']'):
224         return '{:s}'.format(chr(b))
225     elif b in _control_codes:
226         return '[{:s}]'.format(_control_codes[b])
227     # Use a compact yet readable and unambigous presentation for bytes
228     # which contain non-printables. The format that is used here is
229     # compatible with 93xx EEPROM and UART decoders.
230     return '[{:02x}]'.format(b)
231
232 (
233     PIN_DIO1, PIN_DIO2, PIN_DIO3, PIN_DIO4,
234     PIN_DIO5, PIN_DIO6, PIN_DIO7, PIN_DIO8,
235     PIN_EOI, PIN_DAV, PIN_NRFD, PIN_NDAC,
236     PIN_IFC, PIN_SRQ, PIN_ATN, PIN_REN,
237     PIN_CLK,
238 ) = range(17)
239 PIN_DATA = PIN_DIO1
240
241 (
242     ANN_RAW_BIT, ANN_RAW_BYTE,
243     ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
244     ANN_EOI,
245     ANN_TEXT,
246     # TODO Want to provide one annotation class per talker address (0-30)?
247     ANN_IEC_PERIPH,
248     ANN_WARN,
249 ) = range(11)
250
251 (
252     BIN_RAW,
253     BIN_DATA,
254     # TODO Want to provide one binary annotation class per talker address (0-30)?
255 ) = range(2)
256
257 class Decoder(srd.Decoder):
258     api_version = 3
259     id = 'ieee488'
260     name = 'IEEE-488'
261     longname = 'General Purpose Interface Bus'
262     desc = 'IEEE-488 General Purpose Interface Bus (GPIB/HPIB or IEC).'
263     license = 'gplv2+'
264     inputs = ['logic']
265     outputs = ['ieee488']
266     tags = ['PC', 'Retro computing']
267     channels = (
268         {'id': 'dio1' , 'name': 'DIO1/DATA',
269             'desc': 'Data I/O bit 1, or serial data'},
270     )
271     optional_channels = (
272         {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
273         {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
274         {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
275         {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
276         {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
277         {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
278         {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
279         {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
280         {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
281         {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
282         {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
283         {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
284         {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
285         {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
286         {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
287         {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
288     )
289     options = (
290         {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
291             'default': 'no', 'values': ('no', 'yes')},
292     )
293     annotations = (
294         ('bit', 'IEC bit'),
295         ('raw', 'Raw byte'),
296         ('cmd', 'Command'),
297         ('laddr', 'Listener address'),
298         ('taddr', 'Talker address'),
299         ('saddr', 'Secondary address'),
300         ('data', 'Data byte'),
301         ('eoi', 'EOI'),
302         ('text', 'Talker text'),
303         ('periph', 'IEC bus peripherals'),
304         ('warn', 'Warning'),
305     )
306     annotation_rows = (
307         ('bits', 'IEC bits', (ANN_RAW_BIT,)),
308         ('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
309         ('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
310         ('eois', 'EOI', (ANN_EOI,)),
311         ('texts', 'Talker texts', (ANN_TEXT,)),
312         ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
313         ('warns', 'Warnings', (ANN_WARN,)),
314     )
315     binary = (
316         ('raw', 'Raw bytes'),
317         ('data', 'Talker bytes'),
318     )
319
320     def __init__(self):
321         self.reset()
322
323     def reset(self):
324         self.curr_raw = None
325         self.curr_atn = None
326         self.curr_eoi = None
327         self.latch_atn = None
328         self.latch_eoi = None
329         self.accu_bytes = []
330         self.accu_text = []
331         self.ss_raw = None
332         self.es_raw = None
333         self.ss_eoi = None
334         self.es_eoi = None
335         self.ss_text = None
336         self.es_text = None
337         self.last_talker = None
338         self.last_listener = []
339         self.last_iec_addr = None
340         self.last_iec_sec = None
341
342     def start(self):
343         self.out_ann = self.register(srd.OUTPUT_ANN)
344         self.out_bin = self.register(srd.OUTPUT_BINARY)
345         self.out_python = self.register(srd.OUTPUT_PYTHON)
346
347     def putg(self, ss, es, data):
348         self.put(ss, es, self.out_ann, data)
349
350     def putbin(self, ss, es, data):
351         self.put(ss, es, self.out_bin, data)
352
353     def putpy(self, ss, es, ptype, addr, pdata):
354         self.put(ss, es, self.out_python, [ptype, addr, pdata])
355
356     def emit_eoi_ann(self, ss, es):
357         self.putg(ss, es, [ANN_EOI, ['EOI']])
358
359     def emit_bin_ann(self, ss, es, ann_cls, data):
360         self.putbin(ss, es, [ann_cls, bytes(data)])
361
362     def emit_data_ann(self, ss, es, ann_cls, data):
363         self.putg(ss, es, [ann_cls, data])
364
365     def emit_warn_ann(self, ss, es, data):
366         self.putg(ss, es, [ANN_WARN, data])
367
368     def flush_bytes_text_accu(self):
369         if self.accu_bytes and self.ss_text is not None and self.es_text is not None:
370             self.emit_bin_ann(self.ss_text, self.es_text, BIN_DATA, bytearray(self.accu_bytes))
371             self.putpy(self.ss_text, self.es_text, 'TALKER_BYTES', self.last_talker, bytearray(self.accu_bytes))
372             self.accu_bytes = []
373         if self.accu_text and self.ss_text is not None and self.es_text is not None:
374             text = ''.join(self.accu_text)
375             self.emit_data_ann(self.ss_text, self.es_text, ANN_TEXT, [text])
376             self.putpy(self.ss_text, self.es_text, 'TALKER_TEXT', self.last_talker, text)
377             self.accu_text = []
378         self.ss_text = self.es_text = None
379
380     def handle_ifc_change(self, ifc):
381         # Track IFC line for parallel input.
382         # Assertion of IFC de-selects all talkers and listeners.
383         if ifc:
384             self.last_talker = None
385             self.last_listener = []
386
387     def handle_eoi_change(self, eoi):
388         # Track EOI line for parallel and serial input.
389         if eoi:
390             self.ss_eoi = self.samplenum
391             self.curr_eoi = eoi
392         else:
393             self.es_eoi = self.samplenum
394             if self.ss_eoi and self.latch_eoi:
395                self.emit_eoi_ann(self.ss_eoi, self.es_eoi)
396             self.es_text = self.es_eoi
397             self.flush_bytes_text_accu()
398             self.ss_eoi = self.es_eoi = None
399             self.curr_eoi = None
400
401     def handle_atn_change(self, atn):
402         # Track ATN line for parallel and serial input.
403         self.curr_atn = atn
404         if atn:
405             self.flush_bytes_text_accu()
406
407     def handle_iec_periph(self, ss, es, addr, sec, data):
408         # The annotation is optional.
409         if self.options['iec_periph'] != 'yes':
410             return
411         # Void internal state.
412         if addr is None and sec is None and data is None:
413             self.last_iec_addr = None
414             self.last_iec_sec = None
415             return
416         # Grab and evaluate new input.
417         _iec_addr_names = {
418             # TODO Add more items here. See the "Device numbering" section
419             # of the https://en.wikipedia.org/wiki/Commodore_bus page.
420             8: 'Disk 0',
421             9: 'Disk 1',
422         }
423         _iec_disk_range = range(8, 16)
424         if addr is not None:
425             self.last_iec_addr = addr
426             name = _iec_addr_names.get(addr, None)
427             if name:
428                 self.emit_data_ann(ss, es, ANN_IEC_PERIPH, [name])
429         addr = self.last_iec_addr # Simplify subsequent logic.
430         if sec is not None:
431             # BEWARE! The secondary address is a full byte and includes
432             # the 0x60 offset, to also work when the MSB was set.
433             self.last_iec_sec = sec
434             subcmd, channel = sec & 0xf0, sec & 0x0f
435             channel_ord = ord('0') + channel
436             if addr is not None and addr in _iec_disk_range:
437                 subcmd_fmts = {
438                     0x60: ['Reopen {ch:d}', 'Re {ch:d}', 'R{ch_ord:c}'],
439                     0xe0: ['Close {ch:d}', 'Cl {ch:d}', 'C{ch_ord:c}'],
440                     0xf0: ['Open {ch:d}', 'Op {ch:d}', 'O{ch_ord:c}'],
441                 }.get(subcmd, None)
442                 if subcmd_fmts:
443                     texts = _format_ann_texts(subcmd_fmts, ch = channel, ch_ord = channel_ord)
444                     self.emit_data_ann(ss, es, ANN_IEC_PERIPH, texts)
445         sec = self.last_iec_sec # Simplify subsequent logic.
446         if data is not None:
447             if addr is None or sec is None:
448                 return
449             # TODO Process data depending on peripheral type and channel?
450
451     def handle_data_byte(self):
452         b = self.curr_raw
453         texts = _get_raw_text(b, self.curr_atn)
454         self.emit_data_ann(self.ss_raw, self.es_raw, ANN_RAW_BYTE, texts)
455         self.emit_bin_ann(self.ss_raw, self.es_raw, BIN_RAW, b.to_bytes(1, byteorder='big'))
456         self.putpy(self.ss_raw, self.es_raw, 'GPIB_RAW', None, _get_raw_byte(b, self.curr_atn))
457         if self.curr_atn:
458             ann_cls = None
459             upd_iec = False,
460             py_type = None
461             py_peers = False
462             is_cmd, is_unl, is_unt = _is_command(b)
463             laddr = _is_listen_addr(b)
464             taddr = _is_talk_addr(b)
465             saddr = _is_secondary_addr(b)
466             msb = _is_msb_set(b)
467             if is_cmd:
468                 known, texts = _get_command_texts(b)
469                 if not known:
470                     warn_texts = ['Unknown GPIB command', 'unknown', 'UNK']
471                     self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
472                 ann_cls = ANN_CMD
473                 py_type, py_addr = 'COMMAND', None
474                 if is_unl:
475                     self.last_listener = []
476                     py_peers = True
477                 if is_unt:
478                     self.last_talker = None
479                     py_peers = True
480                 if is_unl or is_unt:
481                     upd_iec = True, None, None, None
482             elif laddr is not None:
483                 addr = laddr
484                 texts = _get_address_texts(b)
485                 ann_cls = ANN_LADDR
486                 py_type, py_addr = 'LISTEN', addr
487                 if addr == self.last_talker:
488                     self.last_talker = None
489                 self.last_listener.append(addr)
490                 upd_iec = True, addr, None, None
491                 py_peers = True
492             elif taddr is not None:
493                 addr = taddr
494                 texts = _get_address_texts(b)
495                 ann_cls = ANN_TADDR
496                 py_type, py_addr = 'TALK', addr
497                 if addr in self.last_listener:
498                     self.last_listener.remove(addr)
499                 self.last_talker = addr
500                 upd_iec = True, addr, None, None
501                 py_peers = True
502             elif saddr is not None:
503                 addr = saddr
504                 texts = _get_address_texts(b)
505                 ann_cls = ANN_SADDR
506                 upd_iec = True, None, b, None
507                 py_type, py_addr = 'SECONDARY', addr
508             elif msb is not None:
509                 # These are not really "secondary addresses", but they
510                 # are used by the Commodore IEC bus (floppy channels).
511                 texts = _get_address_texts(b)
512                 ann_cls = ANN_SADDR
513                 upd_iec = True, None, b, None
514                 py_type, py_addr = 'MSB_SET', b
515             if ann_cls is not None and texts is not None:
516                 self.emit_data_ann(self.ss_raw, self.es_raw, ann_cls, texts)
517             if upd_iec[0]:
518                 self.handle_iec_periph(self.ss_raw, self.es_raw, upd_iec[1], upd_iec[2], upd_iec[3])
519             if py_type:
520                 self.putpy(self.ss_raw, self.es_raw, py_type, py_addr, b)
521             if py_peers:
522                 self.last_listener.sort()
523                 self.putpy(self.ss_raw, self.es_raw, 'TALK_LISTEN', self.last_talker, self.last_listener)
524         else:
525             self.accu_bytes.append(b)
526             text = _get_data_text(b)
527             if not self.accu_text:
528                 self.ss_text = self.ss_raw
529             self.accu_text.append(text)
530             self.es_text = self.es_raw
531             self.emit_data_ann(self.ss_raw, self.es_raw, ANN_DATA, [text])
532             self.handle_iec_periph(self.ss_raw, self.es_raw, None, None, b)
533             self.putpy(self.ss_raw, self.es_raw, 'DATA_BYTE', self.last_talker, b)
534
535     def handle_dav_change(self, dav, data):
536         if dav:
537             # Data availability starts when the flag goes active.
538             self.ss_raw = self.samplenum
539             self.curr_raw = bitpack(data)
540             self.latch_atn = self.curr_atn
541             self.latch_eoi = self.curr_eoi
542             return
543         # Data availability ends when the flag goes inactive. Handle the
544         # previously captured data byte according to associated flags.
545         self.es_raw = self.samplenum
546         self.handle_data_byte()
547         self.ss_raw = self.es_raw = None
548         self.curr_raw = None
549
550     def inject_dav_phase(self, ss, es, data):
551         # Inspection of serial input has resulted in one raw byte which
552         # spans a given period of time. Pretend we had seen a DAV active
553         # phase, to re-use code for the parallel transmission.
554         self.ss_raw = ss
555         self.curr_raw = bitpack(data)
556         self.latch_atn = self.curr_atn
557         self.latch_eoi = self.curr_eoi
558         self.es_raw = es
559         self.handle_data_byte()
560         self.ss_raw = self.es_raw = None
561         self.curr_raw = None
562
563     def invert_pins(self, pins):
564         # All lines (including data bits!) are low active and thus need
565         # to get inverted to receive their logical state (high active,
566         # regular data bit values). Cope with inputs being optional.
567         return [1 - p if p in (0, 1) else p for p in pins]
568
569     def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
570         if not has_clk or not has_data_1 or not has_atn:
571             raise ChannelError('IEC bus needs at least ATN and serial CLK and DATA.')
572
573         # This is a rephrased version of decoders/iec/pd.py:decode().
574         # SRQ was not used there either. Magic numbers were eliminated.
575         (
576             STEP_WAIT_READY_TO_SEND,
577             STEP_WAIT_READY_FOR_DATA,
578             STEP_PREP_DATA_TEST_EOI,
579             STEP_CLOCK_DATA_BITS,
580         ) = range(4)
581         step_wait_conds = (
582             [{PIN_ATN: 'f'}, {PIN_DATA: 'l', PIN_CLK: 'h'}],
583             [{PIN_ATN: 'f'}, {PIN_DATA: 'h', PIN_CLK: 'h'}, {PIN_CLK: 'l'}],
584             [{PIN_ATN: 'f'}, {PIN_DATA: 'f'}, {PIN_CLK: 'l'}],
585             [{PIN_ATN: 'f'}, {PIN_CLK: 'e'}],
586         )
587         step = STEP_WAIT_READY_TO_SEND
588         bits = []
589
590         while True:
591
592             # Sample input pin values. Keep DATA/CLK in verbatim form to
593             # re-use 'iec' decoder logic. Turn ATN to positive logic for
594             # easier processing. The data bits get handled during byte
595             # accumulation.
596             pins = self.wait(step_wait_conds[step])
597             data, clk = pins[PIN_DATA], pins[PIN_CLK]
598             atn, = self.invert_pins([pins[PIN_ATN]])
599
600             if self.matched[0]:
601                 # Falling edge on ATN, reset step.
602                 step = STEP_WAIT_READY_TO_SEND
603
604             if step == STEP_WAIT_READY_TO_SEND:
605                 # Don't use self.matched[1] here since we might come from
606                 # a step with different conds due to the code above.
607                 if data == 0 and clk == 1:
608                     # Rising edge on CLK while DATA is low: Ready to send.
609                     step = STEP_WAIT_READY_FOR_DATA
610             elif step == STEP_WAIT_READY_FOR_DATA:
611                 if data == 1 and clk == 1:
612                     # Rising edge on DATA while CLK is high: Ready for data.
613                     ss_byte = self.samplenum
614                     self.handle_atn_change(atn)
615                     if self.curr_eoi:
616                         self.handle_eoi_change(False)
617                     bits = []
618                     step = STEP_PREP_DATA_TEST_EOI
619                 elif clk == 0:
620                     # CLK low again, transfer aborted.
621                     step = STEP_WAIT_READY_TO_SEND
622             elif step == STEP_PREP_DATA_TEST_EOI:
623                 if data == 0 and clk == 1:
624                     # DATA goes low while CLK is still high, EOI confirmed.
625                     self.handle_eoi_change(True)
626                 elif clk == 0:
627                     step = STEP_CLOCK_DATA_BITS
628                     ss_bit = self.samplenum
629             elif step == STEP_CLOCK_DATA_BITS:
630                 if self.matched[1]:
631                     if clk == 1:
632                         # Rising edge on CLK; latch DATA.
633                         bits.append(data)
634                     elif clk == 0:
635                         # Falling edge on CLK; end of bit.
636                         es_bit = self.samplenum
637                         self.emit_data_ann(ss_bit, es_bit, ANN_RAW_BIT, ['{:d}'.format(bits[-1])])
638                         self.putpy(ss_bit, es_bit, 'IEC_BIT', None, bits[-1])
639                         ss_bit = self.samplenum
640                         if len(bits) == 8:
641                             es_byte = self.samplenum
642                             self.inject_dav_phase(ss_byte, es_byte, bits)
643                             if self.curr_eoi:
644                                 self.handle_eoi_change(False)
645                             step = STEP_WAIT_READY_TO_SEND
646
647     def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
648
649         if False in has_data_n or not has_dav or not has_atn:
650             raise ChannelError('IEEE-488 needs at least ATN and DAV and eight DIO lines.')
651         has_ifc = self.has_channel(PIN_IFC)
652
653         # Capture data lines at the falling edge of DAV, process their
654         # values at rising DAV edge (when data validity ends). Also make
655         # sure to start inspection when the capture happens to start with
656         # low signal levels, i.e. won't include the initial falling edge.
657         # Scan for ATN/EOI edges as well (including the trick which works
658         # around initial pin state).
659         # Map low-active physical transport lines to positive logic here,
660         # to simplify logical inspection/decoding of communicated data,
661         # and to avoid redundancy and inconsistency in later code paths.
662         waitcond = []
663         idx_dav = len(waitcond)
664         waitcond.append({PIN_DAV: 'l'})
665         idx_atn = len(waitcond)
666         waitcond.append({PIN_ATN: 'l'})
667         idx_eoi = None
668         if has_eoi:
669             idx_eoi = len(waitcond)
670             waitcond.append({PIN_EOI: 'l'})
671         idx_ifc = None
672         if has_ifc:
673             idx_ifc = len(waitcond)
674             waitcond.append({PIN_IFC: 'l'})
675         while True:
676             pins = self.wait(waitcond)
677             pins = self.invert_pins(pins)
678
679             # BEWARE! Order of evaluation does matter. For low samplerate
680             # captures, many edges fall onto the same sample number. So
681             # we process active edges of flags early (before processing
682             # data bits), and inactive edges late (after data got processed).
683             if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
684                 self.handle_ifc_change(pins[PIN_IFC])
685             if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
686                 self.handle_eoi_change(pins[PIN_EOI])
687             if self.matched[idx_atn] and pins[PIN_ATN] == 1:
688                 self.handle_atn_change(pins[PIN_ATN])
689             if self.matched[idx_dav]:
690                 self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
691             if self.matched[idx_atn] and pins[PIN_ATN] == 0:
692                 self.handle_atn_change(pins[PIN_ATN])
693             if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
694                 self.handle_eoi_change(pins[PIN_EOI])
695             if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
696                 self.handle_ifc_change(pins[PIN_IFC])
697
698             waitcond[idx_dav][PIN_DAV] = 'e'
699             waitcond[idx_atn][PIN_ATN] = 'e'
700             if has_eoi:
701                 waitcond[idx_eoi][PIN_EOI] = 'e'
702             if has_ifc:
703                 waitcond[idx_ifc][PIN_IFC] = 'e'
704
705     def decode(self):
706         # The decoder's boilerplate declares some of the input signals as
707         # optional, but only to support both serial and parallel variants.
708         # The CLK signal discriminates the two. For either variant some
709         # of the "optional" signals are not really optional for proper
710         # operation of the decoder. Check these conditions here.
711         has_clk = self.has_channel(PIN_CLK)
712         has_data_1 = self.has_channel(PIN_DIO1)
713         has_data_n = [bool(self.has_channel(pin) for pin in range(PIN_DIO1, PIN_DIO8 + 1))]
714         has_dav = self.has_channel(PIN_DAV)
715         has_atn = self.has_channel(PIN_ATN)
716         has_eoi = self.has_channel(PIN_EOI)
717         has_srq = self.has_channel(PIN_SRQ)
718         if has_clk:
719             self.decode_serial(has_clk, has_data_1, has_atn, has_srq)
720         else:
721             self.decode_parallel(has_data_n, has_dav, has_atn, has_eoi, has_srq)