blob: 03f778e8ab2ad1439ba49475ecf4748285d75f45 [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
2
Behdad Esfahbod1ae29592014-01-14 15:07:50 +08003from __future__ import print_function, division, absolute_import
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05004from fontTools.misc.py23 import *
5from fontTools.misc.textTools import num2binary, binary2num, readHex
Just7842e561999-12-16 21:34:53 +00006import array
Behdad Esfahbod14fb0312013-11-27 05:47:34 -05007import re
Just7842e561999-12-16 21:34:53 +00008
9# first, the list of instructions that eat bytes or words from the instruction stream
10
11streamInstructions = [
12# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
Just51e75db2000-02-01 15:30:15 +000013# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
Just7842e561999-12-16 21:34:53 +000014# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
15 (0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
16 (0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
17 (0xb0, 'PUSHB', 3, 'PushBytes', 0, -1), # b0, b1,..bn b0, b1, ...,bn
18 (0xb8, 'PUSHW', 3, 'PushWords', 0, -1), # w0,w1,..wn w0 ,w1, ...wn
19# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
20]
21
22
23# next, the list of "normal" instructions
24
25instructions = [
26# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
Just51e75db2000-02-01 15:30:15 +000027# opcode mnemonic argBits descriptive name pops pushes pops pushes
Just7842e561999-12-16 21:34:53 +000028# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
29 (0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
30 (0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
31 (0x60, 'ADD', 0, 'Add', 2, 1), # n2, n1 (n1 + n2)
32 (0x27, 'ALIGNPTS', 0, 'AlignPts', 2, 0), # p2, p1 -
33 (0x3c, 'ALIGNRP', 0, 'AlignRelativePt', -1, 0), # p1, p2, ... , ploopvalue -
34 (0x5a, 'AND', 0, 'LogicalAnd', 2, 1), # e2, e1 b
35 (0x2b, 'CALL', 0, 'CallFunction', 1, 0), # f -
36 (0x67, 'CEILING', 0, 'Ceiling', 1, 1), # n ceil(n)
37 (0x25, 'CINDEX', 0, 'CopyXToTopStack', 1, 1), # k ek
38 (0x22, 'CLEAR', 0, 'ClearStack', -1, 0), # all items on the stack -
39 (0x4f, 'DEBUG', 0, 'DebugCall', 1, 0), # n -
40 (0x73, 'DELTAC1', 0, 'DeltaExceptionC1', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
41 (0x74, 'DELTAC2', 0, 'DeltaExceptionC2', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
42 (0x75, 'DELTAC3', 0, 'DeltaExceptionC3', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
43 (0x5d, 'DELTAP1', 0, 'DeltaExceptionP1', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
44 (0x71, 'DELTAP2', 0, 'DeltaExceptionP2', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
45 (0x72, 'DELTAP3', 0, 'DeltaExceptionP3', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
46 (0x24, 'DEPTH', 0, 'GetDepthStack', 0, 1), # - n
47 (0x62, 'DIV', 0, 'Divide', 2, 1), # n2, n1 (n1 * 64)/ n2
48 (0x20, 'DUP', 0, 'DuplicateTopStack', 1, 2), # e e, e
49 (0x59, 'EIF', 0, 'EndIf', 0, 0), # - -
50 (0x1b, 'ELSE', 0, 'Else', 0, 0), # - -
51 (0x2d, 'ENDF', 0, 'EndFunctionDefinition', 0, 0), # - -
52 (0x54, 'EQ', 0, 'Equal', 2, 1), # e2, e1 b
53 (0x57, 'EVEN', 0, 'Even', 1, 1), # e b
54 (0x2c, 'FDEF', 0, 'FunctionDefinition', 1, 0), # f -
55 (0x4e, 'FLIPOFF', 0, 'SetAutoFlipOff', 0, 0), # - -
56 (0x4d, 'FLIPON', 0, 'SetAutoFlipOn', 0, 0), # - -
57 (0x80, 'FLIPPT', 0, 'FlipPoint', -1, 0), # p1, p2, ..., ploopvalue -
58 (0x82, 'FLIPRGOFF', 0, 'FlipRangeOff', 2, 0), # h, l -
59 (0x81, 'FLIPRGON', 0, 'FlipRangeOn', 2, 0), # h, l -
60 (0x66, 'FLOOR', 0, 'Floor', 1, 1), # n floor(n)
61 (0x46, 'GC', 1, 'GetCoordOnPVector', 1, 1), # p c
62 (0x88, 'GETINFO', 0, 'GetInfo', 1, 1), # selector result
63 (0x0d, 'GFV', 0, 'GetFVector', 0, 2), # - px, py
64 (0x0c, 'GPV', 0, 'GetPVector', 0, 2), # - px, py
65 (0x52, 'GT', 0, 'GreaterThan', 2, 1), # e2, e1 b
66 (0x53, 'GTEQ', 0, 'GreaterThanOrEqual', 2, 1), # e2, e1 b
67 (0x89, 'IDEF', 0, 'InstructionDefinition', 1, 0), # f -
68 (0x58, 'IF', 0, 'If', 1, 0), # e -
69 (0x8e, 'INSTCTRL', 0, 'SetInstrExecControl', 2, 0), # s, v -
70 (0x39, 'IP', 0, 'InterpolatePts', -1, 0), # p1, p2, ... , ploopvalue -
71 (0x0f, 'ISECT', 0, 'MovePtToIntersect', 5, 0), # a1, a0, b1, b0, p -
72 (0x30, 'IUP', 1, 'InterpolateUntPts', 0, 0), # - -
73 (0x1c, 'JMPR', 0, 'Jump', 1, 0), # offset -
74 (0x79, 'JROF', 0, 'JumpRelativeOnFalse', 2, 0), # e, offset -
75 (0x78, 'JROT', 0, 'JumpRelativeOnTrue', 2, 0), # e, offset -
76 (0x2a, 'LOOPCALL', 0, 'LoopAndCallFunction', 2, 0), # f, count -
77 (0x50, 'LT', 0, 'LessThan', 2, 1), # e2, e1 b
78 (0x51, 'LTEQ', 0, 'LessThenOrEqual', 2, 1), # e2, e1 b
79 (0x8b, 'MAX', 0, 'Maximum', 2, 1), # e2, e1 max(e1, e2)
80 (0x49, 'MD', 1, 'MeasureDistance', 2, 1), # p2,p1 d
81 (0x2e, 'MDAP', 1, 'MoveDirectAbsPt', 1, 0), # p -
82 (0xc0, 'MDRP', 5, 'MoveDirectRelPt', 1, 0), # p -
83 (0x3e, 'MIAP', 1, 'MoveIndirectAbsPt', 2, 0), # n, p -
84 (0x8c, 'MIN', 0, 'Minimum', 2, 1), # e2, e1 min(e1, e2)
jvrae43d922002-01-07 08:44:09 +000085 (0x26, 'MINDEX', 0, 'MoveXToTopStack', 1, 1), # k ek
86 (0xe0, 'MIRP', 5, 'MoveIndirectRelPt', 2, 0), # n, p -
Just7842e561999-12-16 21:34:53 +000087 (0x4b, 'MPPEM', 0, 'MeasurePixelPerEm', 0, 1), # - ppem
88 (0x4c, 'MPS', 0, 'MeasurePointSize', 0, 1), # - pointSize
89 (0x3a, 'MSIRP', 1, 'MoveStackIndirRelPt', 2, 0), # d, p -
90 (0x63, 'MUL', 0, 'Multiply', 2, 1), # n2, n1 (n1 * n2)/64
91 (0x65, 'NEG', 0, 'Negate', 1, 1), # n -n
92 (0x55, 'NEQ', 0, 'NotEqual', 2, 1), # e2, e1 b
93 (0x5c, 'NOT', 0, 'LogicalNot', 1, 1), # e ( not e )
94 (0x6c, 'NROUND', 2, 'NoRound', 1, 1), # n1 n2
95 (0x56, 'ODD', 0, 'Odd', 1, 1), # e b
96 (0x5b, 'OR', 0, 'LogicalOr', 2, 1), # e2, e1 b
97 (0x21, 'POP', 0, 'PopTopStack', 1, 0), # e -
98 (0x45, 'RCVT', 0, 'ReadCVT', 1, 1), # location value
99 (0x7d, 'RDTG', 0, 'RoundDownToGrid', 0, 0), # - -
100 (0x7a, 'ROFF', 0, 'RoundOff', 0, 0), # - -
101 (0x8a, 'ROLL', 0, 'RollTopThreeStack', 3, 3), # a,b,c b,a,c
102 (0x68, 'ROUND', 2, 'Round', 1, 1), # n1 n2
103 (0x43, 'RS', 0, 'ReadStore', 1, 1), # n v
104 (0x3d, 'RTDG', 0, 'RoundToDoubleGrid', 0, 0), # - -
105 (0x18, 'RTG', 0, 'RoundToGrid', 0, 0), # - -
106 (0x19, 'RTHG', 0, 'RoundToHalfGrid', 0, 0), # - -
107 (0x7c, 'RUTG', 0, 'RoundUpToGrid', 0, 0), # - -
108 (0x77, 'S45ROUND', 0, 'SuperRound45Degrees', 1, 0), # n -
109 (0x7e, 'SANGW', 0, 'SetAngleWeight', 1, 0), # weight -
110 (0x85, 'SCANCTRL', 0, 'ScanConversionControl', 1, 0), # n -
111 (0x8d, 'SCANTYPE', 0, 'ScanType', 1, 0), # n -
112 (0x48, 'SCFS', 0, 'SetCoordFromStackFP', 2, 0), # c, p -
113 (0x1d, 'SCVTCI', 0, 'SetCVTCutIn', 1, 0), # n -
114 (0x5e, 'SDB', 0, 'SetDeltaBaseInGState', 1, 0), # n -
115 (0x86, 'SDPVTL', 1, 'SetDualPVectorToLine', 2, 0), # p2, p1 -
116 (0x5f, 'SDS', 0, 'SetDeltaShiftInGState', 1, 0), # n -
117 (0x0b, 'SFVFS', 0, 'SetFVectorFromStack', 2, 0), # y, x -
118 (0x04, 'SFVTCA', 1, 'SetFVectorToAxis', 0, 0), # - -
119 (0x08, 'SFVTL', 1, 'SetFVectorToLine', 2, 0), # p2, p1 -
120 (0x0e, 'SFVTPV', 0, 'SetFVectorToPVector', 0, 0), # - -
121 (0x34, 'SHC', 1, 'ShiftContourByLastPt', 1, 0), # c -
122 (0x32, 'SHP', 1, 'ShiftPointByLastPoint', -1, 0), # p1, p2, ..., ploopvalue -
123 (0x38, 'SHPIX', 0, 'ShiftZoneByPixel', -1, 0), # d, p1, p2, ..., ploopvalue -
124 (0x36, 'SHZ', 1, 'ShiftZoneByLastPoint', 1, 0), # e -
125 (0x17, 'SLOOP', 0, 'SetLoopVariable', 1, 0), # n -
126 (0x1a, 'SMD', 0, 'SetMinimumDistance', 1, 0), # distance -
127 (0x0a, 'SPVFS', 0, 'SetPVectorFromStack', 2, 0), # y, x -
128 (0x02, 'SPVTCA', 1, 'SetPVectorToAxis', 0, 0), # - -
129 (0x06, 'SPVTL', 1, 'SetPVectorToLine', 2, 0), # p2, p1 -
130 (0x76, 'SROUND', 0, 'SuperRound', 1, 0), # n -
131 (0x10, 'SRP0', 0, 'SetRefPoint0', 1, 0), # p -
132 (0x11, 'SRP1', 0, 'SetRefPoint1', 1, 0), # p -
133 (0x12, 'SRP2', 0, 'SetRefPoint2', 1, 0), # p -
134 (0x1f, 'SSW', 0, 'SetSingleWidth', 1, 0), # n -
135 (0x1e, 'SSWCI', 0, 'SetSingleWidthCutIn', 1, 0), # n -
136 (0x61, 'SUB', 0, 'Subtract', 2, 1), # n2, n1 (n1 - n2)
137 (0x00, 'SVTCA', 1, 'SetFPVectorToAxis', 0, 0), # - -
138 (0x23, 'SWAP', 0, 'SwapTopStack', 2, 2), # e2, e1 e1, e2
139 (0x13, 'SZP0', 0, 'SetZonePointer0', 1, 0), # n -
140 (0x14, 'SZP1', 0, 'SetZonePointer1', 1, 0), # n -
141 (0x15, 'SZP2', 0, 'SetZonePointer2', 1, 0), # n -
142 (0x16, 'SZPS', 0, 'SetZonePointerS', 1, 0), # n -
143 (0x29, 'UTP', 0, 'UnTouchPt', 1, 0), # p -
144 (0x70, 'WCVTF', 0, 'WriteCVTInFUnits', 2, 0), # n, l -
145 (0x44, 'WCVTP', 0, 'WriteCVTInPixels', 2, 0), # v, l -
146 (0x42, 'WS', 0, 'WriteStore', 2, 0), # v, l -
147# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
148]
149
150
151def bitRepr(value, bits):
152 s = ""
153 for i in range(bits):
154 s = "01"[value & 0x1] + s
155 value = value >> 1
156 return s
157
Just51e75db2000-02-01 15:30:15 +0000158
159_mnemonicPat = re.compile("[A-Z][A-Z0-9]*$")
160
161def _makeDict(instructionList):
Just7842e561999-12-16 21:34:53 +0000162 opcodeDict = {}
Just51e75db2000-02-01 15:30:15 +0000163 mnemonicDict = {}
164 for op, mnemonic, argBits, name, pops, pushes in instructionList:
165 assert _mnemonicPat.match(mnemonic)
Behdad Esfahbod4fab60c2013-11-14 20:13:49 -0500166 mnemonicDict[mnemonic] = op, argBits
Just51e75db2000-02-01 15:30:15 +0000167 if argBits:
Just7842e561999-12-16 21:34:53 +0000168 argoffset = op
Just51e75db2000-02-01 15:30:15 +0000169 for i in range(1 << argBits):
Behdad Esfahbod4fab60c2013-11-14 20:13:49 -0500170 opcodeDict[op+i] = mnemonic, argBits, argoffset
Just7842e561999-12-16 21:34:53 +0000171 else:
Behdad Esfahbod4fab60c2013-11-14 20:13:49 -0500172 opcodeDict[op] = mnemonic, 0, 0
Just51e75db2000-02-01 15:30:15 +0000173 return opcodeDict, mnemonicDict
Just7842e561999-12-16 21:34:53 +0000174
Just51e75db2000-02-01 15:30:15 +0000175streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
176opcodeDict, mnemonicDict = _makeDict(instructions)
Just7842e561999-12-16 21:34:53 +0000177
pabs386368e72009-11-08 06:39:37 +0000178class tt_instructions_error(Exception):
179 def __init__(self, error):
180 self.error = error
181 def __str__(self):
182 return "TT instructions error: %s" % repr(self.error)
Just7842e561999-12-16 21:34:53 +0000183
184
Just51e75db2000-02-01 15:30:15 +0000185_comment = r"/\*.*?\*/"
186_instruction = r"([A-Z][A-Z0-9]*)\s*\[(.*?)\]"
187_number = r"-?[0-9]+"
188_token = "(%s)|(%s)|(%s)" % (_instruction, _number, _comment)
189
190_tokenRE = re.compile(_token)
191_whiteRE = re.compile(r"\s*")
192
Justb263e4d2000-02-01 15:54:37 +0000193_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]*).*?\*/")
194
195
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500196def _skipWhite(data, pos):
Just51e75db2000-02-01 15:30:15 +0000197 m = _whiteRE.match(data, pos)
198 newPos = m.regs[0][1]
199 assert newPos >= pos
200 return newPos
201
202
Behdad Esfahbode388db52013-11-28 14:26:58 -0500203class Program(object):
Just7842e561999-12-16 21:34:53 +0000204
205 def __init__(self):
206 pass
207
208 def fromBytecode(self, bytecode):
Just51e75db2000-02-01 15:30:15 +0000209 self.bytecode = array.array("B", bytecode)
210 if hasattr(self, "assembly"):
211 del self.assembly
Just7842e561999-12-16 21:34:53 +0000212
213 def fromAssembly(self, assembly):
214 self.assembly = assembly
Just51e75db2000-02-01 15:30:15 +0000215 if hasattr(self, "bytecode"):
216 del self.bytecode
Just7842e561999-12-16 21:34:53 +0000217
218 def getBytecode(self):
219 if not hasattr(self, "bytecode"):
220 self._assemble()
221 return self.bytecode.tostring()
222
223 def getAssembly(self):
224 if not hasattr(self, "assembly"):
225 self._disassemble()
226 return self.assembly
227
Just51e75db2000-02-01 15:30:15 +0000228 def toXML(self, writer, ttFont):
Behdad Esfahbod6d658cf2013-12-07 16:06:10 -0500229 if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions:
Just51e75db2000-02-01 15:30:15 +0000230 assembly = self.getAssembly()
231 writer.begintag("assembly")
232 writer.newline()
233 i = 0
234 nInstr = len(assembly)
235 while i < nInstr:
236 instr = assembly[i]
237 writer.write(instr)
238 writer.newline()
239 m = _pushCountPat.match(instr)
240 i = i + 1
241 if m:
242 nValues = int(m.group(1))
243 line = []
244 j = 0
245 for j in range(nValues):
246 if j and not (j % 25):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500247 writer.write(' '.join(line))
Just51e75db2000-02-01 15:30:15 +0000248 writer.newline()
249 line = []
250 line.append(assembly[i+j])
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500251 writer.write(' '.join(line))
Just51e75db2000-02-01 15:30:15 +0000252 writer.newline()
253 i = i + j + 1
254 writer.endtag("assembly")
255 else:
256 writer.begintag("bytecode")
257 writer.newline()
258 writer.dumphex(self.getBytecode())
259 writer.endtag("bytecode")
260
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500261 def fromXML(self, name, attrs, content, ttFont):
Just51e75db2000-02-01 15:30:15 +0000262 if name == "assembly":
Behdad Esfahbod18316aa2013-11-27 21:17:35 -0500263 self.fromAssembly(strjoin(content))
Just51e75db2000-02-01 15:30:15 +0000264 self._assemble()
265 del self.assembly
266 else:
267 assert name == "bytecode"
268 self.fromBytecode(readHex(content))
269
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500270 def _assemble(self):
Just51e75db2000-02-01 15:30:15 +0000271 assembly = self.assembly
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500272 if isinstance(assembly, type([])):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500273 assembly = ' '.join(assembly)
Just51e75db2000-02-01 15:30:15 +0000274 bytecode = []
275 push = bytecode.append
276 lenAssembly = len(assembly)
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500277 pos = _skipWhite(assembly, 0)
Just51e75db2000-02-01 15:30:15 +0000278 while pos < lenAssembly:
279 m = _tokenRE.match(assembly, pos)
280 if m is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500281 raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos-5:pos+15])
Just51e75db2000-02-01 15:30:15 +0000282 dummy, mnemonic, arg, number, comment = m.groups()
283 pos = m.regs[0][1]
284 if comment:
285 continue
286
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500287 arg = arg.strip()
Behdad Esfahbod67fef702013-11-14 20:24:16 -0500288 if mnemonic.startswith("INSTR"):
289 # Unknown instruction
290 op = int(mnemonic[5:])
291 push(op)
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500292 elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
Behdad Esfahbod4fab60c2013-11-14 20:13:49 -0500293 op, argBits = mnemonicDict[mnemonic]
Behdad Esfahbod180ace62013-11-27 02:40:30 -0500294 if len(arg) != argBits:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500295 raise tt_instructions_error("Incorrect number of argument bits (%s[%s])" % (mnemonic, arg))
Just51e75db2000-02-01 15:30:15 +0000296 if arg:
297 arg = binary2num(arg)
298 push(op + arg)
299 else:
300 push(op)
301 else:
302 args = []
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500303 pos = _skipWhite(assembly, pos)
Just51e75db2000-02-01 15:30:15 +0000304 while pos < lenAssembly:
Just51e75db2000-02-01 15:30:15 +0000305 m = _tokenRE.match(assembly, pos)
306 if m is None:
Behdad Esfahbodcd5aad92013-11-27 02:42:28 -0500307 raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos:pos+15])
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500308 dummy, _mnemonic, arg, number, comment = m.groups()
Just51e75db2000-02-01 15:30:15 +0000309 if number is None and comment is None:
310 break
311 pos = m.regs[0][1]
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500312 pos = _skipWhite(assembly, pos)
Just51e75db2000-02-01 15:30:15 +0000313 if comment is not None:
314 continue
315 args.append(int(number))
Just51e75db2000-02-01 15:30:15 +0000316 nArgs = len(args)
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500317 if mnemonic == "PUSH":
Behdad Esfahbodfcc56e62013-12-17 05:06:37 -0500318 # Automatically choose the most compact representation
319 nWords = 0
320 while nArgs:
321 while nWords < nArgs and nWords < 255 and not (0 <= args[nWords] <= 255):
322 nWords += 1
323 nBytes = 0
324 while nWords+nBytes < nArgs and nBytes < 255 and 0 <= args[nWords+nBytes] <= 255:
325 nBytes += 1
Behdad Esfahbod42d305a2013-12-17 05:10:30 -0500326 if nBytes < 2 and nWords + nBytes < 255 and nWords + nBytes != nArgs:
Behdad Esfahbodfcc56e62013-12-17 05:06:37 -0500327 # Will write bytes as words
328 nWords += nBytes
329 continue
330
331 # Write words
332 if nWords:
333 if nWords <= 8:
334 op, argBits = streamMnemonicDict["PUSHW"]
335 op = op + nWords - 1
336 push(op)
337 else:
338 op, argBits = streamMnemonicDict["NPUSHW"]
339 push(op)
340 push(nWords)
341 for value in args[:nWords]:
342 assert -32768 <= value < 32768, "PUSH value out of range %d" % value
343 push((value >> 8) & 0xff)
344 push(value & 0xff)
345
346 # Write bytes
347 if nBytes:
348 pass
349 if nBytes <= 8:
350 op, argBits = streamMnemonicDict["PUSHB"]
351 op = op + nBytes - 1
352 push(op)
353 else:
354 op, argBits = streamMnemonicDict["NPUSHB"]
355 push(op)
356 push(nBytes)
357 for value in args[nWords:nWords+nBytes]:
358 push(value)
359
360 nTotal = nWords + nBytes
361 args = args[nTotal:]
362 nArgs -= nTotal
363 nWords = 0
364 else:
365 # Write exactly what we've been asked to
366 words = mnemonic[-1] == "W"
367 op, argBits = streamMnemonicDict[mnemonic]
368 if mnemonic[0] != "N":
369 assert nArgs <= 8, nArgs
370 op = op + nArgs - 1
371 push(op)
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500372 else:
Behdad Esfahbodfcc56e62013-12-17 05:06:37 -0500373 assert nArgs < 256
374 push(op)
375 push(nArgs)
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500376 if words:
Behdad Esfahbodfcc56e62013-12-17 05:06:37 -0500377 for value in args:
Behdad Esfahbod3714c782013-12-17 03:47:44 -0500378 assert -32768 <= value < 32768, "PUSHW value out of range %d" % value
379 push((value >> 8) & 0xff)
380 push(value & 0xff)
381 else:
382 for value in args:
383 assert 0 <= value < 256, "PUSHB value out of range %d" % value
384 push(value)
Behdad Esfahbodfcc56e62013-12-17 05:06:37 -0500385
Behdad Esfahbod153ec402013-12-04 01:15:46 -0500386 pos = _skipWhite(assembly, pos)
Just51e75db2000-02-01 15:30:15 +0000387
388 if bytecode:
389 assert max(bytecode) < 256 and min(bytecode) >= 0
390 self.bytecode = array.array("B", bytecode)
Just7842e561999-12-16 21:34:53 +0000391
Behdad Esfahboda36c74e2014-01-19 17:30:19 +0800392 def _disassemble(self, preserve=False):
Just7842e561999-12-16 21:34:53 +0000393 assembly = []
394 i = 0
395 bytecode = self.bytecode
396 numBytecode = len(bytecode)
397 while i < numBytecode:
398 op = bytecode[i]
Just7842e561999-12-16 21:34:53 +0000399 try:
Behdad Esfahbod4fab60c2013-11-14 20:13:49 -0500400 mnemonic, argBits, argoffset = opcodeDict[op]
Just7842e561999-12-16 21:34:53 +0000401 except KeyError:
Behdad Esfahbod4ff77122013-11-14 20:16:41 -0500402 if op in streamOpcodeDict:
Behdad Esfahbod965c87f2013-12-17 04:19:14 -0500403 values = []
404
405 # Merge consecutive PUSH operations
406 while bytecode[i] in streamOpcodeDict:
407 op = bytecode[i]
408 mnemonic, argBits, argoffset = streamOpcodeDict[op]
409 words = mnemonic[-1] == "W"
410 if argBits:
411 nValues = op - argoffset + 1
412 else:
Behdad Esfahbodce84d322013-12-17 04:02:10 -0500413 i = i + 1
Behdad Esfahbod965c87f2013-12-17 04:19:14 -0500414 nValues = bytecode[i]
415 i = i + 1
416 assert nValues > 0
417 if not words:
418 for j in range(nValues):
419 value = bytecode[i]
420 values.append(repr(value))
421 i = i + 1
422 else:
423 for j in range(nValues):
424 # cast to signed int16
425 value = (bytecode[i] << 8) | bytecode[i+1]
426 if value >= 0x8000:
427 value = value - 0x10000
428 values.append(repr(value))
429 i = i + 2
Behdad Esfahboda36c74e2014-01-19 17:30:19 +0800430 if preserve:
431 break
Behdad Esfahbod965c87f2013-12-17 04:19:14 -0500432
Behdad Esfahboda36c74e2014-01-19 17:30:19 +0800433 if not preserve:
434 mnemonic = "PUSH"
Behdad Esfahbod965c87f2013-12-17 04:19:14 -0500435 nValues = len(values)
436 if nValues == 1:
Behdad Esfahboda36c74e2014-01-19 17:30:19 +0800437 assembly.append("%s[ ]" % mnemonic)
Behdad Esfahbodce84d322013-12-17 04:02:10 -0500438 else:
Behdad Esfahboda36c74e2014-01-19 17:30:19 +0800439 assembly.append("%s[ ] /* %s values pushed */" % (mnemonic, nValues))
Behdad Esfahbod965c87f2013-12-17 04:19:14 -0500440 assembly.extend(values)
Behdad Esfahbod4ff77122013-11-14 20:16:41 -0500441 else:
Behdad Esfahbod67fef702013-11-14 20:24:16 -0500442 assembly.append("INSTR%d[ ]" % op)
443 i = i + 1
Just7842e561999-12-16 21:34:53 +0000444 else:
Just51e75db2000-02-01 15:30:15 +0000445 if argBits:
446 assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
Just7842e561999-12-16 21:34:53 +0000447 else:
448 assembly.append(mnemonic + "[ ]")
449 i = i + 1
450 self.assembly = assembly
Just7842e561999-12-16 21:34:53 +0000451
452
Just51e75db2000-02-01 15:30:15 +0000453if __name__ == "__main__":
454 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/-"""
455
456 p = Program()
457 p.fromBytecode(bc)
jvrd67cf252008-09-16 14:14:44 +0000458 asm = p.getAssembly()
459 p.fromAssembly(asm)
Behdad Esfahbod3ec6a252013-11-27 04:57:33 -0500460 print(bc == p.getBytecode())
Just7842e561999-12-16 21:34:53 +0000461