Merge branch 'master' into i2

This commit is contained in:
Andrew Kay
2020-07-19 14:55:40 -05:00
9 changed files with 319 additions and 67 deletions

View File

@@ -187,6 +187,22 @@ implemented as a second buffer that shadows the regen buffer; this buffer only
contains extended attribute bytes that control the formatting of the characters
in the regen buffer - it does not include any characters.
An extended attribute byte is considered an Extended Field Attribute (EFA) if
the byte shadows an attribute byte in the regen buffer, or an Extended
Character Attribute (ECA) if it shadows a character byte.
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| | _M_ | _M_ | _C_ | _C_ | _C_ | _S_ | _S_ | _S_ |
Bits:
| Bits | Description |
| --------- | ----------- |
| 7-6 (`M`) | `00` - normal (or most recent EFA)<br>`01` - blink<br>`10` - reverse<br>`11` - underline |
| 5-3 (`C`) | `000` - base color (or most recent EFA)<br>`001` - blue<br>`010` - red<br>`011` - pink<br>`100` - green<br>`101` - turquoise<br>`110` - yellow<br>`111` - white |
| 2-0 (`S`) | `000` - base (or most recent EFA)<br>`001` - APL<br>`010` - PS 2<br>`011` - PS 3<br>`100` - PS 4<br>`101` - PS 5<br>`110` - PS 6<br>`111` - PS 7 |
### Keyboard
Keypresses are stored in a FIFO buffer. If there are any keypresses, the scan
@@ -217,33 +233,37 @@ Registers can be read-only, write-only, or read-write.
### Commands
| Feature | Command | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Value |
| ------- | ------- |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-----:|
| Base | `POLL` | _X_ | _X_ | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0x01` |
| Base | `POLL_ACK` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0` | `1` | `0x11` |
| Base | `READ_STATUS` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `1` | `0x0d` |
| Base | `READ_TERMINAL_ID` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `1` | `0x09` |
| Base | `READ_EXTENDED_ID` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `1` | `0x07` |
| Base | `READ_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0x05` |
| Base | `READ_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0` | `1` | `0x15` |
| Base | `READ_DATA` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0x03` |
| Base | `READ_MULTIPLE` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `1` | `0x0b` |
| Base | `RESET` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0x02` |
| Base | `LOAD_CONTROL_REGISTER` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `1` | `0x0a` |
| Base | `LOAD_SECONDARY_CONTROL` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `0` | `1` | `0x1a` |
| Base | `LOAD_MASK` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `0` | `1` | `0x16` |
| Base | `LOAD_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0x04` |
| Base | `LOAD_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `0` | `1` | `0x14` |
| Base | `WRITE_DATA` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `0` | `1` | `0x0c` |
| Base | `CLEAR` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `1` | `0x06` |
| Base | `SEARCH_FORWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `0` | `1` | `0x10` |
| Base | `SEARCH_BACKWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `0` | `1` | `0x12` |
| Base | `INSERT_BYTE` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `1` | `0x0e` |
| Base | `START_OPERATION` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `1` | `0x08` |
| Base | `DIAGNOSTIC_RESET` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `0` | `1` | `0x1c` |
_The hexadecimal value above represents the value of the 8-bit command byte; this is
bits 9-2 shifted two bits to the right._
| Feature | Command | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| ------- | ------- |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Base | `POLL` | _X_ | _X_ | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` |
| Base | `POLL_ACK` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0` | `1` |
| Base | `READ_STATUS` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `1` |
| Base | `READ_TERMINAL_ID` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `1` |
| Base | `READ_EXTENDED_ID` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `1` |
| Base | `READ_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` |
| Base | `READ_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0` | `1` |
| Base | `READ_DATA` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` |
| Base | `READ_MULTIPLE` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `1` |
| Base | `RESET` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` |
| Base | `LOAD_CONTROL_REGISTER` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `1` |
| Base | `LOAD_SECONDARY_CONTROL` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `0` | `1` |
| Base | `LOAD_MASK` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `0` | `1` |
| Base | `LOAD_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` |
| Base | `LOAD_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `0` | `1` |
| Base | `WRITE_DATA` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `0` | `1` |
| Base | `CLEAR` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `1` |
| Base | `SEARCH_FORWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `0` | `1` |
| Base | `SEARCH_BACKWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `0` | `1` |
| Base | `INSERT_BYTE` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `1` |
| Base | `START_OPERATION` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `1` |
| Base | `DIAGNOSTIC_RESET` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `0` | `1` |
| All | `READ_FEATURE_ID` | _F_ | _F_ | _F_ | _F_ | `0` | `1` | `1` | `1` | `0` | `1` |
| EAB | `READ_DATA` | _F_ | _F_ | _F_ | _F_ | `0` | `0` | `1` | `1` | `0` | `1` |
| EAB | `LOAD_MASK` | _F_ | _F_ | _F_ | _F_ | `0` | `1` | `0` | `1` | `0` | `1` |
| EAB | `WRITE_ALTERNATE` | _F_ | _F_ | _F_ | _F_ | `1` | `0` | `1` | `0` | `0` | `1` |
| EAB | `READ_MULTIPLE` | _F_ | _F_ | _F_ | _F_ | `1` | `0` | `1` | `1` | `0` | `1` |
| EAB | `WRITE_UNDER_MASK` | _F_ | _F_ | _F_ | _F_ | `1` | `1` | `0` | `0` | `0` | `1` |
| EAB | `READ_STATUS` | _F_ | _F_ | _F_ | _F_ | `1` | `1` | `0` | `1` | `0` | `1` |
## References
@@ -253,3 +273,4 @@ bits 9-2 shifted two bits to the right._
* NS DP8340
* NS DP8341
* NS DP8344
* IRMA Technical Reference

View File

@@ -1 +1 @@
__version__ = '0.4.2'
__version__ = '0.5.0'

View File

@@ -31,7 +31,19 @@ from .protocol import (
search_backward,
insert_byte,
start_operation,
diagnostic_reset
diagnostic_reset,
read_feature_id,
eab_read_data,
eab_load_mask,
eab_write_alternate,
eab_read_multiple,
eab_write_under_mask,
eab_read_status
)
from .features import (
Feature,
get_features
)
from .exceptions import (

29
pycoax/coax/features.py Normal file
View File

@@ -0,0 +1,29 @@
"""
coax.features
~~~~~~~~~~~~~
"""
from enum import Enum
from .protocol import read_feature_id
class Feature(Enum):
"""Terminal feature."""
EAB = 0x79
def get_features(interface, **kwargs):
"""Get the features a terminal supports."""
known_ids = set([feature.value for feature in Feature])
features = dict()
for address in range(2, 16):
id_ = read_feature_id(interface, address, **kwargs)
if id_ is not None and id_ in known_ids:
feature = Feature(id_)
features[feature] = address
return features

View File

@@ -11,7 +11,7 @@ from .parity import odd_parity
class Command(Enum):
"""Terminal command."""
# Read Commands
# Base
POLL = 0x01
POLL_ACK = 0x11
READ_STATUS = 0x0d
@@ -22,7 +22,6 @@ class Command(Enum):
READ_DATA = 0x03
READ_MULTIPLE = 0x0b
# Write Commands
RESET = 0x02
LOAD_CONTROL_REGISTER = 0x0a
LOAD_SECONDARY_CONTROL = 0x1a
@@ -37,6 +36,17 @@ class Command(Enum):
START_OPERATION = 0x08
DIAGNOSTIC_RESET = 0x1c
# Feature
READ_FEATURE_ID = 0x07
# EAB Feature
EAB_READ_DATA = 0x03
EAB_LOAD_MASK = 0x05
EAB_WRITE_ALTERNATE = 0x0a
EAB_READ_MULTIPLE = 0x0b
EAB_WRITE_UNDER_MASK = 0x0c
EAB_READ_STATUS = 0x0d
class PollAction(Enum):
"""Terminal POLL action."""
@@ -326,22 +336,61 @@ def diagnostic_reset(interface):
"""Execute a DIAGNOSTIC_RESET command."""
raise NotImplementedError
def pack_command_word(command):
def read_feature_id(interface, feature_address, **kwargs):
"""Execute a READ_FEATURE_ID command."""
command_word = pack_command_word(Command.READ_FEATURE_ID, feature_address)
response = _execute_read_command(interface, command_word, 1, allow_trta_response=True,
**kwargs)
if response is None:
return None
return response[0]
def eab_read_data(interface, feature_address, **kwargs):
"""Execute a EAB_READ_DATA command."""
command_word = pack_command_word(Command.EAB_READ_DATA, feature_address)
return _execute_read_command(interface, command_word, **kwargs)
def eab_load_mask(interface, feature_address, mask, **kwargs):
"""Execute a EAB_LOAD_MASK command."""
command_word = pack_command_word(Command.EAB_LOAD_MASK, feature_address)
_execute_write_command(interface, command_word, bytes([mask]), **kwargs)
def eab_write_alternate(interface, feature_address, data, **kwargs):
"""Execute a EAB_WRITE_ALTERNATE command."""
command_word = pack_command_word(Command.EAB_WRITE_ALTERNATE, feature_address)
_execute_write_command(interface, command_word, data, **kwargs)
def eab_read_multiple(interface, feature_address, **kwargs):
"""Execute a EAB_READ_MULTIPLE command."""
command_word = pack_command_word(Command.EAB_READ_MULTIPLE, feature_address)
return _execute_read_command(interface, command_word, 32,
validate_response_length=False, **kwargs)
def eab_write_under_mask(interface, feature_address, byte, **kwargs):
"""Execute a EAB_WRITE_UNDER_MASK command."""
command_word = pack_command_word(Command.EAB_WRITE_UNDER_MASK, feature_address)
_execute_write_command(interface, command_word, bytes([byte]), **kwargs)
def eab_read_status(interface, feature_address, **kwargs):
"""Execute a EAB_READ_STATUS command."""
command_word = pack_command_word(Command.EAB_READ_STATUS, feature_address)
return _execute_read_command(interface, command_word, **kwargs)[0]
def pack_command_word(command, feature_address=None):
"""Pack a command into a 10-bit command word."""
return (command.value << 2) | 0x1
if feature_address is not None and (feature_address < 2 or feature_address > 15):
raise ValueError(f'Invalid feature address: {feature_address}')
def is_command_word(word):
"""Is command word bit set?"""
return (word & 0x1) == 1
def unpack_command_word(word):
"""Unpack a 10-bit command word."""
if not is_command_word(word):
raise ProtocolError(f'Word does not have command bit set: {word}')
command = (word >> 2) & 0x1f
return Command(command)
return (feature_address << 6 if feature_address is not None else 0) | (command.value << 2) | 0x1
def pack_data_word(byte, set_parity=True):
"""Pack a data byte into a 10-bit data word."""
@@ -385,10 +434,7 @@ def _execute_read_command(interface, command_word, response_length=1,
return trta_value
if validate_response_length and len(response) != response_length:
command = unpack_command_word(command_word)
raise ProtocolError((f'Expected {response_length} word {command.name} '
f'response: {response}'))
raise ProtocolError((f'Expected {response_length} word response: {response}'))
return unpack_data_words(response) if unpack else response
@@ -408,9 +454,7 @@ def _execute_write_command(interface, command_word, data=None, **kwargs):
receive_length=1, **kwargs)
if len(response) != 1:
command = unpack_command_word(command_word)
raise ProtocolError(f'Expected 1 word {command.name} response: {response}')
raise ProtocolError(f'Expected 1 word response: {response}')
if response[0] != 0:
raise ProtocolError(f'Expected TR/TA response: {response}')

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python
import sys
from common import create_serial, create_interface
from coax import get_features
with create_serial() as serial:
interface = create_interface(serial)
features = get_features(interface)
print(features)

75
pycoax/examples/40_eab.py Executable file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
import sys
from itertools import chain
from common import create_serial, create_interface
from coax import Feature, get_features, load_address_counter_hi, load_address_counter_lo, write_data, eab_write_alternate, eab_load_mask
def eab_alternate_zip(regen_buffer, eab_buffer):
return bytes(chain(*zip(regen_buffer, eab_buffer)))
with create_serial() as serial:
interface = create_interface(serial)
features = get_features(interface)
if Feature.EAB not in features:
sys.exit('No EAB feature found.')
eab_address = features[Feature.EAB]
print(f'EAB feature found at address {eab_address}')
# Protected Normal
load_address_counter_hi(interface, 0)
load_address_counter_lo(interface, 80)
regen_buffer = bytes.fromhex('e0 08 00 af 91 8e 93 84 82 93 84 83 00 ad 8e 91 8c 80 8b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09')
write_data(interface, regen_buffer)
# Protected Intense
load_address_counter_hi(interface, 0)
load_address_counter_lo(interface, 160)
regen_buffer = bytes.fromhex('e8 08 00 af 91 8e 93 84 82 93 84 83 00 a8 8d 93 84 8d 92 84 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09')
write_data(interface, regen_buffer)
# Normal EFA
load_address_counter_hi(interface, 1)
load_address_counter_lo(interface, 64)
regen_buffer = bytes.fromhex('e0 08 00 ad 8e 91 8c 80 8b 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0')
eab_buffer = bytes.fromhex('00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00')
eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer))
# Blink EFA
load_address_counter_hi(interface, 1)
load_address_counter_lo(interface, 144)
regen_buffer = bytes.fromhex('e0 08 00 a1 8b 88 8d 8a 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0')
eab_buffer = bytes.fromhex('40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00')
eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer))
# Reverse EFA
load_address_counter_hi(interface, 1)
load_address_counter_lo(interface, 224)
regen_buffer = bytes.fromhex('e0 08 00 b1 84 95 84 91 92 84 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0')
eab_buffer = bytes.fromhex('80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00')
eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer))
# Underline EFA
load_address_counter_hi(interface, 2)
load_address_counter_lo(interface, 48)
regen_buffer = bytes.fromhex('e0 08 00 b4 8d 83 84 91 8b 88 8d 84 00 a4 a5 a0 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0')
eab_buffer = bytes.fromhex('c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00')
eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer))

View File

@@ -0,0 +1,69 @@
import unittest
from unittest.mock import Mock, call, patch
import context
from coax.features import Feature, get_features
class GetFeaturesTestCase(unittest.TestCase):
def setUp(self):
self.interface = Mock()
patcher = patch('coax.features.read_feature_id')
self.read_feature_id_mock = patcher.start()
self.addCleanup(patch.stopall)
def test_with_known_feature(self):
# Arrange
def read_feature_id(interface, feature_address, **kwargs):
if feature_address == 7:
return 0x79
return None
self.read_feature_id_mock.side_effect = read_feature_id
# Act
features = get_features(self.interface)
# Assert
self.assertEqual(features, { Feature.EAB: 7 })
def test_with_unknown_feature(self):
# Arrange
def read_feature_id(interface, feature_address, **kwargs):
if feature_address == 7:
return 0x99
return None
self.read_feature_id_mock.side_effect = read_feature_id
# Act
features = get_features(self.interface)
# Assert
self.assertEqual(features, { })
def test_all_feature_addresses_are_enumerated(self):
# Act
features = get_features(self.interface)
# Assert
calls = self.read_feature_id_mock.call_args_list
self.assertEqual(calls, [call(self.interface, address) for address in range(2, 16)])
def test_receive_timeout_is_passed_to_read_feature_id(self):
# Act
features = get_features(self.interface, receive_timeout=10)
# Assert
calls = self.read_feature_id_mock.call_args_list
self.assertEqual(calls, [call(self.interface, address, receive_timeout=10) for address in range(2, 16)])
if __name__ == '__main__':
unittest.main()

View File

@@ -4,7 +4,7 @@ from unittest.mock import Mock
import context
from coax import PollResponse, KeystrokePollResponse, ProtocolError
from coax.protocol import Command, Status, TerminalType, TerminalId, Control, SecondaryControl, pack_command_word, unpack_command_word, pack_data_word, unpack_data_word, pack_data_words, unpack_data_words, _execute_read_command, _execute_write_command
from coax.protocol import Command, Status, TerminalType, TerminalId, Control, SecondaryControl, pack_command_word, pack_data_word, unpack_data_word, pack_data_words, unpack_data_words, _execute_read_command, _execute_write_command
class PollResponseTestCase(unittest.TestCase):
def test_is_power_on_reset_complete(self):
@@ -108,18 +108,6 @@ class PackCommandWordTestCase(unittest.TestCase):
def test(self):
self.assertEqual(pack_command_word(Command.POLL_ACK), 0b0001000101)
class UnpackCommandWordTestCase(unittest.TestCase):
def test(self):
# Act
command = unpack_command_word(0b0001000101)
# Assert
self.assertEqual(command, Command.POLL_ACK)
def test_command_bit_not_set_error(self):
with self.assertRaisesRegex(ProtocolError, 'Word does not have command bit set'):
unpack_command_word(0b0001000100)
class PackDataWordTestCase(unittest.TestCase):
def test(self):
self.assertEqual(pack_data_word(0x00), 0b0000000010)
@@ -190,7 +178,7 @@ class ExecuteReadCommandTestCase(unittest.TestCase):
self.interface.transmit_receive = Mock(return_value=[])
# Act and assert
with self.assertRaisesRegex(ProtocolError, 'Expected 1 word READ_TERMINAL_ID response'):
with self.assertRaisesRegex(ProtocolError, 'Expected 1 word response'):
_execute_read_command(self.interface, command_word)
def test_receive_timeout_is_passed_to_interface(self):
@@ -225,7 +213,7 @@ class ExecuteWriteCommandTestCase(unittest.TestCase):
self.interface.transmit_receive = Mock(return_value=[])
# Act and assert
with self.assertRaisesRegex(ProtocolError, 'Expected 1 word WRITE_DATA response'):
with self.assertRaisesRegex(ProtocolError, 'Expected 1 word response'):
_execute_write_command(self.interface, command_word, bytes.fromhex('de ad be ef'))
def test_not_trta_response(self):