pysvp64dis: disassemble word instruction operands
[openpower-isa.git] / src / openpower / decoder / power_insn.py
1 import collections as _collections
2 import csv as _csv
3 import dataclasses as _dataclasses
4 import enum as _enum
5 import functools as _functools
6 import os as _os
7 import operator as _operator
8 import pathlib as _pathlib
9 import re as _re
10
11 try:
12 from functools import cached_property
13 except ImportError:
14 from cached_property import cached_property
15
16 from openpower.decoder.power_enums import (
17 Function as _Function,
18 MicrOp as _MicrOp,
19 In1Sel as _In1Sel,
20 In2Sel as _In2Sel,
21 In3Sel as _In3Sel,
22 OutSel as _OutSel,
23 CRInSel as _CRInSel,
24 CROutSel as _CROutSel,
25 LDSTLen as _LDSTLen,
26 LDSTMode as _LDSTMode,
27 RCOE as _RCOE,
28 CryIn as _CryIn,
29 Form as _Form,
30 SVEtype as _SVEtype,
31 SVMode as _SVMode,
32 SVPtype as _SVPtype,
33 SVExtra as _SVExtra,
34 SVExtraRegType as _SVExtraRegType,
35 SVExtraReg as _SVExtraReg,
36 )
37 from openpower.decoder.selectable_int import (
38 SelectableInt as _SelectableInt,
39 selectconcat as _selectconcat,
40 )
41 from openpower.decoder.power_fields import (
42 Field as _Field,
43 Mapping as _Mapping,
44 DecodeFields as _DecodeFields,
45 )
46 from openpower.decoder.pseudo.pagereader import ISA as _ISA
47
48
49 def dataclass(cls, record, keymap=None, typemap=None):
50 if keymap is None:
51 keymap = {}
52 if typemap is None:
53 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
54
55 def transform(key_value):
56 (key, value) = key_value
57 key = keymap.get(key, key)
58 hook = typemap.get(key, lambda value: value)
59 if hook is bool and value in ("", "0"):
60 value = False
61 else:
62 value = hook(value)
63 return (key, value)
64
65 record = dict(map(transform, record.items()))
66 for key in frozenset(record.keys()):
67 if record[key] == "":
68 record.pop(key)
69
70 return cls(**record)
71
72
73 @_functools.total_ordering
74 @_dataclasses.dataclass(eq=True, frozen=True)
75 class Opcode:
76 class Value(int):
77 def __repr__(self):
78 if self.bit_length() <= 32:
79 return f"0x{self:08x}"
80 else:
81 return f"0x{self:016x}"
82
83 class Mask(int):
84 def __repr__(self):
85 if self.bit_length() <= 32:
86 return f"0x{self:08x}"
87 else:
88 return f"0x{self:016x}"
89
90 value: Value
91 mask: Mask = None
92
93 def __lt__(self, other):
94 if not isinstance(other, Opcode):
95 return NotImplemented
96 return ((self.value, self.mask) < (other.value, other.mask))
97
98 def __post_init__(self):
99 (value, mask) = (self.value, self.mask)
100
101 if isinstance(value, Opcode):
102 if mask is not None:
103 raise ValueError(mask)
104 (value, mask) = (value.value, value.mask)
105 elif isinstance(value, str):
106 if mask is not None:
107 raise ValueError(mask)
108 value = int(value, 0)
109
110 if not isinstance(value, int):
111 raise ValueError(value)
112 if mask is None:
113 mask = value
114 if not isinstance(mask, int):
115 raise ValueError(mask)
116
117 object.__setattr__(self, "value", self.__class__.Value(value))
118 object.__setattr__(self, "mask", self.__class__.Mask(mask))
119
120
121 class IntegerOpcode(Opcode):
122 def __init__(self, value):
123 if isinstance(value, str):
124 value = int(value, 0)
125 return super().__init__(value=value, mask=None)
126
127
128 class PatternOpcode(Opcode):
129 def __init__(self, value):
130 (pattern, value, mask) = (value, 0, 0)
131
132 for symbol in pattern:
133 if symbol not in {"0", "1", "-"}:
134 raise ValueError(pattern)
135 value |= (symbol == "1")
136 mask |= (symbol != "-")
137 value <<= 1
138 mask <<= 1
139 value >>= 1
140 mask >>= 1
141
142 return super().__init__(value=value, mask=mask)
143
144
145 class FieldsOpcode(Opcode):
146 def __init__(self, fields):
147 def field(opcode, field):
148 (value, mask) = opcode
149 (field, bits) = field
150 shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
151 for (index, shift) in enumerate(shifts):
152 bit = ((field & (1 << index)) != 0)
153 value |= (bit << shift)
154 mask |= (1 << shift)
155 return (value, mask)
156
157 (value, mask) = _functools.reduce(field, fields, (0, 0))
158
159 return super().__init__(value=value, mask=mask)
160
161
162 @_dataclasses.dataclass(eq=True, frozen=True)
163 class PPCRecord:
164 class FlagsMeta(type):
165 def __iter__(cls):
166 yield from (
167 "inv A",
168 "inv out",
169 "cry out",
170 "BR",
171 "sgn ext",
172 "rsrv",
173 "32b",
174 "sgn",
175 "lk",
176 "sgl pipe",
177 )
178
179 class Flags(frozenset, metaclass=FlagsMeta):
180 def __new__(cls, flags=frozenset()):
181 flags = frozenset(flags)
182 diff = (flags - frozenset(cls))
183 if diff:
184 raise ValueError(flags)
185 return super().__new__(cls, flags)
186
187 opcode: Opcode
188 comment: str
189 flags: Flags = Flags()
190 comment2: str = ""
191 function: _Function = _Function.NONE
192 intop: _MicrOp = _MicrOp.OP_ILLEGAL
193 in1: _In1Sel = _In1Sel.RA
194 in2: _In2Sel = _In2Sel.NONE
195 in3: _In3Sel = _In3Sel.NONE
196 out: _OutSel = _OutSel.NONE
197 cr_in: _CRInSel = _CRInSel.NONE
198 cr_out: _CROutSel = _CROutSel.NONE
199 cry_in: _CryIn = _CryIn.ZERO
200 ldst_len: _LDSTLen = _LDSTLen.NONE
201 upd: _LDSTMode = _LDSTMode.NONE
202 rc: _RCOE = _RCOE.NONE
203 form: _Form = _Form.NONE
204 conditions: str = ""
205 unofficial: bool = False
206
207 __KEYMAP = {
208 "unit": "function",
209 "internal op": "intop",
210 "CR in": "cr_in",
211 "CR out": "cr_out",
212 "cry in": "cry_in",
213 "ldst len": "ldst_len",
214 "CONDITIONS": "conditions",
215 }
216
217 @classmethod
218 def CSV(cls, record, opcode_cls=Opcode):
219 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
220 typemap["opcode"] = opcode_cls
221
222 flags = set()
223 for flag in frozenset(PPCRecord.Flags):
224 if bool(record.pop(flag, "")):
225 flags.add(flag)
226 record["flags"] = PPCRecord.Flags(flags)
227
228 return dataclass(cls, record, keymap=PPCRecord.__KEYMAP, typemap=typemap)
229
230 @cached_property
231 def names(self):
232 return frozenset(self.comment.split("=")[-1].split("/"))
233
234
235 @_dataclasses.dataclass(eq=True, frozen=True)
236 class SVP64Record:
237 class ExtraMap(tuple):
238 class Extra(tuple):
239 @_dataclasses.dataclass(eq=True, frozen=True)
240 class Entry:
241 regtype: _SVExtraRegType = _SVExtraRegType.NONE
242 reg: _SVExtraReg = _SVExtraReg.NONE
243
244 def __repr__(self):
245 return f"{self.regtype.value}:{self.reg.name}"
246
247 def __new__(cls, value="0"):
248 if isinstance(value, str):
249 def transform(value):
250 (regtype, reg) = value.split(":")
251 regtype = _SVExtraRegType(regtype)
252 reg = _SVExtraReg(reg)
253 return cls.Entry(regtype=regtype, reg=reg)
254
255 if value == "0":
256 value = tuple()
257 else:
258 value = map(transform, value.split(";"))
259
260 return super().__new__(cls, value)
261
262 def __repr__(self):
263 return repr(list(self))
264
265 def __new__(cls, value=tuple()):
266 value = tuple(value)
267 if len(value) == 0:
268 value = (("0",) * 4)
269 return super().__new__(cls, map(cls.Extra, value))
270
271 def __repr__(self):
272 return repr({index:self[index] for index in range(0, 4)})
273
274 name: str
275 ptype: _SVPtype = _SVPtype.NONE
276 etype: _SVEtype = _SVEtype.NONE
277 in1: _In1Sel = _In1Sel.NONE
278 in2: _In2Sel = _In2Sel.NONE
279 in3: _In3Sel = _In3Sel.NONE
280 out: _OutSel = _OutSel.NONE
281 out2: _OutSel = _OutSel.NONE
282 cr_in: _CRInSel = _CRInSel.NONE
283 cr_out: _CROutSel = _CROutSel.NONE
284 extra: ExtraMap = ExtraMap()
285 pu: bool = False
286 conditions: str = ""
287 mode: _SVMode = _SVMode.NORMAL
288
289 __KEYMAP = {
290 "insn": "name",
291 "CONDITIONS": "conditions",
292 "Ptype": "ptype",
293 "Etype": "etype",
294 "CR in": "cr_in",
295 "CR out": "cr_out",
296 "PU": "pu",
297 }
298
299 @classmethod
300 def CSV(cls, record):
301 for key in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"):
302 value = record[key]
303 if value == "0":
304 record[key] = "NONE"
305
306 record["extra"] = cls.ExtraMap(record.pop(f"{index}") for index in range(0, 4))
307
308 return dataclass(cls, record, keymap=cls.__KEYMAP)
309
310
311 class BitSel:
312 def __init__(self, value=(0, 32)):
313 if isinstance(value, str):
314 (start, end) = map(int, value.split(":"))
315 else:
316 (start, end) = value
317 if start < 0 or end < 0 or start >= end:
318 raise ValueError(value)
319
320 self.__start = start
321 self.__end = end
322
323 return super().__init__()
324
325 def __repr__(self):
326 return f"[{self.__start}:{self.__end}]"
327
328 def __iter__(self):
329 yield from range(self.start, (self.end + 1))
330
331 @property
332 def start(self):
333 return self.__start
334
335 @property
336 def end(self):
337 return self.__end
338
339
340 @_dataclasses.dataclass(eq=True, frozen=True)
341 class Section:
342 class Mode(_enum.Enum):
343 INTEGER = _enum.auto()
344 PATTERN = _enum.auto()
345
346 @classmethod
347 def _missing_(cls, value):
348 if isinstance(value, str):
349 return cls[value.upper()]
350 return super()._missing_(value)
351
352 class Suffix(int):
353 def __new__(cls, value=None):
354 if isinstance(value, str):
355 if value.upper() == "NONE":
356 value = None
357 else:
358 value = int(value, 0)
359 if value is None:
360 value = 0
361
362 return super().__new__(cls, value)
363
364 def __str__(self):
365 return repr(self)
366
367 def __repr__(self):
368 return (bin(self) if self else "None")
369
370 path: _pathlib.Path
371 opcode: Opcode
372 bitsel: BitSel
373 suffix: Suffix
374 mode: Mode
375
376 @classmethod
377 def CSV(cls, record):
378 return dataclass(cls, record)
379
380
381 class Fields:
382 def __init__(self, items):
383 if isinstance(items, dict):
384 items = items.items()
385
386 def transform(item):
387 (name, bitrange) = item
388 return (name, tuple(bitrange.values()))
389
390 self.__mapping = dict(map(transform, items))
391
392 return super().__init__()
393
394 def __repr__(self):
395 return repr(self.__mapping)
396
397 def __iter__(self):
398 yield from self.__mapping.items()
399
400 def __contains__(self, key):
401 return self.__mapping.__contains__(key)
402
403 def __getitem__(self, key):
404 return self.__mapping.get(key, None)
405
406
407 @_dataclasses.dataclass(eq=True, frozen=True)
408 class Operand:
409 pass
410
411
412 @_dataclasses.dataclass(eq=True, frozen=True)
413 class DynamicOperand(Operand):
414 name: str
415
416
417 @_dataclasses.dataclass(eq=True, frozen=True)
418 class StaticOperand(Operand):
419 name: str
420 value: int = None
421
422
423 class Operands(tuple):
424 def __new__(cls, iterable):
425 operands = []
426 for operand in iterable:
427 if "=" in operand:
428 (name, value) = operand.split("=")
429 value = int(value)
430 operand = StaticOperand(name=name, value=value)
431 else:
432 operand = DynamicOperand(name=operand)
433 operands.append(operand)
434 return super().__new__(cls, operands)
435
436
437 @_functools.total_ordering
438 @_dataclasses.dataclass(eq=True, frozen=True)
439 class Record:
440 name: str
441 section: Section
442 ppc: PPCRecord
443 fields: Fields
444 operands: Operands
445 svp64: SVP64Record = None
446
447 __EXTRA = (
448 _SVExtra.Idx0,
449 _SVExtra.Idx1,
450 _SVExtra.Idx2,
451 _SVExtra.Idx3,
452 )
453
454 def __lt__(self, other):
455 if not isinstance(other, Record):
456 return NotImplemented
457 return (self.opcode < other.opcode)
458
459 def __repr__(self):
460 return f"{self.__class__.__name__}(name={self.name!r}, opcode={self.opcode})"
461
462 @cached_property
463 def opcode(self):
464 fields = []
465 if self.section.opcode:
466 fields += [(self.section.opcode.value, BitSel((0, 5)))]
467 fields += [(self.ppc.opcode.value, self.section.bitsel)]
468 else:
469 fields += [(self.ppc.opcode.value, self.section.bitsel)]
470
471 for operand in self.static_operands:
472 fields += [(operand.value, self.fields[operand.name])]
473
474 return FieldsOpcode(fields)
475
476 @property
477 def function(self):
478 return self.ppc.function
479
480 @property
481 def in1(self):
482 return self.ppc.in1
483
484 @property
485 def in2(self):
486 return self.ppc.in2
487
488 @property
489 def in3(self):
490 return self.ppc.in3
491
492 @property
493 def out(self):
494 return self.ppc.out
495
496 @property
497 def out2(self):
498 if self.svp64 is None:
499 return _OutSel.NONE
500 return self.ppc.out
501
502 @property
503 def cr_in(self):
504 return self.ppc.cr_in
505
506 @property
507 def cr_out(self):
508 return self.ppc.cr_out
509
510 def sv_extra(self, key):
511 if key not in frozenset({
512 "in1", "in2", "in3", "cr_in",
513 "out", "out2", "cr_out",
514 }):
515 raise KeyError(key)
516
517 sel = getattr(self.svp64, key)
518 if sel is _CRInSel.BA_BB:
519 return _SVExtra.Idx_1_2
520 reg = _SVExtraReg(sel)
521 if reg is _SVExtraReg.NONE:
522 return _SVExtra.NONE
523
524 extra_map = {
525 _SVExtraRegType.SRC: {},
526 _SVExtraRegType.DST: {},
527 }
528 for index in range(0, 4):
529 for entry in self.svp64.extra[index]:
530 extra_map[entry.regtype][entry.reg] = Record.__EXTRA[index]
531
532 for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
533 extra = extra_map[regtype].get(reg, _SVExtra.NONE)
534 if extra is not _SVExtra.NONE:
535 return extra
536
537 return _SVExtra.NONE
538
539 sv_in1 = property(_functools.partial(sv_extra, key="in1"))
540 sv_in2 = property(_functools.partial(sv_extra, key="in2"))
541 sv_in3 = property(_functools.partial(sv_extra, key="in3"))
542 sv_out = property(_functools.partial(sv_extra, key="out"))
543 sv_out2 = property(_functools.partial(sv_extra, key="out2"))
544 sv_cr_in = property(_functools.partial(sv_extra, key="cr_in"))
545 sv_cr_out = property(_functools.partial(sv_extra, key="cr_out"))
546
547 @property
548 def sv_ptype(self):
549 if self.svp64 is None:
550 return _SVPtype.NONE
551 return self.svp64.ptype
552
553 @property
554 def sv_etype(self):
555 if self.svp64 is None:
556 return _SVEtype.NONE
557 return self.svp64.etype
558
559 @property
560 def dynamic_operands(self):
561 for operand in self.operands:
562 if isinstance(operand, DynamicOperand):
563 yield operand
564
565 @property
566 def static_operands(self):
567 for operand in self.operands:
568 if isinstance(operand, StaticOperand):
569 yield operand
570
571
572 class Instruction(_Mapping):
573 @classmethod
574 def integer(cls, value=0, bits=None, byteorder="little"):
575 if isinstance(value, (int, bytes)) and not isinstance(bits, int):
576 raise ValueError(bits)
577
578 if isinstance(value, bytes):
579 if ((len(value) * 8) != bits):
580 raise ValueError(f"bit length mismatch")
581 value = int.from_bytes(value, byteorder=byteorder)
582
583 if isinstance(value, int):
584 value = _SelectableInt(value=value, bits=bits)
585 elif isinstance(value, Instruction):
586 value = value.storage
587
588 if not isinstance(value, _SelectableInt):
589 raise ValueError(value)
590 if bits is None:
591 bits = len(cls)
592 if len(value) != bits:
593 raise ValueError(value)
594
595 value = _SelectableInt(value=value, bits=bits)
596
597 return cls(storage=value)
598
599 def __hash__(self):
600 return hash(int(self))
601
602 def disassemble(self, db):
603 raise NotImplementedError
604
605
606 class WordInstruction(Instruction):
607 _: _Field = range(0, 32)
608 po: _Field = range(0, 6)
609
610 @classmethod
611 def integer(cls, value, byteorder="little"):
612 return super().integer(bits=32, value=value, byteorder=byteorder)
613
614 def disassemble(self, db):
615 record = db[self]
616 if record is None:
617 yield f".long 0x{int(self):08x}"
618 else:
619 operands = []
620 for operand in record.dynamic_operands:
621 operand = int(self[record.fields[operand.name]])
622 operands.append(operand)
623 if operands:
624 operands = ",".join(map(str, operands))
625 operands = f" {operands}"
626 else:
627 operands = ""
628
629 yield f"{record.name}{operands}"
630
631 class PrefixedInstruction(Instruction):
632 class Prefix(WordInstruction.remap(range(0, 32))):
633 pass
634
635 class Suffix(WordInstruction.remap(range(32, 64))):
636 pass
637
638 _: _Field = range(64)
639 prefix: Prefix
640 suffix: Suffix
641 po: Suffix.po
642
643 @classmethod
644 def integer(cls, value, byteorder="little"):
645 return super().integer(bits=64, value=value, byteorder=byteorder)
646
647 @classmethod
648 def pair(cls, prefix=0, suffix=0, byteorder="little"):
649 def transform(value):
650 return WordInstruction.integer(value=value,
651 byteorder=byteorder)[0:32]
652
653 (prefix, suffix) = map(transform, (prefix, suffix))
654 value = _selectconcat(prefix, suffix)
655
656 return super().integer(value=value)
657
658 def disassemble(self, db):
659 record = db[self.suffix]
660 if record is None:
661 yield f".llong 0x{int(self):016x}"
662 else:
663 yield f".llong 0x{int(self):016x} # {record.name}"
664
665
666 class SVP64Instruction(PrefixedInstruction):
667 """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
668 class Prefix(PrefixedInstruction.Prefix):
669 class RM(_Mapping):
670 _: _Field = range(24)
671 mmode: _Field = (0,)
672 mask: _Field = range(1, 4)
673 elwidth: _Field = range(4, 6)
674 ewsrc: _Field = range(6, 8)
675 subvl: _Field = range(8, 10)
676 extra: _Field = range(10, 19)
677 mode: _Field = range(19, 24)
678 extra2: _Field[4] = (
679 range(10, 12),
680 range(12, 14),
681 range(14, 16),
682 range(16, 18),
683 )
684 smask: _Field = range(16, 19)
685 extra3: _Field[3] = (
686 range(10, 13),
687 range(13, 16),
688 range(16, 19),
689 )
690
691 id: _Field = (7, 9)
692 rm: RM = ((6, 8) + tuple(range(10, 32)))
693
694 prefix: Prefix
695
696 def disassemble(self, db):
697 record = db[self.suffix]
698 if record is None:
699 yield f".llong 0x{int(self):016x}"
700 else:
701 yield f".llong 0x{int(self):016x} # sv.{record.name}"
702
703
704 def parse(stream, factory):
705 lines = filter(lambda line: not line.strip().startswith("#"), stream)
706 entries = _csv.DictReader(lines)
707 entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries)
708 return tuple(map(factory, entries))
709
710
711 class PPCDatabase:
712 def __init__(self, root):
713 db = _collections.defaultdict(set)
714 path = (root / "insndb.csv")
715 with open(path, "r", encoding="UTF-8") as stream:
716 for section in parse(stream, Section.CSV):
717 path = (root / section.path)
718 opcode_cls = {
719 section.Mode.INTEGER: IntegerOpcode,
720 section.Mode.PATTERN: PatternOpcode,
721 }[section.mode]
722 factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
723 with open(path, "r", encoding="UTF-8") as stream:
724 db[section].update(parse(stream, factory))
725 self.__db = db
726 return super().__init__()
727
728 def __getitem__(self, key):
729 for (section, records) in self.__db.items():
730 for record in records:
731 for name in record.names:
732 if ((key == name) or
733 ((record.rc is _RC.RC) and
734 key.endswith(".") and
735 name == key[:-1])):
736 return (section, record)
737 return (None, None)
738
739
740 class SVP64Database:
741 def __init__(self, root):
742 db = set()
743 pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
744 for (prefix, _, names) in _os.walk(root):
745 prefix = _pathlib.Path(prefix)
746 for name in filter(lambda name: pattern.match(name), names):
747 path = (prefix / _pathlib.Path(name))
748 with open(path, "r", encoding="UTF-8") as stream:
749 db.update(parse(stream, SVP64Record.CSV))
750 self.__db = {record.name:record for record in db}
751 return super().__init__()
752
753 def __getitem__(self, key):
754 for name in key:
755 record = self.__db.get(name, None)
756 if record is not None:
757 return record
758 return None
759
760
761 class FieldsDatabase:
762 def __init__(self):
763 db = {}
764 df = _DecodeFields()
765 df.create_specs()
766 for (form, fields) in df.instrs.items():
767 if form in {"DQE", "TX"}:
768 continue
769 if form == "all":
770 form = "NONE"
771 db[_Form[form]] = Fields(fields)
772 self.__db = db
773 return super().__init__()
774
775 def __getitem__(self, key):
776 return self.__db.__getitem__(key)
777
778
779 class MarkdownDatabase:
780 def __init__(self):
781 db = {}
782 for (name, desc) in _ISA():
783 operands = []
784 if desc.regs:
785 (dynamic, *static) = desc.regs
786 operands.extend(dynamic)
787 operands.extend(static)
788 db[name] = Operands(operands)
789 self.__db = db
790 return super().__init__()
791
792 def __iter__(self):
793 yield from self.__db.items()
794
795 def __getitem__(self, key):
796 return self.__db.__getitem__(key)
797
798
799 class Database:
800 def __init__(self, root):
801 root = _pathlib.Path(root)
802
803 mdwndb = MarkdownDatabase()
804 fieldsdb = FieldsDatabase()
805 svp64db = SVP64Database(root)
806 ppcdb = PPCDatabase(root)
807
808 db = set()
809 for (name, operands) in mdwndb:
810 (section, ppc) = ppcdb[name]
811 if ppc is None:
812 continue
813 svp64 = svp64db[ppc.names]
814 fields = fieldsdb[ppc.form]
815 record = Record(name=name,
816 section=section, ppc=ppc, svp64=svp64,
817 operands=operands, fields=fields)
818 db.add(record)
819
820 self.__db = tuple(sorted(db))
821
822 return super().__init__()
823
824 def __repr__(self):
825 return repr(self.__db)
826
827 def __iter__(self):
828 yield from self.__db
829
830 @_functools.lru_cache(maxsize=None)
831 def __contains__(self, key):
832 return self.__getitem__(key) is not None
833
834 @_functools.lru_cache(maxsize=None)
835 def __getitem__(self, key):
836 if isinstance(key, (int, Instruction)):
837 key = int(key)
838 for record in self:
839 opcode = record.opcode
840 if ((opcode.value & opcode.mask) ==
841 (key & opcode.mask)):
842 return record
843 return None
844 elif isinstance(key, Opcode):
845 for record in self:
846 if record.opcode == key:
847 return record
848 elif isinstance(key, str):
849 for record in self:
850 if record.name == key:
851 return record
852 return None