· last year · Oct 01, 2024, 04:56 PM
1import javax.swing.*;
2import java.io.IOException;
3import java.io.*;
4import java.util.HashMap;
5import java.util.LinkedHashMap;
6import java.util.Map;
7import java.awt.*;
8import java.io.File;
9import java.io.InputStream;
10
11class TwoPassAssemblerGUI extends JFrame {
12 private final JTextField inputFileField;
13 private final JTextField optabFileField;
14 private final JButton assembleBtn;
15 private final JTextArea intermediateFileOutput;
16 private final JTextArea symbolTableOutput;
17 private final JTextArea objectCodeOutput;
18
19 public TwoPassAssemblerGUI() throws IOException {
20 setTitle("Two-Pass Assembler");
21 setSize(800, 650);
22 setResizable(false);
23 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
24
25 Font font = new Font("Arial", Font.PLAIN, 16);
26 Font font2 = new Font("Monospaced", Font.PLAIN, 14);
27
28 // Input label and field
29 JLabel inputFileLabel = new JLabel("Input File:");
30 inputFileLabel.setFont(font);
31 inputFileField = new JTextField(30);
32 inputFileField.setFont(font);
33 JButton browseBtn = new JButton("Browse");
34 browseBtn.setFont(font);
35 browseBtn.addActionListener(e -> browseFile(inputFileField));
36
37 // Input panel
38 JPanel inputPanel = new JPanel(new BorderLayout(10, 0));
39 inputPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
40 inputPanel.add(inputFileLabel, BorderLayout.WEST);
41 inputPanel.add(inputFileField, BorderLayout.CENTER);
42 inputPanel.add(browseBtn, BorderLayout.EAST);
43
44 // Optab label and field
45 JLabel optabFileLabel = new JLabel("Optab File:");
46 optabFileLabel.setFont(font);
47 optabFileField = new JTextField(30);
48 optabFileField.setFont(font);
49 JButton optabBrowseBtn = new JButton("Browse");
50 optabBrowseBtn.setFont(font);
51 optabBrowseBtn.addActionListener(e -> browseFile(optabFileField));
52
53 // Optab panel
54 JPanel optabPanel = new JPanel(new BorderLayout(10, 0));
55 optabPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
56 optabPanel.add(optabFileLabel, BorderLayout.WEST);
57 optabPanel.add(optabFileField, BorderLayout.CENTER);
58 optabPanel.add(optabBrowseBtn, BorderLayout.EAST);
59
60 // Assemble button
61 assembleBtn = new JButton("Assemble");
62 assembleBtn.setFont(new Font("Arial", Font.BOLD, 16));
63 assembleBtn.setBackground(new Color(0, 153, 255));
64 assembleBtn.setForeground(Color.WHITE);
65 assembleBtn.setFocusPainted(false);
66 assembleBtn.setPreferredSize(new Dimension(120, 35));
67 assembleBtn.addActionListener(e -> runAssembler());
68
69 // Button panel
70 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
71 buttonPanel.add(assembleBtn);
72
73 // Text areas
74 intermediateFileOutput = new JTextArea(10, 30);
75 intermediateFileOutput.setBorder(BorderFactory.createTitledBorder("Intermediate File Output"));
76 intermediateFileOutput.setFont(font2);
77 intermediateFileOutput.setEditable(false);
78
79 symbolTableOutput = new JTextArea(10, 30);
80 symbolTableOutput.setBorder(BorderFactory.createTitledBorder("Symbol Table Output"));
81 symbolTableOutput.setFont(font2);
82 symbolTableOutput.setEditable(false);
83
84 objectCodeOutput = new JTextArea(8, 91);
85 objectCodeOutput.setBorder(BorderFactory.createTitledBorder("Object Code Output"));
86 objectCodeOutput.setFont(font2);
87 objectCodeOutput.setEditable(false);
88
89 // Panel for text areas
90 JPanel textAreaPanel = new JPanel(new GridLayout(1, 2, 10, 10));
91 textAreaPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
92 textAreaPanel.add(new JScrollPane(intermediateFileOutput));
93 textAreaPanel.add(new JScrollPane(symbolTableOutput));
94
95 // Bottom panel for object code
96 JPanel bottomTextAreaPanel = new JPanel(new BorderLayout());
97 bottomTextAreaPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
98 bottomTextAreaPanel.add(new JScrollPane(objectCodeOutput), BorderLayout.CENTER);
99
100 // Main panel to hold all components
101 JPanel mainPanel = new JPanel();
102 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
103 mainPanel.add(inputPanel);
104 mainPanel.add(optabPanel);
105 mainPanel.add(buttonPanel);
106 mainPanel.add(textAreaPanel);
107 mainPanel.add(bottomTextAreaPanel);
108
109 add(mainPanel, BorderLayout.CENTER);
110
111 // Center the window on screen
112 setLocationRelativeTo(null);
113 }
114
115 //Browse file
116 private void browseFile(JTextField field) {
117 JFileChooser fileChooser = new JFileChooser();
118 int option = fileChooser.showOpenDialog(this);
119 if (option == JFileChooser.APPROVE_OPTION) {
120 File file = fileChooser.getSelectedFile();
121 field.setText(file.getPath());
122 }
123 }
124
125 // Run the assembler when the "Assemble" button is clicked
126 private void runAssembler() {
127 String inputFile = inputFileField.getText();
128 String optabFile = optabFileField.getText();
129
130 if (inputFile.isEmpty()) {
131 JOptionPane.showMessageDialog(this, "Please provide the input file!", "Error", JOptionPane.ERROR_MESSAGE);
132 return;
133 }
134
135 // Disable the Assemble button to prevent multiple clicks
136 assembleBtn.setEnabled(false);
137
138 // Use a SwingWorker to handle the assembly process in the background
139 SwingWorker<Void, Void> worker = new SwingWorker<>() {
140 @Override
141 protected Void doInBackground() {
142 TwoPassAssembler assembler = new TwoPassAssembler(inputFile, optabFile);
143 try {
144 assembler.loadOptab(); // Load opcode table
145 assembler.passOne(); // Run first pass
146 assembler.passTwo(); // Run second pass
147
148 // Display outputs from the HashMaps instead of files
149 displayIntermediateCode(assembler.getIntermediate(), assembler.getIntermediateStart());
150 displaySymbolTable(assembler.getSymtab());
151 displayObjectCode(assembler.getObjectCode());
152
153 } catch (IOException e) {
154 JOptionPane.showMessageDialog(TwoPassAssemblerGUI.this, "Error running the assembler: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
155 }
156 return null;
157 }
158
159 @Override
160 protected void done() {
161 // Re-enable the Assemble button once processing is complete
162 assembleBtn.setEnabled(true);
163 }
164 };
165
166 worker.execute();
167 }
168
169
170 // Method to display intermediate code from the HashMap
171 private void displayIntermediateCode(Map<Integer, String> intermediate, Map<Integer, String> intermediateStart) {
172 StringBuilder content = new StringBuilder();
173 for (Map.Entry<Integer, String> entry : intermediateStart.entrySet()) {
174 content.append(String.format("%04X",entry.getKey())).append(entry.getValue()).append("\n");
175 }
176 for (Map.Entry<Integer, String> entry : intermediate.entrySet()) {
177 content.append(String.format("%04X",entry.getKey())).append(entry.getValue()).append("\n");
178 }
179 intermediateFileOutput.setText(content.toString()); // Display in the text area
180 }
181
182 // Method to display symbol table from the HashMap
183 private void displaySymbolTable(Map<String, Integer> symtab) {
184 StringBuilder content = new StringBuilder();
185 for (Map.Entry<String, Integer> entry : symtab.entrySet()) {
186 content.append(entry.getKey()).append("\t").append(String.format("%04X",entry.getValue())).append("\n");
187 }
188 symbolTableOutput.setText(content.toString()); // Display in the text area
189 }
190
191 // Method to display object code from the HashMap
192 private void displayObjectCode(Map<Integer, String> objectCode) {
193 StringBuilder content = new StringBuilder();
194 for (Map.Entry<Integer, String> entry : objectCode.entrySet()) {
195 content.append(entry.getValue()).append("\n");
196 }
197 objectCodeOutput.setText(content.toString()); // Display in the text area
198 }
199
200 public Icon getBrowseIcon() throws IOException {
201 // Use ClassLoader to get the resource as an InputStream
202 InputStream inputStream = getClass().getClassLoader().getResourceAsStream("browse-icon.png");
203 if (inputStream == null) {
204 System.out.println("Resource not found: browse-icon.png");
205 return null;
206 }
207
208 // Create an ImageIcon from the InputStream
209 return new ImageIcon(inputStream.readAllBytes());
210 }
211}
212
213class TwoPassAssembler {
214 private final String inputFile;
215 private final String optabFile;
216 private final Map<String, String> optab = new HashMap<>();
217 private final Map<String, Integer> symtab = new LinkedHashMap<>();
218 private final Map<Integer, String> intermediate = new LinkedHashMap<>();
219 private final Map<Integer, String> intermediateStart = new LinkedHashMap<>();
220 private final Map<Integer, String> objectCode = new LinkedHashMap<>();
221 private int locctr = 0;
222 private int start = 0;
223 private int length = 0;
224
225 public TwoPassAssembler(String inputFile, String optabFile) {
226 this.inputFile = inputFile;
227 this.optabFile = optabFile;
228 }
229
230 public void loadOptab() throws IOException {
231 BufferedReader reader = new BufferedReader(new FileReader(optabFile));
232 String line;
233 while ((line = reader.readLine()) != null) {
234 String[] parts = line.split("\\s+");
235 optab.put(parts[0], parts[1]);
236 }
237 reader.close();
238 }
239
240 public void passOne() throws IOException {
241 BufferedReader reader = new BufferedReader(new FileReader(inputFile));
242 String line = reader.readLine();
243 String[] parts = line.split("\\s+");
244
245 if (parts[1].equals("START")) {
246 start = Integer.parseInt(parts[2], 16); // Parse start as hexadecimal
247 locctr = start;
248 intermediateStart.put(locctr, String.format("\t%s\t%s\t%s", parts[0], parts[1], parts[2]));
249 line = reader.readLine();
250 } else {
251 locctr = 0;
252 }
253
254 // Process each line
255 while (line != null) {
256 parts = line.split("\\s+");
257 if (parts[1].equals("END")) break;
258
259 // Store intermediate with location counter in hex format
260 intermediate.put(locctr, String.format("\t%s\t%s\t%s", parts[0], parts[1], parts[2]));
261
262 // Insert into symbol table if label exists
263 if (!parts[0].equals("-")) {
264 symtab.put(parts[0], locctr);
265 }
266
267 // Update locctr based on the opcode
268 if (optab.containsKey(parts[1])) {
269 locctr += 3;
270 } else if (parts[1].equals("WORD")) {
271 locctr += 3;
272 } else if (parts[1].equals("BYTE")) {
273 locctr += parts[2].length() - 3;
274 } else if (parts[1].equals("RESW")) {
275 locctr += 3 * Integer.parseInt(parts[2]);
276 } else if (parts[1].equals("RESB")) {
277 locctr += Integer.parseInt(parts[2]);
278 }
279
280 line = reader.readLine();
281 }
282
283 // Store final line in intermediate and calculate program length
284 intermediate.put(locctr, String.format("\t%s\t%s\t%s", parts[0], parts[1], parts[2]));
285 length = locctr - start;
286
287 reader.close();
288 }
289
290 public void passTwo() throws IOException {
291 String line;
292 String[] parts;
293
294 String startLine = intermediateStart.get(start);
295 String[] startParts = startLine.trim().split("\\s+");
296
297 // Handle "START" directive
298 if (startParts[1].equals("START")) {
299 objectCode.put(0, "H^ " + startParts[0] + "^ " + String.format("%06X", start) + "^ " + String.format("%06X", length));
300 //line = intermediate.get(start + 1);
301 } else {
302 objectCode.put(0, "H^ " + " " + "^ 0000^ " + String.format("%06X", length));
303 }
304
305 StringBuilder textRecord = new StringBuilder();
306 int textStartAddr = 0;
307 int textLength = 0;
308
309 for (int loc : intermediate.keySet()) {
310
311 line = intermediate.get(loc);
312 parts = line.trim().split("\\s+");
313 if (parts.length < 3) continue;
314
315 if (parts[2].equals("END")) break;
316
317 if (textLength == 0) {
318 textStartAddr = loc;
319 textRecord.append("T^ ").append(String.format("%06X", textStartAddr)).append("^ ");
320 }
321
322 // Generate object code for each line
323 if (optab.containsKey(parts[1])) {
324 String machineCode = optab.get(parts[1]);
325 int address = symtab.getOrDefault(parts[2], 0);
326 String code = machineCode + String.format("%04X", address);
327 textRecord.append(code).append("^ ");
328 textLength += code.length() / 2;
329 } else if (parts[1].equals("WORD")) {
330 String wordCode = String.format("%06X", Integer.parseInt(parts[2]));
331 textRecord.append(wordCode).append("^ ");
332 textLength += wordCode.length() / 2;
333 } else if (parts[1].equals("BYTE")) {
334 String byteCode = parts[2].substring(2, parts[2].length() - 1); // Extract value from BYTE literal
335 textRecord.append(byteCode).append("^ ");
336 textLength += byteCode.length() / 2;
337 } else if (parts[1].equals("RESW") || parts[1].equals("RESB")) {
338 // If we hit RESW/RESB, flush the current text record and start a new one after reserving memory
339 if (textLength > 0) {
340 objectCode.put(textStartAddr, textRecord.toString());
341 textRecord = new StringBuilder();
342 textLength = 0;
343 }
344 continue; // Do not generate object code for reserved space
345 }
346
347 if (textLength >= 30) { // Text records should not exceed 30 bytes (60 hex characters)
348 objectCode.put(textStartAddr, textRecord.toString());
349 textRecord = new StringBuilder();
350 textLength = 0;
351 }
352 }
353
354 // Write remaining text record if not empty
355 if (textLength > 0) {
356 objectCode.put(textStartAddr, textRecord.toString());
357 }
358
359 // Write End record
360 objectCode.put(locctr, "E^ " + String.format("%06X", start));
361 }
362
363
364 public Map<Integer, String> getIntermediate() {
365 return intermediate;
366 }
367
368 public Map<Integer, String> getIntermediateStart() {
369 return intermediateStart;
370 }
371
372 public Map<String, Integer> getSymtab() {
373 return symtab;
374 }
375
376 public Map<Integer, String> getObjectCode() {
377 return objectCode;
378 }
379
380}
381
382public class Main {
383 public static void main(String[] args) {
384 SwingUtilities.invokeLater(() -> {
385 TwoPassAssemblerGUI gui;
386 try {
387 gui = new TwoPassAssemblerGUI();
388 } catch (IOException e) {
389 throw new RuntimeException(e);
390 }
391 gui.setVisible(true);
392 });
393 }
394}