flags.add(flag)
record["flags"] = PPCRecord.Flags(flags)
- return dataclass(cls, record, keymap=PPCRecord.__KEYMAP, typemap=typemap)
+ return dataclass(cls, record,
+ keymap=PPCRecord.__KEYMAP,
+ typemap=typemap)
@cached_property
def names(self):
return frozenset(self.comment.split("=")[-1].split("/"))
+class PPCMultiRecord(frozenset):
+ @cached_property
+ def unified(self):
+ def merge(lhs, rhs):
+ value = 0
+ mask = 0
+ lvalue = lhs.opcode.value
+ rvalue = rhs.opcode.value
+ lmask = lhs.opcode.mask
+ rmask = rhs.opcode.mask
+ bits = max(lmask.bit_length(), rmask.bit_length())
+ for bit in range(bits):
+ lvstate = ((lvalue & (1 << bit)) != 0)
+ rvstate = ((rvalue & (1 << bit)) != 0)
+ lmstate = ((lmask & (1 << bit)) != 0)
+ rmstate = ((rmask & (1 << bit)) != 0)
+ vstate = lvstate
+ mstate = True
+ if (not lmstate or not rmstate) or (lvstate != rvstate):
+ vstate = 0
+ mstate = 0
+ value |= (vstate << bit)
+ mask |= (mstate << bit)
+
+ opcode = opcode=Opcode(value=value, mask=mask)
+
+ return _dataclasses.replace(lhs, opcode=opcode)
+
+ return _functools.reduce(merge, self)
+
+ def __getattr__(self, attr):
+ return getattr(self.unified, attr)
+
+
@_dataclasses.dataclass(eq=True, frozen=True)
class SVP64Record:
class ExtraMap(tuple):
cr_in: _CRInSel = _CRInSel.NONE
cr_out: _CROutSel = _CROutSel.NONE
extra: ExtraMap = ExtraMap()
- pu: bool = False
conditions: str = ""
mode: _SVMode = _SVMode.NORMAL
"Etype": "etype",
"CR in": "cr_in",
"CR out": "cr_out",
- "PU": "pu",
}
@classmethod
if value == "0":
record[key] = "NONE"
- record["extra"] = cls.ExtraMap(record.pop(f"{index}") for index in range(0, 4))
+ extra = []
+ for idx in range(0, 4):
+ extra.append(record.pop(f"{idx}"))
+
+ record["extra"] = cls.ExtraMap(extra)
return dataclass(cls, record, keymap=cls.__KEYMAP)
def transform(item):
(name, bitrange) = item
- return (name, tuple(bitrange.values()))
+ return (name, list(bitrange.values()))
self.__mapping = dict(map(transform, items))
return self.__mapping.get(key, None)
-class Operands:
- @_dataclasses.dataclass(eq=True, frozen=True)
- class Operand:
- name: str
+@_dataclasses.dataclass(eq=True, frozen=True)
+class Operand:
+ name: str
+
+ def disassemble(self, value, record, verbose=False):
+ raise NotImplementedError
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperand(Operand):
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields[self.name]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ else:
+ yield str(int(value))
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class ImmediateOperand(DynamicOperand):
+ pass
+
- def disassemble(self, value, record):
- raise NotImplementedError
+@_dataclasses.dataclass(eq=True, frozen=True)
+class StaticOperand(Operand):
+ value: int
+
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields[self.name]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ else:
+ yield str(int(value))
- @_dataclasses.dataclass(eq=True, frozen=True)
- class DynamicOperand(Operand):
- def disassemble(self, value, record):
- return str(int(value[record.fields[self.name]]))
- @_dataclasses.dataclass(eq=True, frozen=True)
- class StaticOperand(Operand):
- value: int = None
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperandTargetAddrLI(DynamicOperand):
+ @property
+ def name(self):
+ return "LI"
+
+ @name.setter
+ def name(self, _):
+ pass
- @_dataclasses.dataclass(eq=True, frozen=True)
- class DynamicOperandIFormLI(DynamicOperand):
- def disassemble(self, value, record):
- return hex(int(_selectconcat(
- value[record.fields["LI"]],
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields["LI"]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ yield "target_addr = EXTS(LI || 0b00))"
+ else:
+ yield hex(int(_selectconcat(value,
_SelectableInt(value=0b00, bits=2))))
- class DynamicOperandBFormBD(DynamicOperand):
- def disassemble(self, value, record):
- return hex(int(_selectconcat(
- value[record.fields["BD"]],
+
+class DynamicOperandTargetAddrBD(DynamicOperand):
+ @property
+ def name(self):
+ return "BD"
+
+ @name.setter
+ def name(self, _):
+ pass
+
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields["BD"]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ yield "target_addr = EXTS(BD || 0b00))"
+ else:
+ yield hex(int(_selectconcat(value,
_SelectableInt(value=0b00, bits=2))))
- @_dataclasses.dataclass(eq=True, frozen=True)
- class DynamicOperandGPR(DynamicOperand):
- def disassemble(self, value, record):
- return f"r{super().disassemble(value=value, record=record)}"
- @_dataclasses.dataclass(eq=True, frozen=True)
- class DynamicOperandFPR(DynamicOperand):
- def disassemble(self, value, record):
- return f"f{super().disassemble(value=value, record=record)}"
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperandGPR(DynamicOperand):
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields[self.name]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ else:
+ yield f"r{str(int(value))}"
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperandFPR(DynamicOperand):
+ def disassemble(self, value, record, verbose=False):
+ span = record.fields[self.name]
+ value = value[span]
+ if verbose:
+ yield f"{int(value):0{value.bits}b}"
+ yield repr(span)
+ else:
+ yield f"f{str(int(value))}"
+
- def __init__(self, insn, iterable):
+class Operands(tuple):
+ def __new__(cls, insn, iterable):
branches = {
- "b": {"LI": self.__class__.DynamicOperandIFormLI},
- "ba": {"LI": self.__class__.DynamicOperandIFormLI},
- "bl": {"LI": self.__class__.DynamicOperandIFormLI},
- "bla": {"LI": self.__class__.DynamicOperandIFormLI},
- "bc": {"BD": self.__class__.DynamicOperandBFormBD},
- "bca": {"BD": self.__class__.DynamicOperandBFormBD},
- "bcl": {"BD": self.__class__.DynamicOperandBFormBD},
- "bcla": {"BD": self.__class__.DynamicOperandBFormBD},
+ "b": {"target_addr": DynamicOperandTargetAddrLI},
+ "ba": {"target_addr": DynamicOperandTargetAddrLI},
+ "bl": {"target_addr": DynamicOperandTargetAddrLI},
+ "bla": {"target_addr": DynamicOperandTargetAddrLI},
+ "bc": {"target_addr": DynamicOperandTargetAddrBD},
+ "bca": {"target_addr": DynamicOperandTargetAddrBD},
+ "bcl": {"target_addr": DynamicOperandTargetAddrBD},
+ "bcla": {"target_addr": DynamicOperandTargetAddrBD},
}
operands = []
for operand in iterable:
- dynamic_cls = self.__class__.DynamicOperand
- static_cls = self.__class__.StaticOperand
+ dynamic_cls = DynamicOperand
+ static_cls = StaticOperand
if "=" in operand:
(name, value) = operand.split("=")
operand = static_cls(name=name, value=int(value))
+ operands.append(operand)
else:
+ if operand.endswith(")"):
+ operand = operand.replace("(", " ").replace(")", "")
+ (immediate, _, operand) = operand.partition(" ")
+ else:
+ immediate = None
+
+ if immediate is not None:
+ operands.append(ImmediateOperand(name=immediate))
+
if insn in branches and operand in branches[insn]:
dynamic_cls = branches[insn][operand]
if operand in _RegType.__members__:
regtype = _RegType[operand]
if regtype is _RegType.GPR:
- dynamic_cls = self.__class__.DynamicOperandGPR
+ dynamic_cls = DynamicOperandGPR
elif regtype is _RegType.FPR:
- dynamic_cls = self.__class__.DynamicOperandFPR
+ dynamic_cls = DynamicOperandFPR
operand = dynamic_cls(name=operand)
+ operands.append(operand)
- operands.append(operand)
-
- self.__operands = operands
-
- return super().__init__()
-
- def __repr__(self):
- return self.__operands.__repr__()
-
- def __iter__(self):
- yield from self.__operands
+ return super().__new__(cls, operands)
def __contains__(self, key):
return self.__getitem__(key) is not None
def __getitem__(self, key):
- for operand in self.__operands:
+ for operand in self:
if operand.name == key:
return operand
@property
def dynamic(self):
for operand in self:
- if isinstance(operand, self.__class__.DynamicOperand):
+ if isinstance(operand, DynamicOperand):
yield operand
@property
def static(self):
for operand in self:
- if isinstance(operand, self.__class__.StaticOperand):
+ if isinstance(operand, StaticOperand):
yield operand
def __hash__(self):
return hash(int(self))
- def disassemble(self, db, byteorder="little"):
+ def record(self, db):
+ record = db[self]
+ if record is None:
+ raise KeyError(self)
+ return record
+
+ def disassemble(self, db, byteorder="little", verbose=False):
raise NotImplementedError
def integer(cls, value, byteorder="little"):
return super().integer(bits=32, value=value, byteorder=byteorder)
- def disassemble(self, db, byteorder="little"):
+ @property
+ def binary(self):
+ bits = []
+ for idx in range(32):
+ bit = int(self[idx])
+ bits.append(bit)
+ return "".join(map(str, bits))
+
+ def spec(self, db):
+ record = self.record(db=db)
+
+ immediate = ""
+ dynamic_operands = []
+ for operand in record.operands.dynamic:
+ name = operand.name
+ if immediate:
+ name = f"{immediate}({name})"
+ immediate = ""
+ if isinstance(operand, ImmediateOperand):
+ immediate = operand.name
+ if not immediate:
+ dynamic_operands.append(name)
+
+ static_operands = []
+ for operand in record.operands.static:
+ static_operands.append(f"{operand.name}={operand.value}")
+
+ operands = ""
+ if dynamic_operands:
+ operands += f" {','.join(dynamic_operands)}"
+ if static_operands:
+ operands += f" ({' '.join(static_operands)})"
+
+ return f"{record.name}{operands}"
+
+ def opcode(self, db):
+ record = self.record(db=db)
+ return f"0x{record.opcode.value:08x}"
+
+ def mask(self, db):
+ record = self.record(db=db)
+ return f"0x{record.opcode.mask:08x}"
+
+ def disassemble(self, db, byteorder="little", verbose=False):
integer = int(self)
blob = integer.to_bytes(length=4, byteorder=byteorder)
blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
- record = db[self]
+ record = self.record(db=db)
if record is None:
yield f"{blob} .long 0x{integer:08x}"
+ return
+
+ operands = []
+ for operand in record.operands.dynamic:
+ operand = " ".join(operand.disassemble(value=self,
+ record=record, verbose=False))
+ operands.append(operand)
+ if operands:
+ operands = ",".join(operands)
+ operands = f" {operands}"
else:
- operands = []
- for operand in record.operands.dynamic:
- operand = operand.disassemble(self, record)
- operands.append(operand)
- if operands:
- operands = ",".join(operands)
- operands = f" {operands}"
- else:
- operands = ""
- yield f"{blob} {record.name}{operands}"
+ operands = ""
+
+ yield f"{blob} {record.name}{operands}"
+
+ if verbose:
+ indent = (" " * 4)
+ binary = self.binary
+ spec = self.spec(db=db)
+ opcode = self.opcode(db=db)
+ mask = self.mask(db=db)
+ yield f"{indent}spec"
+ yield f"{indent}{indent}{spec}"
+ yield f"{indent}binary"
+ yield f"{indent}{indent}[0:8] {binary[0:8]}"
+ yield f"{indent}{indent}[8:16] {binary[8:16]}"
+ yield f"{indent}{indent}[16:24] {binary[16:24]}"
+ yield f"{indent}{indent}[24:32] {binary[24:32]}"
+ yield f"{indent}opcode"
+ yield f"{indent}{indent}{opcode}"
+ yield f"{indent}mask"
+ yield f"{indent}{indent}{mask}"
+ for operand in record.operands:
+ name = operand.name
+ yield f"{indent}{name}"
+ parts = operand.disassemble(value=self,
+ record=record, verbose=True)
+ for part in parts:
+ yield f"{indent}{indent}{part}"
+ yield ""
+
class PrefixedInstruction(Instruction):
class Prefix(WordInstruction.remap(range(0, 32))):
class NormalMode(Mode):
- class normal(Mode):
- """normal mode"""
+ class simple(Mode):
+ """simple mode"""
dz: Mode[3]
sz: Mode[4]
dz: Mode[3]
sz: Mode[3]
- normal: normal
+ simple: simple
smr: smr
pmr: pmr
svmr: svmr
class LDSTImmMode(Mode):
- class normal(Mode):
- """normal mode"""
+ class simple(Mode):
+ """simple mode"""
zz: Mode[3]
els: Mode[4]
dz: Mode[3]
els: Mode[3]
RC1: Mode[4]
- normal: normal
+ simple: simple
spu: spu
ffrc1: ffrc1
ffrc0: ffrc0
class LDSTIdxMode(Mode):
- class normal(Mode):
- """normal mode"""
+ class simple(Mode):
+ """simple mode"""
SEA: Mode[2]
sz: Mode[3]
dz: Mode[3]
dz: Mode[3]
sz: Mode[3]
- normal: normal
+ simple: simple
stride: stride
sat: sat
prrc1: prrc1
prefix: Prefix
- def disassemble(self, db, byteorder="little"):
+ @property
+ def binary(self):
+ bits = []
+ for idx in range(64):
+ bit = int(self[idx])
+ bits.append(bit)
+ return "".join(map(str, bits))
+
+ def spec(self, db):
+ return f"sv.{self.suffix.spec(db=db)}"
+
+ def opcode(self, db):
+ return self.suffix.opcode(db=db)
+
+ def mask(self, db):
+ return self.suffix.mask(db=db)
+
+ def mode(self, db):
+ record = self.record(db=db)
+
+ Rc = False
+ if record.operands["Rc"] is not None:
+ Rc = bool(self[record.fields["Rc"]])
+
+ record = self.record(db=db)
+ subvl = self.prefix.rm.subvl
+ mode = self.prefix.rm.mode
+ sel = mode.sel
+
+ if record.svp64.mode is _SVMode.NORMAL:
+ mode = mode.normal
+ if sel == 0b00:
+ if mode[2] == 0b0:
+ mode = mode.simple
+ else:
+ if subvl == 0b00:
+ if mode[3] == 0b0:
+ mode = mode.smr
+ else:
+ mode = mode.pmr
+ else:
+ if mode[4] == 0b0:
+ mode = mode.svmr
+ else:
+ mode = mode.pu
+ elif sel == 0b01:
+ if Rc:
+ mode = mode.ffrc1
+ else:
+ mode = mode.ffrc0
+ elif sel == 0b10:
+ if subvl == 0b00:
+ mode = mode.sat
+ else:
+ if mode[4]:
+ mode = mode.satx
+ else:
+ mode = mode.satpu
+ elif sel == 0b11:
+ if Rc:
+ mode = mode.prrc1
+ else:
+ mode = mode.prrc0
+ elif record.svp64.mode is _SVMode.LDST_IMM:
+ mode = mode.ldst_imm
+ if sel == 0b00:
+ if mode[2] == 0b0:
+ mode = mode.simple
+ else:
+ mode = mode.spu
+ elif sel == 0b01:
+ if Rc:
+ mode = mode.ffrc1
+ else:
+ mode = mode.ffrc0
+ elif sel == 0b10:
+ mode = mode.sat
+ elif sel == 0b11:
+ if Rc:
+ mode = mode.prrc1
+ else:
+ mode = mode.prrc0
+ elif record.svp64.mode is _SVMode.LDST_IMM:
+ mode = mode.ldst_idx
+ if mode.sel == 0b00:
+ mode = mode.simple
+ elif mode.sel == 0b01:
+ mode = mode.stride
+ elif mode.sel == 0b10:
+ mode = mode.sat
+ elif mode.sel == 0b11:
+ if Rc:
+ mode = mode.prrc1
+ else:
+ mode = mode.prrc0
+
+ modes = {
+ NormalMode.simple: "normal: simple",
+ NormalMode.smr: "normal: smr",
+ NormalMode.pmr: "normal: pmr",
+ NormalMode.svmr: "normal: svmr",
+ NormalMode.pu: "normal: pu",
+ NormalMode.ffrc1: "normal: ffrc1",
+ NormalMode.ffrc0: "normal: ffrc0",
+ NormalMode.sat: "normal: sat",
+ NormalMode.satx: "normal: satx",
+ NormalMode.satpu: "normal: satpu",
+ NormalMode.prrc1: "normal: prrc1",
+ NormalMode.prrc0: "normal: prrc0",
+ LDSTImmMode.simple: "ld/st imm: simple",
+ LDSTImmMode.spu: "ld/st imm: spu",
+ LDSTImmMode.ffrc1: "ld/st imm: ffrc1",
+ LDSTImmMode.ffrc0: "ld/st imm: ffrc0",
+ LDSTImmMode.sat: "ld/st imm: sat",
+ LDSTImmMode.prrc1: "ld/st imm: prrc1",
+ LDSTImmMode.prrc0: "ld/st imm: prrc0",
+ LDSTIdxMode.simple: "ld/st idx simple",
+ LDSTIdxMode.stride: "ld/st idx stride",
+ LDSTIdxMode.sat: "ld/st idx sat",
+ LDSTIdxMode.prrc1: "ld/st idx prrc1",
+ LDSTIdxMode.prrc0: "ld/st idx prrc0",
+ }
+ for (cls, desc) in modes.items():
+ if isinstance(mode, cls):
+ return (mode, desc)
+
+ raise NotImplementedError
+
+ def disassemble(self, db, byteorder="little", verbose=False):
integer_prefix = int(self.prefix)
blob_prefix = integer_prefix.to_bytes(length=4, byteorder=byteorder)
blob_prefix = " ".join(map(lambda byte: f"{byte:02x}", blob_prefix))
blob_suffix = integer_suffix.to_bytes(length=4, byteorder=byteorder)
blob_suffix = " ".join(map(lambda byte: f"{byte:02x}", blob_suffix))
- record = db[self.suffix]
+ record = self.record(db=db)
if record is None or record.svp64 is None:
yield f"{blob_prefix} .long 0x{int(self.prefix):08x}"
yield f"{blob_suffix} .long 0x{int(self.suffix):08x}"
- else:
- yield f"{blob_prefix} sv.{record.name}"
- yield f"{blob_suffix}"
+ return
+
+ yield f"{blob_prefix} sv.{record.name}"
+ yield f"{blob_suffix}"
+
+ (mode, mode_desc) = self.mode(db=db)
+
+ if verbose:
+ indent = (" " * 4)
+ binary = self.binary
+ spec = self.spec(db=db)
+ opcode = self.opcode(db=db)
+ mask = self.mask(db=db)
+ yield f"{indent}spec"
+ yield f"{indent}{indent}{spec}"
+ yield f"{indent}binary"
+ yield f"{indent}{indent}[0:8] {binary[0:8]}"
+ yield f"{indent}{indent}[8:16] {binary[8:16]}"
+ yield f"{indent}{indent}[16:24] {binary[16:24]}"
+ yield f"{indent}{indent}[24:32] {binary[24:32]}"
+ yield f"{indent}{indent}[32:40] {binary[32:40]}"
+ yield f"{indent}{indent}[40:48] {binary[40:48]}"
+ yield f"{indent}{indent}[48:56] {binary[48:56]}"
+ yield f"{indent}{indent}[56:64] {binary[56:64]}"
+ yield f"{indent}opcode"
+ yield f"{indent}{indent}{opcode}"
+ yield f"{indent}mask"
+ yield f"{indent}{indent}{mask}"
+ for operand in record.operands:
+ name = operand.name
+ yield f"{indent}{name}"
+ parts = operand.disassemble(value=self,
+ record=record, verbose=True)
+ for part in parts:
+ yield f"{indent}{indent}{part}"
+
+ yield f"{indent}mode"
+ yield f"{indent}{indent}{mode_desc}"
+ yield ""
def parse(stream, factory):
+ def match(entry):
+ return ("TODO" not in frozenset(entry.values()))
+
lines = filter(lambda line: not line.strip().startswith("#"), stream)
entries = _csv.DictReader(lines)
- entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries)
+ entries = filter(match, entries)
return tuple(map(factory, entries))
class PPCDatabase:
- def __init__(self, root, mdwndb, fieldsdb):
+ def __init__(self, root, mdwndb):
# The code below groups the instructions by section:identifier.
# We use the comment as an identifier, there's nothing better.
# The point is to capture different opcodes for the same instruction.
section.Mode.INTEGER: IntegerOpcode,
section.Mode.PATTERN: PatternOpcode,
}[section.mode]
- factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
+ factory = _functools.partial(
+ PPCRecord.CSV, opcode_cls=opcode_cls)
with open(path, "r", encoding="UTF-8") as stream:
for insn in parse(stream, factory):
records[section][insn.comment].add(insn)
- # Once we collected all instructions with the same identifier,
- # it's time to merge the different opcodes into the single pattern.
- # At this point, we only consider masks; the algorithm as follows:
- # 1. If any of two masks ignores the bit, it's ignored entirely.
- # 2. If the bit is not equal between masks, it's ignored.
- # 3. Otherwise the bits are equal and considered.
- def merge(lhs, rhs):
- value = 0
- mask = 0
- lvalue = lhs.opcode.value
- rvalue = rhs.opcode.value
- lmask = lhs.opcode.mask
- rmask = rhs.opcode.mask
- bits = max(lmask.bit_length(), rmask.bit_length())
- for bit in range(bits):
- lvstate = ((lvalue & (1 << bit)) != 0)
- rvstate = ((rvalue & (1 << bit)) != 0)
- lmstate = ((lmask & (1 << bit)) != 0)
- rmstate = ((rmask & (1 << bit)) != 0)
- vstate = lvstate
- mstate = True
- if (not lmstate or not rmstate) or (lvstate != rvstate):
- vstate = 0
- mstate = 0
- value |= (vstate << bit)
- mask |= (mstate << bit)
- return _dataclasses.replace(lhs, opcode=Opcode(value=value, mask=mask))
-
db = dd(set)
for (section, group) in records.items():
for records in group.values():
- db[section].add(_functools.reduce(merge, records))
+ db[section].add(PPCMultiRecord(records))
self.__db = db
self.__mdwndb = mdwndb
- self.__fieldsdb = fieldsdb
return super().__init__()
for (section, records) in self.__db.items():
for record in records:
- if (exact_match(key, record) or
- Rc_match(key, record) or
+ if exact_match(key, record):
+ return (section, record)
+
+ for record in records:
+ if (Rc_match(key, record) or
LK_match(key, record) or
AA_match(key, record)):
return (section, record)
mdwndb = MarkdownDatabase()
fieldsdb = FieldsDatabase()
- ppcdb = PPCDatabase(root=root, mdwndb=mdwndb, fieldsdb=fieldsdb)
+ ppcdb = PPCDatabase(root=root, mdwndb=mdwndb)
svp64db = SVP64Database(root=root, ppcdb=ppcdb)
db = set()