- 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."""
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
(0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
|
||||
@ -21,7 +22,7 @@ streamInstructions = [
|
||||
|
||||
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 -
|
||||
(0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
|
||||
@ -152,34 +153,58 @@ def bitRepr(value, bits):
|
||||
value = value >> 1
|
||||
return s
|
||||
|
||||
def makeOpcodeDict(instructionList):
|
||||
|
||||
_mnemonicPat = re.compile("[A-Z][A-Z0-9]*$")
|
||||
|
||||
def _makeDict(instructionList):
|
||||
opcodeDict = {}
|
||||
for op, mnemonic, argbits, name, pops, pushes in instructionList:
|
||||
if argbits:
|
||||
mnemonicDict = {}
|
||||
for op, mnemonic, argBits, name, pops, pushes in instructionList:
|
||||
assert _mnemonicPat.match(mnemonic)
|
||||
mnemonicDict[mnemonic] = op, argBits, name
|
||||
if argBits:
|
||||
argoffset = op
|
||||
for i in range(1 << argbits):
|
||||
opcodeDict[op+i] = mnemonic, argbits, argoffset, name
|
||||
for i in range(1 << argBits):
|
||||
opcodeDict[op+i] = mnemonic, argBits, argoffset, name
|
||||
else:
|
||||
opcodeDict[op] = mnemonic, 0, 0, name
|
||||
return opcodeDict
|
||||
return opcodeDict, mnemonicDict
|
||||
|
||||
streamOpcodeDict = makeOpcodeDict(streamInstructions)
|
||||
opcodeDict = makeOpcodeDict(instructions)
|
||||
streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
|
||||
opcodeDict, mnemonicDict = _makeDict(instructions)
|
||||
|
||||
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:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def fromBytecode(self, bytecode):
|
||||
self.bytecode = array.array("B")
|
||||
self.bytecode.fromstring(bytecode)
|
||||
self.bytecode = array.array("B", bytecode)
|
||||
if hasattr(self, "assembly"):
|
||||
del self.assembly
|
||||
|
||||
def fromAssembly(self, assembly):
|
||||
self.assembly = assembly
|
||||
if hasattr(self, "bytecode"):
|
||||
del self.bytecode
|
||||
|
||||
def getBytecode(self):
|
||||
if not hasattr(self, "bytecode"):
|
||||
@ -191,8 +216,121 @@ class Program:
|
||||
self._disassemble()
|
||||
return self.assembly
|
||||
|
||||
def _assemble(self):
|
||||
xxx
|
||||
def toXML(self, writer, ttFont):
|
||||
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):
|
||||
assembly = []
|
||||
@ -203,47 +341,57 @@ class Program:
|
||||
op = bytecode[i]
|
||||
arg = 0
|
||||
try:
|
||||
mnemonic, argbits, argoffset, name = opcodeDict[op]
|
||||
mnemonic, argBits, argoffset, name = opcodeDict[op]
|
||||
except KeyError:
|
||||
try:
|
||||
mnemonic, argbits, argoffset, name = streamOpcodeDict[op]
|
||||
mnemonic, argBits, argoffset, name = streamOpcodeDict[op]
|
||||
except KeyError:
|
||||
raise tt_instructions_error, "illegal opcode: 0x%.2x" % op
|
||||
pushbytes = pushwords = 0
|
||||
if argbits:
|
||||
pushBytes = pushWords = 0
|
||||
if argBits:
|
||||
if mnemonic == "PUSHB":
|
||||
pushbytes = op - argoffset + 1
|
||||
pushBytes = op - argoffset + 1
|
||||
else:
|
||||
pushwords = op - argoffset + 1
|
||||
pushWords = op - argoffset + 1
|
||||
else:
|
||||
i = i + 1
|
||||
if mnemonic == "NPUSHB":
|
||||
pushbytes = bytecode[i]
|
||||
pushBytes = bytecode[i]
|
||||
else:
|
||||
pushwords = bytecode[i]
|
||||
pushWords = bytecode[i]
|
||||
i = i + 1
|
||||
assembly.append(mnemonic + "[ ]")
|
||||
for j in range(pushbytes):
|
||||
assembly.append(`bytecode[i]`)
|
||||
nValues = pushBytes or pushWords
|
||||
assert nValues > 0
|
||||
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
|
||||
for j in range(0, pushwords, 2):
|
||||
assembly.append(`(bytecode[i] << 8) + bytecode[i+1]`)
|
||||
for j in range(pushWords):
|
||||
# cast to signed int16
|
||||
value = (bytecode[i] << 8) | bytecode[i+1]
|
||||
if value >= 0x8000:
|
||||
value = value - 0x10000
|
||||
assembly.append(`value`)
|
||||
i = i + 2
|
||||
else:
|
||||
if argbits:
|
||||
assembly.append(mnemonic + "[%s]" % bitRepr(op - argoffset, argbits))
|
||||
if argBits:
|
||||
assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
|
||||
else:
|
||||
assembly.append(mnemonic + "[ ]")
|
||||
i = i + 1
|
||||
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-'
|
||||
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'
|
||||
if __name__ == "__main__":
|
||||
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.fromBytecode(fpgm)
|
||||
for line in p.getAssembly():
|
||||
print line
|
||||
p.fromBytecode(bc)
|
||||
as = p.getAssembly()
|
||||
p.fromAssembly(as)
|
||||
print bc == p.getBytecode()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user