add option to run without a disassembly listing to ISACaller
[openpower-isa.git] / src / openpower / simulator / program.py
1 # License: LGPLv3+
2 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
3 # Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4
5 """POWER Program
6
7 takes powerpc assembly instructions and turns them into LE/BE binary
8 data. calls powerpc64-linux-gnu-as, ld and objcopy to do so.
9 """
10
11 import tempfile
12 import subprocess
13 import struct
14 import os
15 import sys
16 from io import BytesIO
17
18 from openpower.simulator.envcmds import cmds
19
20 filedir = os.path.dirname(os.path.realpath(__file__))
21 memmap = os.path.join(filedir, "memmap")
22
23
24 class Program:
25 def __init__(self, instructions, bigendian):
26 self.bigendian = bigendian
27 if self.bigendian:
28 self.endian_fmt = "elf64-big"
29 self.obj_fmt = "-be"
30 self.ld_fmt = "-EB"
31 else:
32 self.ld_fmt = "-EL"
33 self.endian_fmt = "elf64-little"
34 self.obj_fmt = "-le"
35
36 if isinstance(instructions, bytes): # actual bytes
37 self.binfile = BytesIO(instructions)
38 self.binfile.name = "assembly"
39 self.assembly = '' # noo disassemble number fiiive
40 print("binary", self.binfile)
41 elif isinstance(instructions, str): # filename
42 # read instructions into a BytesIO to avoid "too many open files"
43 with open(instructions, "rb") as f:
44 b = f.read()
45 self.binfile = BytesIO(b)
46 self.assembly = '' # noo disassemble number fiiive
47 print("program", self.binfile)
48 else:
49 if isinstance(instructions, list):
50 instructions = '\n'.join(instructions)
51 self.assembly = instructions + '\n' # plus final newline
52 self._assemble()
53 self._instructions = list(self._get_instructions())
54
55 def __enter__(self):
56 return self
57
58 def __exit__(self, type, value, traceback):
59 self.close()
60
61 def _get_binary(self, elffile):
62 self.binfile = tempfile.NamedTemporaryFile(suffix=".bin")
63 args = [cmds['objcopy'],
64 "-O", "binary",
65 "-I", self.endian_fmt,
66 elffile.name,
67 self.binfile.name]
68 subprocess.check_output(args)
69
70 def _link(self, ofile):
71 with tempfile.NamedTemporaryFile(suffix=".elf") as elffile:
72 args = [cmds['ld'],
73 self.ld_fmt,
74 "-o", elffile.name,
75 "-T", memmap,
76 ofile.name]
77 subprocess.check_output(args)
78 self._get_binary(elffile)
79
80 def _assemble(self):
81 with tempfile.NamedTemporaryFile(suffix=".o") as outfile:
82 args = [cmds['as'],
83 '-mpower9',
84 '-mregnames',
85 self.obj_fmt,
86 "-o",
87 outfile.name]
88 p = subprocess.Popen(args, stdin=subprocess.PIPE)
89 p.communicate(self.assembly.encode('utf-8'))
90 if p.wait() != 0:
91 print("Error in program:")
92 print(self.assembly)
93 sys.exit(1)
94 self._link(outfile)
95
96 def _get_instructions(self):
97 while True:
98 data = self.binfile.read(4)
99 if not data:
100 break
101 yield struct.unpack('<I', data)[0] # unsigned int
102
103 def generate_instructions(self):
104 yield from self._instructions
105
106 def reset(self):
107 self.binfile.seek(0)
108
109 def size(self):
110 curpos = self.binfile.tell()
111 self.binfile.seek(0, 2) # Seek to end of file
112 size = self.binfile.tell()
113 self.binfile.seek(curpos, 0)
114 return size
115
116 def write_bin(self, fname):
117 self.reset()
118 data = self.binfile.read()
119 with open(fname, "wb") as f:
120 f.write(data)
121
122 def close(self):
123 self.binfile.close()
124
125 if __name__ == '__main__':
126 lst = ['addi 5, 0, 4660/2',
127 'mtcrf 255, 5+3',
128 'mfocrf 2, 1',
129 'addi r2, 3, 1',
130 'attn',
131 ]
132 lst = ["addi 9, 0, 0x10", # i = 16
133 "addi 9,9,-1", # i = i - 1
134 "cmpi 2,1,9,12", # compare 9 to value 12, store in CR2
135 "bc 4,10,-8", # branch if CR2 "test was != 12"
136 'attn',
137 ]
138
139 with Program(lst, False) as p:
140 for instruction in p.generate_instructions():
141 print (hex(instruction))
142 p.write_bin("/tmp/test.bin")