@@ -12,14 +12,37 @@ class InstructionStream(object):
1212 The instruction stream.
1313 """
1414
15- def __init__ (self , instruction_processor = None , program_bytes = b"" ):
15+ def __init__ (self , instruction_processor = None , program_bytes = b"" ) -> None :
16+ self .ip = instruction_processor
1617 self .io = BytesIO (program_bytes )
18+ self ._num_bytes = len (program_bytes )
1719
18- def rewind (self ):
20+ def __len__ (self ):
21+ return self ._num_bytes
22+
23+ def __repr__ (self ) -> str :
1924 """
20- Rewind the instruction pointer to the beginning of the stream.
25+ Return the instructions from the bytecode in the current stream
26+ starting at the beginning.
2127 """
22- self .io .seek (0 )
28+ return self .get_assembly ()
29+
30+ def __str__ (self ) -> str :
31+ """
32+ Return the instructions from the bytecode in the current stream
33+ starting at the beginning.
34+ """
35+ return self .get_assembly ()
36+
37+ def move_instruction_pointer (self , bytes_offset : int ) -> None :
38+ """
39+ :param bytes_offset: The offset in bytes. May be positive or negative.
40+ :type bytes_offset: int
41+
42+ Move the instruction pointer inside the current stream, relative to the
43+ current pointer position.
44+ """
45+ self .io .seek (bytes_offset , 1 ) # 1 = relative to current position
2346
2447 def read_byte (self ):
2548 """
@@ -41,11 +64,29 @@ def read_word(self):
4164 return False
4265 return w , int .from_bytes (w , byteorder = "big" , signed = True )
4366
44- def __repr__ (self ):
67+ def rewind (self ) -> None :
4568 """
46- Print the instructions from the bytecode in the current stream starting
47- at the beginning.
69+ Rewind the instruction pointer to the beginning of the stream.
4870 """
71+ self .io .seek (0 )
72+
73+ # Getting the assembly code
74+
75+ @property
76+ def vtt_assembly (self ) -> str :
77+ """
78+ Return the instructions from the bytecode in the current stream as VTT
79+ assembly code.
80+ """
81+ return self .get_assembly (dialect = "vtt" , end = "\n " )
82+
83+ def get_assembly (self , dialect = "ttx" , end = "\n " ) -> str :
84+ """
85+ Return the instructions from the bytecode in the current stream as
86+ assembly code in the specified dialect, "ttx" or "vtt".
87+ """
88+ vtt = dialect == "vtt"
89+ ttx = dialect == "ttx"
4990 self .rewind ()
5091
5192 asm = ""
@@ -54,7 +95,16 @@ def __repr__(self):
5495 while True :
5596 opcode = self .io .read (1 )
5697 if not opcode :
57- return asm .strip ()
98+ asm = asm .strip ()
99+ if ttx :
100+ return asm
101+ elif vtt :
102+ if asm :
103+ return f"#PUSHOFF{ end } " + asm .strip () + f"{ end } #PUSHON"
104+ return ""
105+ else :
106+ # Unknown dialect
107+ raise NotImplementedError
58108
59109 opcode = int .from_bytes (opcode , byteorder = "big" , signed = False )
60110 cmd_info = streamOpcodeDict .get (opcode , None )
@@ -92,19 +142,145 @@ def __repr__(self):
92142 arg_bits = 0 # Don't output bits for push instructions
93143
94144 if arg_bits == 0 :
95- arg_bitstring = " "
145+ if ttx :
146+ arg_bitstring = " "
147+ else :
148+ arg_bitstring = ""
96149 else :
97- arg_bitstring = num2binary (opcode - base_opcode , arg_bits )
150+ if ttx :
151+ arg_bitstring = num2binary (opcode - base_opcode , arg_bits )
152+ elif vtt :
153+ arg_bitstring = self .bitstring_to_mnemonic (
154+ cmd_name , num2binary (opcode - base_opcode , arg_bits )
155+ )
156+ else :
157+ # Unknown dialect
158+ raise NotImplementedError
98159
99- if cmd_name in ("NPUSHB" , "NPUSHW" , "PUSHB" , "PUSHW" ):
100- num_args = len (args )
101- val = "value" if num_args == 1 else "values"
102- asm += f"\n { ' ' * indent } { cmd_name } [{ arg_bitstring } ]\t /* { num_args } { val } pushed */"
103- else :
104- asm += f"\n { ' ' * indent } { cmd_name } [{ arg_bitstring } ]\t /* { name } */"
160+ if ttx :
161+ if cmd_name in ("NPUSHB" , "NPUSHW" , "PUSHB" , "PUSHW" ):
162+ num_args = len (args )
163+ val = "value" if num_args == 1 else "values"
164+ asm += (
165+ f"\n { ' ' * indent } { cmd_name } [{ arg_bitstring } ]"
166+ f"\t /* { num_args } { val } pushed */"
167+ )
168+ else :
169+ asm += (
170+ f"\n { ' ' * indent } { cmd_name } [{ arg_bitstring } ]"
171+ f"\t /* { name } */"
172+ )
105173
106- if args :
107- asm += f"\n { ' ' * indent } { ' ' .join (args )} "
174+ if args :
175+ asm += f"\n { ' ' * indent } { ' ' .join (args )} "
176+
177+ elif vtt :
178+ if cmd_name in ("NPUSHB" , "NPUSHW" , "PUSHB" , "PUSHW" ):
179+ # Format as generic #PUSH for VTT assembly output
180+ cmd_name = "#PUSH"
181+ asm += f"{ end } { ' ' * indent } { cmd_name } , { ', ' .join (args )} "
182+ elif cmd_name in ("JMPR" , "JROF" ):
183+ # Special formatting for jump instructions
184+ if cmd_name == "JPMR" :
185+ args = ("*" ,)
186+ elif cmd_name == "JROF" :
187+ args = ("*" , "*" )
188+ asm += f"{ end } #PUSHON"
189+ asm += f"{ end } { ' ' * indent } { cmd_name } , { ', ' .join (args )} "
190+ asm += f"{ end } #PUSHOFF"
191+ else :
192+ asm += (
193+ f"{ end } { ' ' * indent } { cmd_name } [{ arg_bitstring } ]"
194+ f"\t /* { name } */"
195+ )
196+
197+ else :
198+ # Unknown dialect
199+ raise NotImplementedError
108200
109201 if cmd_name in ("ELSE" , "FDEF" , "IF" ):
110202 indent += 1
203+
204+ def bitstring_to_mnemonic (self , cmd_name : str , bitstring : str ) -> str :
205+ """
206+ Return VTT mnemonics for a bit string
207+ """
208+ if cmd_name in ("SVTCA" , "SPVTCA" , "SFVTCA" , "IUP" ):
209+ # Direction
210+ if bitstring == "0" :
211+ return "Y" # Y axis
212+ return "X" # X axis
213+
214+ elif cmd_name in ("SPVTL" , "SFVTL" , "SDPVTL" ):
215+ # Line relation
216+ if bitstring == "0" :
217+ return "r" # parallel to line
218+ return "R" # perpendicular to line
219+
220+ elif cmd_name in ("MDAP" , "MIAP" ):
221+ # Rounding
222+ if bitstring == "0" :
223+ return "r" # do not round distance
224+ return "R" # round distance
225+
226+ elif cmd_name in ("SHP" , "SHC" , "SHZ" ):
227+ # Reference Point Usage
228+ if bitstring == "0" :
229+ return "2" # Use rp2
230+ return "1" # Use rp1
231+
232+ elif cmd_name in ("MSIRP" ,):
233+ # Reference Point Autoset
234+ if bitstring == "0" :
235+ return "m" # Do not set rp0
236+ return "M" # Set rp0 to point number on the stack
237+
238+ elif cmd_name in ("GC" , "MD" ):
239+ # Outline
240+ if bitstring == "0" :
241+ return "N" # Use gridfitted outline
242+ return "O" # Use original outline
243+
244+ elif cmd_name in ("ROUND" , "NROUND" ):
245+ # Color
246+ return self .bitstring_to_color_mnemonic (bitstring )
247+
248+ elif cmd_name in ("MDRP" , "MIRP" ):
249+ flags = ""
250+
251+ # Reference Point Autoset
252+ if bitstring [0 ] == "0" :
253+ flags += "m"
254+ else :
255+ flags += "M"
256+
257+ # Minimum Distance
258+ if bitstring [1 ] == "0" :
259+ flags += "<"
260+ else :
261+ flags += ">"
262+
263+ # Rounding
264+ if bitstring [2 ] == "0" :
265+ flags += "r" # do not round distance
266+ else :
267+ flags += "R" # round distance
268+
269+ # Color
270+ return flags + self .bitstring_to_color_mnemonic (bitstring [3 :])
271+
272+ # Unknown command
273+ raise KeyError
274+
275+ def bitstring_to_color_mnemonic (self , bitstring : str ) -> str :
276+ """
277+ Return VTT distance color mnemonics for a bit string
278+ """
279+ if bitstring == "00" :
280+ return "Gr" # Gray
281+ elif bitstring == "01" :
282+ return "Bl" # Black
283+ elif bitstring == "10" :
284+ return "Wh" # White
285+ # "11" is not defined
286+ raise KeyError
0 commit comments