]> sigrok.org Git - libsigrokdecode.git/blobdiff - decoders/ieee488/pd.py
ieee488: support optional parity for ATN commands (for HP gear)
[libsigrokdecode.git] / decoders / ieee488 / pd.py
index 5c25c668b50c38e18d1501e3e9dcb1b00c1b3e04..0451c14f4f0edb494e66a258278dd060cbd76b42 100644 (file)
@@ -145,10 +145,7 @@ def _is_msb_set(b):
 
 def _get_raw_byte(b, atn):
     # "Decorate" raw byte values for stacked decoders.
-    raw_byte = b
-    if atn:
-        raw_byte |= 0x100
-    return raw_byte
+    return b | 0x100 if atn else b
 
 def _get_raw_text(b, atn):
     return ['{leader}{data:02x}'.format(leader = '/' if atn else '', data = b)]
@@ -258,7 +255,7 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'ieee488'
     name = 'IEEE-488'
-    longname = 'General Purpose Interface Bus'
+    longname = 'IEEE-488 GPIB/HPIB/IEC'
     desc = 'IEEE-488 General Purpose Interface Bus (GPIB/HPIB or IEC).'
     license = 'gplv2+'
     inputs = ['logic']
@@ -289,6 +286,10 @@ class Decoder(srd.Decoder):
     options = (
         {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
             'default': 'no', 'values': ('no', 'yes')},
+        {'id': 'delim', 'desc': 'Payload data delimiter',
+            'default': 'eol', 'values': ('none', 'eol')},
+        {'id': 'atn_parity', 'desc': 'ATN commands use parity',
+            'default': 'no', 'values': ('no', 'yes')},
     )
     annotations = (
         ('bit', 'IEC bit'),
@@ -301,7 +302,7 @@ class Decoder(srd.Decoder):
         ('eoi', 'EOI'),
         ('text', 'Talker text'),
         ('periph', 'IEC bus peripherals'),
-        ('warn', 'Warning'),
+        ('warning', 'Warning'),
     )
     annotation_rows = (
         ('bits', 'IEC bits', (ANN_RAW_BIT,)),
@@ -310,7 +311,7 @@ class Decoder(srd.Decoder):
         ('eois', 'EOI', (ANN_EOI,)),
         ('texts', 'Talker texts', (ANN_TEXT,)),
         ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
-        ('warns', 'Warnings', (ANN_WARN,)),
+        ('warnings', 'Warnings', (ANN_WARN,)),
     )
     binary = (
         ('raw', 'Raw bytes'),
@@ -377,12 +378,38 @@ class Decoder(srd.Decoder):
             self.accu_text = []
         self.ss_text = self.es_text = None
 
+    def check_extra_flush(self, b):
+        # Optionally flush previously accumulated runs of payload data
+        # according to user specified conditions.
+        if self.options['delim'] == 'none':
+            return
+        if not self.accu_bytes:
+            return
+
+        # This implementation exlusively handles "text lines", but adding
+        # support for more variants here is straight forward.
+        #
+        # Search for the first data byte _after_ a user specified text
+        # line termination sequence was seen. The termination sequence's
+        # alphabet may be variable, and the sequence may span multiple
+        # data bytes. We accept either CR or LF, and combine the CR+LF
+        # sequence to strive for maximum length annotations for improved
+        # readability at different zoom levels. It's acceptable that this
+        # implementation would also combine multiple line terminations
+        # like LF+LF.
+        term_chars = (10, 13)
+        is_eol = b in term_chars
+        had_eol = self.accu_bytes[-1] in term_chars
+        if had_eol and not is_eol:
+            self.flush_bytes_text_accu()
+
     def handle_ifc_change(self, ifc):
         # Track IFC line for parallel input.
         # Assertion of IFC de-selects all talkers and listeners.
         if ifc:
             self.last_talker = None
             self.last_listener = []
+            self.flush_bytes_text_accu()
 
     def handle_eoi_change(self, eoi):
         # Track EOI line for parallel and serial input.
@@ -449,6 +476,8 @@ class Decoder(srd.Decoder):
             # TODO Process data depending on peripheral type and channel?
 
     def handle_data_byte(self):
+        if not self.curr_atn:
+            self.check_extra_flush(self.curr_raw)
         b = self.curr_raw
         texts = _get_raw_text(b, self.curr_atn)
         self.emit_data_ann(self.ss_raw, self.es_raw, ANN_RAW_BYTE, texts)
@@ -459,6 +488,13 @@ class Decoder(srd.Decoder):
             upd_iec = False,
             py_type = None
             py_peers = False
+            if self.options['atn_parity'] == 'yes':
+                par = 1 if b & 0x80 else 0
+                b &= ~0x80
+                ones = bin(b).count('1') + par
+                if ones % 2:
+                    warn_texts = ['Command parity error', 'parity', 'PAR']
+                    self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
             is_cmd, is_unl, is_unt = _is_command(b)
             laddr = _is_listen_addr(b)
             taddr = _is_talk_addr(b)