- added assembler: we've got a full round trip now!
- added toXML() and fromXML() methods git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@74 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
parent
53602486b4
commit
51e75db43e
@ -1,13 +1,14 @@
|
|||||||
"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
|
"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
|
||||||
|
|
||||||
import array
|
import array
|
||||||
|
import re, string
|
||||||
|
from fontTools.misc.textTools import num2binary, binary2num, readHex
|
||||||
|
|
||||||
# first, the list of instructions that eat bytes or words from the instruction stream
|
# first, the list of instructions that eat bytes or words from the instruction stream
|
||||||
|
|
||||||
streamInstructions = [
|
streamInstructions = [
|
||||||
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
||||||
# opcode mnemonic argbits descriptive name pops pushes eats from instruction stream pushes
|
# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
|
||||||
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
||||||
(0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
|
(0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
|
||||||
(0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
|
(0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
|
||||||
@ -21,7 +22,7 @@ streamInstructions = [
|
|||||||
|
|
||||||
instructions = [
|
instructions = [
|
||||||
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
||||||
# opcode mnemonic argbits descriptive name pops pushes pops pushes
|
# opcode mnemonic argBits descriptive name pops pushes pops pushes
|
||||||
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
|
||||||
(0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
|
(0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
|
||||||
(0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
|
(0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
|
||||||
@ -152,34 +153,58 @@ def bitRepr(value, bits):
|
|||||||
value = value >> 1
|
value = value >> 1
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def makeOpcodeDict(instructionList):
|
|
||||||
|
_mnemonicPat = re.compile("[A-Z][A-Z0-9]*$")
|
||||||
|
|
||||||
|
def _makeDict(instructionList):
|
||||||
opcodeDict = {}
|
opcodeDict = {}
|
||||||
for op, mnemonic, argbits, name, pops, pushes in instructionList:
|
mnemonicDict = {}
|
||||||
if argbits:
|
for op, mnemonic, argBits, name, pops, pushes in instructionList:
|
||||||
|
assert _mnemonicPat.match(mnemonic)
|
||||||
|
mnemonicDict[mnemonic] = op, argBits, name
|
||||||
|
if argBits:
|
||||||
argoffset = op
|
argoffset = op
|
||||||
for i in range(1 << argbits):
|
for i in range(1 << argBits):
|
||||||
opcodeDict[op+i] = mnemonic, argbits, argoffset, name
|
opcodeDict[op+i] = mnemonic, argBits, argoffset, name
|
||||||
else:
|
else:
|
||||||
opcodeDict[op] = mnemonic, 0, 0, name
|
opcodeDict[op] = mnemonic, 0, 0, name
|
||||||
return opcodeDict
|
return opcodeDict, mnemonicDict
|
||||||
|
|
||||||
streamOpcodeDict = makeOpcodeDict(streamInstructions)
|
streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
|
||||||
opcodeDict = makeOpcodeDict(instructions)
|
opcodeDict, mnemonicDict = _makeDict(instructions)
|
||||||
|
|
||||||
tt_instructions_error = "TT instructions error"
|
tt_instructions_error = "TT instructions error"
|
||||||
|
|
||||||
|
|
||||||
|
_comment = r"/\*.*?\*/"
|
||||||
|
_instruction = r"([A-Z][A-Z0-9]*)\s*\[(.*?)\]"
|
||||||
|
_number = r"-?[0-9]+"
|
||||||
|
_token = "(%s)|(%s)|(%s)" % (_instruction, _number, _comment)
|
||||||
|
|
||||||
|
_tokenRE = re.compile(_token)
|
||||||
|
_whiteRE = re.compile(r"\s*")
|
||||||
|
|
||||||
|
def _skipWhite(data, pos, _whiteRE=_whiteRE):
|
||||||
|
m = _whiteRE.match(data, pos)
|
||||||
|
newPos = m.regs[0][1]
|
||||||
|
assert newPos >= pos
|
||||||
|
return newPos
|
||||||
|
|
||||||
|
|
||||||
class Program:
|
class Program:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def fromBytecode(self, bytecode):
|
def fromBytecode(self, bytecode):
|
||||||
self.bytecode = array.array("B")
|
self.bytecode = array.array("B", bytecode)
|
||||||
self.bytecode.fromstring(bytecode)
|
if hasattr(self, "assembly"):
|
||||||
|
del self.assembly
|
||||||
|
|
||||||
def fromAssembly(self, assembly):
|
def fromAssembly(self, assembly):
|
||||||
self.assembly = assembly
|
self.assembly = assembly
|
||||||
|
if hasattr(self, "bytecode"):
|
||||||
|
del self.bytecode
|
||||||
|
|
||||||
def getBytecode(self):
|
def getBytecode(self):
|
||||||
if not hasattr(self, "bytecode"):
|
if not hasattr(self, "bytecode"):
|
||||||
@ -191,8 +216,121 @@ class Program:
|
|||||||
self._disassemble()
|
self._disassemble()
|
||||||
return self.assembly
|
return self.assembly
|
||||||
|
|
||||||
def _assemble(self):
|
def toXML(self, writer, ttFont):
|
||||||
xxx
|
if ttFont.disassembleInstructions:
|
||||||
|
assembly = self.getAssembly()
|
||||||
|
writer.begintag("assembly")
|
||||||
|
writer.newline()
|
||||||
|
i = 0
|
||||||
|
nInstr = len(assembly)
|
||||||
|
while i < nInstr:
|
||||||
|
instr = assembly[i]
|
||||||
|
writer.write(instr)
|
||||||
|
writer.newline()
|
||||||
|
m = _pushCountPat.match(instr)
|
||||||
|
i = i + 1
|
||||||
|
if m:
|
||||||
|
nValues = int(m.group(1))
|
||||||
|
line = []
|
||||||
|
j = 0
|
||||||
|
for j in range(nValues):
|
||||||
|
if j and not (j % 25):
|
||||||
|
writer.write(string.join(line, " "))
|
||||||
|
writer.newline()
|
||||||
|
line = []
|
||||||
|
line.append(assembly[i+j])
|
||||||
|
writer.write(string.join(line, " "))
|
||||||
|
writer.newline()
|
||||||
|
i = i + j + 1
|
||||||
|
writer.endtag("assembly")
|
||||||
|
else:
|
||||||
|
writer.begintag("bytecode")
|
||||||
|
writer.newline()
|
||||||
|
writer.dumphex(self.getBytecode())
|
||||||
|
writer.endtag("bytecode")
|
||||||
|
|
||||||
|
def fromXML(self, (name, attrs, content), ttFont):
|
||||||
|
if name == "assembly":
|
||||||
|
self.fromAssembly(content)
|
||||||
|
self._assemble()
|
||||||
|
del self.assembly
|
||||||
|
else:
|
||||||
|
assert name == "bytecode"
|
||||||
|
self.fromBytecode(readHex(content))
|
||||||
|
|
||||||
|
def _assemble(self,
|
||||||
|
skipWhite=_skipWhite, mnemonicDict=mnemonicDict, strip=string.strip,
|
||||||
|
binary2num=binary2num):
|
||||||
|
assembly = self.assembly
|
||||||
|
if type(assembly) == type([]):
|
||||||
|
assembly = string.join(assembly, " ")
|
||||||
|
bytecode = []
|
||||||
|
push = bytecode.append
|
||||||
|
lenAssembly = len(assembly)
|
||||||
|
pos = skipWhite(assembly, 0)
|
||||||
|
while pos < lenAssembly:
|
||||||
|
m = _tokenRE.match(assembly, pos)
|
||||||
|
if m is None:
|
||||||
|
raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos-5:pos+15]
|
||||||
|
dummy, mnemonic, arg, number, comment = m.groups()
|
||||||
|
pos = m.regs[0][1]
|
||||||
|
if comment:
|
||||||
|
continue
|
||||||
|
|
||||||
|
arg = strip(arg)
|
||||||
|
if mnemonic not in ("NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
|
||||||
|
op, argBits, name = mnemonicDict[mnemonic]
|
||||||
|
if len(arg) <> argBits:
|
||||||
|
raise tt_instructions_error, "Incorrect number of argument bits (%s[%s])" % (mnemonic, arg)
|
||||||
|
if arg:
|
||||||
|
arg = binary2num(arg)
|
||||||
|
push(op + arg)
|
||||||
|
else:
|
||||||
|
push(op)
|
||||||
|
else:
|
||||||
|
args = []
|
||||||
|
while pos < lenAssembly:
|
||||||
|
pos = skipWhite(assembly, pos)
|
||||||
|
m = _tokenRE.match(assembly, pos)
|
||||||
|
if m is None:
|
||||||
|
raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos:pos+15]
|
||||||
|
dummy, mnemonic, arg, number, comment = m.groups()
|
||||||
|
if number is None and comment is None:
|
||||||
|
break
|
||||||
|
pos = m.regs[0][1]
|
||||||
|
if comment is not None:
|
||||||
|
continue
|
||||||
|
args.append(int(number))
|
||||||
|
if max(args) > 255 or min(args) < 0:
|
||||||
|
words = 1
|
||||||
|
mnemonic = "PUSHW"
|
||||||
|
else:
|
||||||
|
words = 0
|
||||||
|
mnemonic = "PUSHB"
|
||||||
|
nArgs = len(args)
|
||||||
|
if nArgs <= 8:
|
||||||
|
op, argBits, name = streamMnemonicDict[mnemonic]
|
||||||
|
op = op + nArgs - 1
|
||||||
|
push(op)
|
||||||
|
elif nArgs < 256:
|
||||||
|
mnemonic = "N" + mnemonic
|
||||||
|
op, argBits, name = streamMnemonicDict[mnemonic]
|
||||||
|
push(op)
|
||||||
|
push(nArgs)
|
||||||
|
else:
|
||||||
|
raise tt_instructions_error, "More than 255 push arguments (%s)" % nArgs
|
||||||
|
if words:
|
||||||
|
for value in args:
|
||||||
|
push((value >> 8) & 0xff)
|
||||||
|
push(value & 0xff)
|
||||||
|
else:
|
||||||
|
for value in args:
|
||||||
|
push(value)
|
||||||
|
pos = skipWhite(assembly, pos)
|
||||||
|
|
||||||
|
if bytecode:
|
||||||
|
assert max(bytecode) < 256 and min(bytecode) >= 0
|
||||||
|
self.bytecode = array.array("B", bytecode)
|
||||||
|
|
||||||
def _disassemble(self):
|
def _disassemble(self):
|
||||||
assembly = []
|
assembly = []
|
||||||
@ -203,47 +341,57 @@ class Program:
|
|||||||
op = bytecode[i]
|
op = bytecode[i]
|
||||||
arg = 0
|
arg = 0
|
||||||
try:
|
try:
|
||||||
mnemonic, argbits, argoffset, name = opcodeDict[op]
|
mnemonic, argBits, argoffset, name = opcodeDict[op]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
mnemonic, argbits, argoffset, name = streamOpcodeDict[op]
|
mnemonic, argBits, argoffset, name = streamOpcodeDict[op]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise tt_instructions_error, "illegal opcode: 0x%.2x" % op
|
raise tt_instructions_error, "illegal opcode: 0x%.2x" % op
|
||||||
pushbytes = pushwords = 0
|
pushBytes = pushWords = 0
|
||||||
if argbits:
|
if argBits:
|
||||||
if mnemonic == "PUSHB":
|
if mnemonic == "PUSHB":
|
||||||
pushbytes = op - argoffset + 1
|
pushBytes = op - argoffset + 1
|
||||||
else:
|
else:
|
||||||
pushwords = op - argoffset + 1
|
pushWords = op - argoffset + 1
|
||||||
else:
|
else:
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if mnemonic == "NPUSHB":
|
if mnemonic == "NPUSHB":
|
||||||
pushbytes = bytecode[i]
|
pushBytes = bytecode[i]
|
||||||
else:
|
else:
|
||||||
pushwords = bytecode[i]
|
pushWords = bytecode[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
assembly.append(mnemonic + "[ ]")
|
nValues = pushBytes or pushWords
|
||||||
for j in range(pushbytes):
|
assert nValues > 0
|
||||||
assembly.append(`bytecode[i]`)
|
if nValues == 1:
|
||||||
|
assembly.append("%s[ ] /* %s value pushed */" % (mnemonic, nValues))
|
||||||
|
else:
|
||||||
|
assembly.append("%s[ ] /* %s values pushed */" % (mnemonic, nValues))
|
||||||
|
for j in range(pushBytes):
|
||||||
|
value = bytecode[i]
|
||||||
|
assembly.append(`value`)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
for j in range(0, pushwords, 2):
|
for j in range(pushWords):
|
||||||
assembly.append(`(bytecode[i] << 8) + bytecode[i+1]`)
|
# cast to signed int16
|
||||||
|
value = (bytecode[i] << 8) | bytecode[i+1]
|
||||||
|
if value >= 0x8000:
|
||||||
|
value = value - 0x10000
|
||||||
|
assembly.append(`value`)
|
||||||
i = i + 2
|
i = i + 2
|
||||||
else:
|
else:
|
||||||
if argbits:
|
if argBits:
|
||||||
assembly.append(mnemonic + "[%s]" % bitRepr(op - argoffset, argbits))
|
assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
|
||||||
else:
|
else:
|
||||||
assembly.append(mnemonic + "[ ]")
|
assembly.append(mnemonic + "[ ]")
|
||||||
i = i + 1
|
i = i + 1
|
||||||
self.assembly = assembly
|
self.assembly = assembly
|
||||||
del self.bytecode
|
|
||||||
|
|
||||||
|
|
||||||
fpgm = '@\01476&%\037\023\022\015\014\005\004\002, \260\003%E#E#ah\212 Eh \212#D`D-,KRXED\033!!Y-, EhD \260\001` E\260Fvh\030\212E`D-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\261\000\003%EhTX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Eh`\260\003%#D-,KRXED\033!!Y-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX?\033!\021Y-,KS#KQZX E\212`D\033!!Y-,KS#KQZX8\033!!Y-'
|
if __name__ == "__main__":
|
||||||
gpgm = '@\022\011\003\207@\005\200\004\207\000\010\007\202\001\010\004\202\000\010\000\020\320\355\020\336\355\001\020\336\375\032}\336\032\030\375\31610'
|
bc = """@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-"""
|
||||||
|
|
||||||
p = Program()
|
p = Program()
|
||||||
p.fromBytecode(fpgm)
|
p.fromBytecode(bc)
|
||||||
for line in p.getAssembly():
|
as = p.getAssembly()
|
||||||
print line
|
p.fromAssembly(as)
|
||||||
|
print bc == p.getBytecode()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user