Skip to content

Instantly share code, notes, and snippets.

@geekman
Created September 27, 2013 16:13
Show Gist options
  • Select an option

  • Save geekman/6731023 to your computer and use it in GitHub Desktop.

Select an option

Save geekman/6731023 to your computer and use it in GitHub Desktop.
decodes TPI opcodes

Given a stream of TPI opcodes, translates it into its human-readable form:

C:\> decode_tpi.py
 [ 72 ] [ 00 ]
 [ F3 00 ] [ ]
 [ 68 C0 ] [ ]
 [ 69 3F ] [ ]
 [ 24 ] [ 1E ]
 [ 24 ] [ 90 ]
 [ 24 ] [ 03 ]
^Z
SIN 0x32 = 0x0
SOUT 0x33, 0x0
SSTPR 0x0, 0xc0
SSTPR 0x1, 0x3f
SLD PR+ = 0x1e
SLD PR+ = 0x90
SLD PR+ = 0x3
#!/usr/bin/python
#
# decodes Atmel TPI opcodes (for programming ATtinys)
# accepts string of raw hex bytes on stdin, such as "72 00 f3 00"
# invalid characters will be skipped, but please avoid text like "deadcow"
#
import sys
class TpiOpcode:
"""Holds information about a TPI opcode."""
def __init__(self, opcode, mask, name, fmt, args=1):
self.opcode = opcode
self.mask = mask
self.name = name
self.fmt = fmt
self.args = args
def match(self, insn):
"""Tests if the instruction byte matches this opcode."""
return insn & self.mask == self.opcode
def __str__(self):
return self.name
class TpiOpcodeDirect(TpiOpcode):
def getaddr(self, x):
return ((x & 0x60) >> 1) | (x & 0xF)
class TpiOpcodeCS(TpiOpcode):
def getaddr(self, x):
return x & 0xF
class TpiOpcodePR(TpiOpcode):
def getaddr(self, x):
return x & 0x1
class TpiInsn:
"""TPI instruction. Represents an "instance" of an opcode."""
def __init__(self, insn, opcode, args):
self.insn = insn
self.opcode = opcode
self.args = args
def __str__(self):
fmt = self.opcode.fmt
# has address?
if hasattr(self.opcode, 'getaddr'):
fmt = fmt.replace('<a>', hex(self.opcode.getaddr(self.insn)))
fmt = fmt.replace('<data>', ','.join([hex(a) for a in self.args]))
return str(self.opcode) + ' ' + fmt
class TpiParser:
"""Parser for TPI opcodes."""
opcodes = [
TpiOpcode(0x20, 0xFF, 'SLD', 'PR = <data>'),
TpiOpcode(0x24, 0xFF, 'SLD', 'PR+ = <data>'),
TpiOpcode(0x60, 0xFF, 'SST', 'PR, <data>'),
TpiOpcode(0x64, 0xFF, 'SST', 'PR+, <data>'),
TpiOpcodePR(0x68, 0xFE, 'SSTPR', '<a>, <data>'),
TpiOpcodeDirect(0x10, 0x90, 'SIN', '<a> = <data>'),
TpiOpcodeDirect(0x90, 0x90, 'SOUT', '<a>, <data>'),
TpiOpcodeCS(0x80, 0x80, 'SLDCS', '<a> = <data>'),
TpiOpcodeCS(0xC0, 0xC0, 'SSTCS', '<a>, <data>'),
TpiOpcode(0xE0, 0xFF, 'SKEY', '<data>', 8),
]
@staticmethod
def parse1(q):
"""
Parses the first recognizable instruction from byte array @q.
The instruction and its argument(s) will be popped off.
If the instruction is not recognized or its full argument(s) are not
present, it returns None and the byte array is untouched.
"""
insn = q[0]
opcode = None
for o in TpiParser.opcodes:
if o.match(insn):
opcode = o
break
if opcode and len(q) - 1 >= opcode.args:
q.pop(0)
inst = TpiInsn(insn, opcode, q[0 : opcode.args])
del q[0:opcode.args]
return inst
return None
def main():
hexin = ''
q = []
for line in sys.stdin:
for c in line:
# if hex digit, accept it
if c.isdigit() or c.lower() in 'abcdef':
hexin += c
elif hexin:
# if an invalid character, reset state
hexin = ''
# two hex digits make a byte
if len(hexin) == 2:
q.append(int(hexin, 16))
hexin = ''
while q:
insn = TpiParser.parse1(q)
if insn:
print insn
else:
break
# there shouldn't be any remaining instruction bytes left
assert not q, "unrecognized opcodes: %r" % [hex(x) for x in q]
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment