forked from kuis-isle3hw/simple_assembler
- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassembler.py
More file actions
Latest commit
223 lines (206 loc) · 8.81 KB
/
Copy pathassembler.py
File metadata and controls
223 lines (206 loc) · 8.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
importsys
importre
importargparse
defread_data(infile):
"""
コマンドライン引数の一番目で指定されたファイルから読み取り、一行ずつリストにして返す。
コマンドライン引数が指定されなかった場合は、標準入力を読み取る。
"""
try:
withopen(infile, errors="ignore") asf:
data= [line.strip() forlineinf.readlines()]
returndata
exceptExceptionase:
ifinfile:
print(e)
data= []
print("命令列を入力してください")
whileTrue:
line=input().strip()
ifnotline:
break
data.append(line)
returndata
defpreproc(line):
"""
一行の命令を命令名と引数の列に分解する。
引数は英数字以外の文字で分割され、前から順番にargsに入る。
d(Rb)の形式のものは、d,Rbの順でargsに入る。
"""
head, *tail=re.findall(r"[a-zA-Z]+|[-+]?\d+", line)
cmd=head.upper()
args= []
foriintail:
try:
args.append(int(i))
exceptException:
raiseValueError(i)
returncmd, args
defto_binary(num, digit, signed=False):
"""
integerを指定された桁数(digit)の二進数に変換する。
signed=Falseの場合は0埋めされ、signed=Trueの場合は二の補数表示になる。
"""
ifsigned:
ifnot-(2** (digit-1)) <=num<2** (digit-1):
raiseValueError(num)
returnformat(num& (2**digit-1), "0"+str(digit) +"b")
else:
ifnot0<=num<2**digit:
raiseValueError(num)
returnformat(num, "0"+str(digit) +"b")
defassemble(data):
"""
下処理された cmd と args の列を受け取り, 2進数の命令列を返す
それぞれの cmd に対応した関数に args を渡すことで変換を行う
命令を追加するときは inst に関数を追加する
"""
result= []
inst= { # 引数の数が多すぎるときに例外を発生させるため
"ADD": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0000"+"0000",
"SUB": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0001"+"0000",
"AND": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0010"+"0000",
"OR": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0011"+"0000",
"XOR": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0100"+"0000",
"CMP": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0101"+"0000",
"MOV": lambdard, rs:
"11"+to_binary(rs, 3) +to_binary(rd, 3) +"0110"+"0000",
"SLL": lambdard, d:
"11"+"000"+to_binary(rd, 3) +"1000"+to_binary(d, 4),
"SLR": lambdard, d:
"11"+"000"+to_binary(rd, 3) +"1001"+to_binary(d, 4),
"SRL": lambdard, d:
"11"+"000"+to_binary(rd, 3) +"1010"+to_binary(d, 4),
"SRA": lambdard, d:
"11"+"000"+to_binary(rd, 3) +"1011"+to_binary(d, 4),
"IN": lambdard:
"11"+"000"+to_binary(rd, 3) +"1100"+"0000",
"OUT": lambdars:
"11"+to_binary(rs, 3) +"000"+"1101"+"0000",
"HLT": lambda:
"11"+"000"+"000"+"1111"+"0000",
"LD": lambdara, d, rb:
"00"+to_binary(ra, 3) +to_binary(rb, 3) +to_binary(d, 8, True),
"ST": lambdara, d, rb:
"01"+to_binary(ra, 3) +to_binary(rb, 3) +to_binary(d, 8, True),
"LI": lambdarb, d:
"10"+"000"+to_binary(rb, 3) +to_binary(d, 8, signed=True),
"B": lambdad:
"10"+"100"+"000"+to_binary(d, 8, signed=True),
"BE": lambdad:
"10"+"111"+"000"+to_binary(d, 8, signed=True),
"BLT": lambdad:
"10"+"111"+"001"+to_binary(d, 8, signed=True),
"BLE": lambdad:
"10"+"111"+"010"+to_binary(d, 8, signed=True),
"BNE": lambdad:
"10"+"111"+"011"+to_binary(d, 8, signed=True)
}
fori, lineinenumerate(data):
ifnotlineorline.startswith("//"):
continue
cmd, args="", []
try:
cmd, args=preproc(line)
exceptValueErrorase:
print(str(i+1) +"行目: 命令の引数が不正です", e, file=sys.stderr)
exit(1)
try:
ifcmdininst:
result.append(inst[cmd](*args))
elifcmd.isdigit() or (cmd[0] =="-"andcmd[1:].isdigit()): # 命令ではない値
result.append(to_binary(int(cmd), 16, signed=True))
else:
print(str(i+1) +"行目:コマンド名が正しくありません", file=sys.stderr)
exit(1)
exceptValueErrorase:
print(str(i+1) +"行目 "+str(e) +": 値の大きさが不正です", file=sys.stderr)
exit(1)
exceptTypeErrorase:
print(str(i+1) +"行目 : 引数の数が不正です", e, file=sys.stderr)
exit(1)
returnresult
defformat_result(result, address_radix=10, data_radix=10,
width=16, depth=4096, fill=0, **kwargs):
"""
アセンブルした二進数のリストを適当な形式に整えたものを返す
アドレスの基数, データの基数をそれぞれ 2, 10, 16 から選ぶことができる
ワード幅やメモリの語数を指定することができる
ただし命令長を16ビットとしてアセンブリしているのでワード幅を変更すると正しい結果が得られないことがある
命令を書き終えた残りのメモリに書き込む値を指定することができる
"""
radix= {
2: ("BIN", "b"),
10: ("DEC", "d"),
16: ("HEX", "x")
}
rdx_a, fmt_a=radix[address_radix]
rdx_d, fmt_d=radix[data_radix]
header=f"""WIDTH={width};
DEPTH={depth};
ADDRESS_RADIX={rdx_a};
DATA_RADIX={rdx_d};
CONTENT BEGIN"""
ifaddress_radix==10: # 基数が10のとき0埋めしない
padding_a=0
else:
padding_a=len(f"{depth-1 : {fmt_a}}")
ifdata_radix==10:
padding_d=0
else:
padding_d=len(f"{2**width-1 : {fmt_d}}")
formatted= [header]
fori, bin_instinenumerate(result):
ifaddress_radix==10:
address=i
else:
address=f"{i : 0{padding_a}{fmt_a}}"
ifdata_radix==10:
ifbin_inst[0] =="1":
inst=str(int(bin_inst, 2) -2**width)
else:
inst=str(int(bin_inst, 2))
else:
inst=f"{int(bin_inst, 2) : 0{padding_d}{fmt_d}}"
formatted.append(f"{address} : {inst};")
formatted.append(
f"[{len(result): 0{padding_a}{fmt_a}} ..{depth-1: 0{padding_a}{fmt_a}}] : {fill: 0{padding_d}{fmt_d}};")
formatted.append("END;")
return"\n".join(formatted)
defwrite_result(result, output=None, **kwargs):
"""
アセンブルした二進数のリストを書き込む
書き込み先は、コマンドライン引数によって指定された場合はそのファイル、
されなかった場合は標準出力
"""
s=format_result(result, **kwargs)
ifoutput:
try:
withopen(output, "w") asf:
f.write(s)
print(f"{output} に書き込みました")
exceptExceptionase:
print(f"ファイル {output} を開けなかったので標準出力に出力します" , e, file=sys.stderr)
print(s)
else:
print(s)
defmain():
parser=argparse.ArgumentParser(description="SIMPLEアセンブラ")
parser.add_argument("input", help="入力ファイル (デフォルト: 標準入力)", nargs="?")
parser.add_argument("output", help="出力ファイル (デフォルト: 標準出力)", nargs="?")
parser.add_argument("-d", "--depth", help="ワード数 (デフォルト: 4096)", type=int, nargs="?", default=4096)
parser.add_argument("-ar", "--address_radix", help="アドレスの基数 (デフォルト: 10)", type=int, nargs="?", default=10, choices=[2, 10, 16])
parser.add_argument("-dr", "--data_radix", help="データの基数 (デフォルト: 10)", type=int, nargs="?", default=10, choices=[2, 10, 16])
parser.add_argument("-f", "--fill", help="空きメモリに埋める数 (デフォルト: 0)", type=int, nargs="?", default=0)
args=parser.parse_args()
data=read_data(args.input)
result=assemble(data)
write_result(result, **vars(args)) # 全部渡す
if__name__=="__main__":
main()