-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.py
More file actions
142 lines (130 loc) · 6.03 KB
/
main.py
File metadata and controls
142 lines (130 loc) · 6.03 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
# R4 instruction opcodes
r4_opcodes = {
"simal": "000", # Signed Integer Multiply-Add Low with Saturation
"simah": "001", # Signed Integer Multiply-Add High with Saturation
"simsl": "010", # Signed Integer Multiply-Subtract Low with Saturation
"simsh": "011", # Signed Integer Multiply-Subtract High with Saturation
"simlal": "100", # Signed Long Integer Multiply-Add Low with Saturation
"simlah": "101", # Signed Long Integer Multiply-Add High with Saturation
"simlsl": "110", # Signed Long Integer Multiply-Subtract Low with Saturation
"simlsh": "111", # Signed Long Integer Multiply-Subtract High with Saturation
}
# R3 instruction opcodes
r3_opcodes = {
"nop": "00000000", # No Operation
"slhi": "00000001", # Shift left halfword immediate
"au": "00000010", # Add word unsigned
"cnt1h": "00000011", # Count 1s in halfwords
"ahs": "00000100", # add halfword saturated
"and": "00000101", # Bitwise logical AND
"bcw": "00000110", # Broadcast word
"maxws": "00000111", # Max signed word
"minws": "00001000", # Min signed word
"mlhu": "00001001", # Multiply low unsigned
"mlhcu": "00001010", # Multiply low by constant unsigned
"or": "00001011", # Bitwise logical or
"clzh": "00001100", # Count leading zeroes in halfwords
"rlh": "00001101", # Rotate left bits in halfwords
"sfwu": "00001110", # Subtract from word unsigned
"sfhs": "00001111", # Subtract from halfword saturated
}
# parses the register to know which register number it is
def parse_register(register):
register = register.rstrip(",")
if not register.startswith("$r"):
raise ValueError(f"Invalid register name: {register}")
try:
reg_num = int(register[2:])
if reg_num < 0 or reg_num > 31:
raise ValueError(f"Register number out of range: {register}")
return f"{reg_num:05b}" # Convert to 5-bit binary
except ValueError:
raise ValueError(f"Invalid register name: {register}")
# Read the immediate values
def parse_immediate(immediate):
try:
# Dont read commas
immediate = immediate.strip().rstrip(",")
imm = int(immediate) # Convert the decimal value to integer
if imm < -32768 or imm > 32767: # Checks if immediate is within 16-bit range
raise ValueError(f"Immediate value out of range: {immediate}")
return f"{imm & 0xFFFF:016b}" # hhandle negative numbers
except ValueError as e:
raise ValueError(f"Invalid immediate value: {immediate}") from e
# parses the load index
def parse_load_index(load_index):
try:
idx = int(load_index)
if idx < 0 or idx > 7:
raise ValueError(f"Load index out of range: {load_index}")
return f"{idx:03b}"
except ValueError:
raise ValueError(f"Invalid load index: {load_index}")
# Assembles the instruction
def assemble_instruction(line, line_number):
parts = line.split()
if not parts:
return None, f"Error on line {line_number}: Blank or invalid line"
instruction = parts[0].lower()
operands = [operand.strip() for operand in parts[1:]]
try:
if instruction == "li": # Load Immediate
if len(operands) != 3:
return None, f"Error on line {line_number}: LI requires 3 operands: {operands}"
rd = parse_register(operands[0])
immediate = parse_immediate(operands[1])
load_index = parse_load_index(operands[2])
binary = f"0{load_index}{immediate}{rd}"
if len(binary) != 25:
return None, f"Error on line {line_number}"
return binary, None
elif instruction == "nop": # No Operation
if len(operands) != 0:
return None, f"Error on line {line_number}: NOP"
binary = "1100000000000000000000000" # Fill instructions with zeros
return binary, None
elif instruction in r4_opcodes:
if len(operands) != 4:
return None, f"Error on line {line_number}: R4 instructions require 4 operands: {operands}"
rd = parse_register(operands[0])
rs1 = parse_register(operands[1])
rs2 = parse_register(operands[2])
rs3 = parse_register(operands[3])
opcode = r4_opcodes[instruction]
binary = f"10{opcode}{rs3}{rs2}{rs1}{rd}"
return binary, None
elif instruction in r3_opcodes:
if len(operands) != 3:
return None, f"Error on line {line_number}: R3 instructions require 3 operands: {operands}"
rd = parse_register(operands[0])
rs1 = parse_register(operands[1])
rs2 = parse_register(operands[2])
opcode = r3_opcodes[instruction]
binary = f"11{opcode}{rs2}{rs1}{rd}"
return binary, None
else:
return None, f"Error on line {line_number}: Unknown instruction: {instruction}"
except ValueError as e:
return None, f"Error on line {line_number}: {str(e)}"
# Main function
def assemble_file(input_file, output_file):
try:
with open(input_file, "r") as infile, open(output_file, "w") as outfile:
lines = infile.readlines()
errors = []
for line_number, line in enumerate(lines, start=1):
line = line.strip()
if not line: # Skip blank lines
continue
binary, comment = assemble_instruction(line, line_number)
if binary:
outfile.write(binary + "\n") # Write only the binary instruction
else:
errors.append(comment)
if errors:
print("\n".join(errors))
print("Output written to", output_file)
except FileNotFoundError:
print(f"Error: File '{input_file}' not found.")
# Run assembler
assemble_file("/Users/dongyunlee/Documents/SBU_2024_Fall/ESE345/ese345_project/instructions.txt", "/Users/dongyunlee/Documents/SBU_2024_Fall/ESE345/ese345_project/output.txt")