1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.zmpp.debug;
24
25 import java.awt.BorderLayout;
26 import java.awt.Dimension;
27 import java.awt.FlowLayout;
28 import java.awt.GridLayout;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.util.Formatter;
32
33 import javax.swing.JButton;
34 import javax.swing.JDialog;
35 import javax.swing.JFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JPanel;
38 import javax.swing.JScrollPane;
39 import javax.swing.JTable;
40 import javax.swing.JTextArea;
41 import javax.swing.JTextField;
42 import javax.swing.table.AbstractTableModel;
43
44 import org.zmpp.base.MemoryAccess;
45 import org.zmpp.vm.AbstractInstruction;
46 import org.zmpp.vm.InputStream;
47 import org.zmpp.vm.LongInstruction;
48 import org.zmpp.vm.Machine3;
49 import org.zmpp.vm.Operand;
50 import org.zmpp.vm.OutputStream;
51 import org.zmpp.vm.PrintLiteralInstruction;
52 import org.zmpp.vm.Short0Instruction;
53 import org.zmpp.vm.Short1Instruction;
54 import org.zmpp.vm.VariableInstruction;
55 import org.zmpp.vm.AbstractInstruction.OperandCount;
56 import org.zmpp.vm.Operand.OperandType;
57 import org.zmpp.vmutil.ZsciiString;
58
59 /***
60 * This is the main frame of the testing ui.
61 *
62 * @author Wei-ju Wu
63 * @version 1.0
64 */
65 public class TestFrame extends JFrame implements ActionListener,
66 InputStream {
67
68 private static final long serialVersionUID = 1L;
69 private Machine3 machine;
70 private JTextField pcField;
71 private JTextField countField;
72 private JTextField spField;
73 private JTextField routineStackField;
74 private JLabel countLabel;
75 private LocalTableModel localTableModel;
76 private GlobalTableModel globalTableModel;
77
78 private JTextField instFormField;
79 private JTextField opCountField;
80 private JTextField opcodeField;
81 private JTextField opnameField;
82 private JTextField branchIfTrueField;
83 private JTextField branchOffsetField;
84 private JTextField storeVarField;
85 private JTextField stepField;
86 private OperandTableModel operandTableModel;
87 private AbstractInstruction instruction;
88
89 private class OutputFrame extends JFrame implements OutputStream {
90
91 private static final long serialVersionUID = 1L;
92 private JTextArea area;
93
94 public OutputFrame() {
95
96 super("Output");
97 area = new JTextArea(40, 80);
98 JScrollPane spane = new JScrollPane(area);
99 getContentPane().add(spane, BorderLayout.CENTER);
100 }
101
102 public void newline() {
103
104 area.append("\n");
105 }
106
107 public void print(String str) {
108
109 area.append(str);
110 }
111 }
112 int count = 1;
113
114 private void printInstruction(AbstractInstruction instr) {
115
116 StringBuilder infoBuilder = new StringBuilder();
117 infoBuilder.append("$");
118 infoBuilder.append(formatHex((short) machine.getProgramCounter()));
119 infoBuilder.append(": ");
120
121 infoBuilder.append(getOpName(instr));
122 infoBuilder.append("\t\t");
123 for (int i = 0; i < instr.getNumOperands(); i++) {
124
125 if (i > 0) infoBuilder.append(", ");
126 infoBuilder.append(getOperand(instr, i));
127 }
128
129 if (instr.storesResult()) {
130
131 infoBuilder.append(" -> ");
132 infoBuilder.append(getVariableName(instr.getStoreVariable()));
133 }
134
135 if (instr.isBranch()) {
136
137 infoBuilder.append("\t\t");
138 infoBuilder.append("[" + instr.branchIfTrue() + "]");
139 infoBuilder.append(" brOff: #$");
140 infoBuilder.append(formatHex(instr.getBranchOffset()));
141 }
142 System.out.println(infoBuilder.toString());
143 }
144
145 private String getOperand(AbstractInstruction instr, int operandNum) {
146
147 Operand operand = instr.getOperand(operandNum);
148 switch (operand.getType()) {
149 case VARIABLE:
150 short value = machine.getVariable(operand.getValue());
151
152 machine.setVariable(operand.getValue(), value);
153
154 return getVariableName(operand.getValue())
155 + "(#$" + formatHex(value) + ")";
156 default:
157 return "#$" + formatHex(operand.getValue());
158 }
159 }
160
161 public void actionPerformed(ActionEvent e) {
162
163 int steps = Integer.parseInt(stepField.getText());
164 for (int i = 0; i < steps; i++) {
165 instruction.execute();
166 instruction = (AbstractInstruction) machine.nextStep();
167 printInstruction(instruction);
168 }
169 pcField.setText(getProgramCounterText());
170 countField.setText(String.valueOf(count++));
171 spField.setText(getStackString());
172 localTableModel.update();
173 globalTableModel.update();
174 routineStackField.setText(String.valueOf(machine.getRoutineStackPointer()));
175
176 instFormField.setText(instruction.getInstructionForm().toString());
177 opCountField.setText(instruction.getOperandCount().toString());
178 opcodeField.setText(formatHex((short) instruction.getOpcode()));
179 opnameField.setText(getOpName(instruction));
180 branchIfTrueField.setText(instruction.isBranch() ? String.valueOf(instruction.branchIfTrue()) : "");
181 branchOffsetField.setText(instruction.isBranch() ? formatHex(instruction.getBranchOffset()) : "");
182
183 storeVarField.setText(instruction.storesResult() ? getVariableName(instruction.getStoreVariable()) : "");
184 operandTableModel.setInstruction(instruction);
185 operandTableModel.update();
186 }
187
188 private String formatHex(short number) {
189
190 Formatter formatter = new Formatter();
191 formatter.format("%x", number);
192 return formatter.toString();
193 }
194
195 private String getVariableName(int varnum) {
196
197 if (varnum == 0) return "SP";
198 if (varnum < 0x10) return "L" + (varnum - 1);
199 return "G" + formatHex((short) (varnum - 0x10));
200 }
201
202 private String getProgramCounterText() {
203
204 return formatHex((short) machine.getProgramCounter());
205 }
206
207 private String getStackString() {
208
209 return "(" + machine.getStackPointer() + "): " + formatHex(machine.getStackTopElement());
210 }
211
212
213 class GlobalTableModel extends AbstractTableModel {
214
215 private static final long serialVersionUID = 1L;
216 private Machine3 machine;
217
218 public GlobalTableModel(Machine3 machine) { this.machine = machine; }
219 public int getRowCount() { return 240; }
220 public int getColumnCount() { return 2; }
221 public String getColumnName(int column) {
222
223 if (column == 0) return "Name";
224 return "Value";
225 }
226 public Object getValueAt(int row, int column) {
227
228 if (column == 0) return "G" + formatHex((short) row);
229 else {
230 return formatHex(machine.getVariable(row + 0x10));
231 }
232 }
233
234 public void update() {
235 super.fireTableDataChanged();
236 }
237 }
238
239 class OperandTableModel extends AbstractTableModel {
240
241 private static final long serialVersionUID = 1L;
242 private AbstractInstruction inst;
243
244 public OperandTableModel() { }
245 public void setInstruction(AbstractInstruction inst) {
246
247 this.inst = inst;
248 }
249
250 public int getRowCount() {
251 if (inst instanceof PrintLiteralInstruction) {
252
253 return 1;
254 }
255 return (inst != null) ? inst.getNumOperands() : 0;
256 }
257 public int getColumnCount() { return 3; }
258 public String getColumnName(int column) {
259
260 if (column == 0) return "Name";
261 if (column == 1) return "Type";
262 return "Value";
263 }
264 public Object getValueAt(int row, int column) {
265
266 if (column == 0) return "Op " + row;
267 else if (column == 1) {
268
269 if (inst instanceof PrintLiteralInstruction) return "ZSTRING";
270 return inst.getOperand(row).getType();
271 }
272 else {
273
274 if (inst instanceof PrintLiteralInstruction) {
275
276 PrintLiteralInstruction plinst = (PrintLiteralInstruction) inst;
277 return (new ZsciiString(machine.getMemoryAccess(), plinst.getLiteralAddress())).toString();
278 }
279 if (inst.getOperand(row).getType() == OperandType.VARIABLE) {
280
281 return getVariableName(inst.getOperand(row).getValue());
282
283 }
284 return formatHex(inst.getOperand(row).getValue());
285 }
286 }
287
288 public void update() {
289 super.fireTableDataChanged();
290 }
291 }
292
293
294 public TestFrame(Machine3 machine) {
295
296 super("Z-machine test gui");
297 this.machine = machine;
298
299 instruction = (AbstractInstruction) machine.nextStep();
300 printInstruction(instruction);
301
302 JPanel toppanel = new JPanel(new BorderLayout());
303 JPanel toptopPanel = new JPanel(new GridLayout(3, 1));
304 JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
305 JPanel instrpanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
306 JPanel instr2panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
307
308 JLabel instFormLabel = new JLabel("Inst. Form: ");
309 instFormField = new JTextField(instruction.getInstructionForm().toString(), 10);
310 instrpanel.add(instFormLabel);
311 instrpanel.add(instFormField);
312
313 JLabel opCountLabel = new JLabel("Op. Count: ");
314 opCountField = new JTextField(instruction.getOperandCount().toString(), 6);
315 instrpanel.add(opCountLabel);
316 instrpanel.add(opCountField);
317
318 JLabel opcodeLabel = new JLabel("Opcode: ");
319 opcodeField = new JTextField(formatHex((short) instruction.getOpcode()), 5);
320 instrpanel.add(opcodeLabel);
321 instrpanel.add(opcodeField);
322
323 JLabel opnameLabel = new JLabel("Name: ");
324 opnameField = new JTextField(getOpName(instruction), 10);
325 instrpanel.add(opnameLabel);
326 instrpanel.add(opnameField);
327
328 JLabel branchIfTrueLabel = new JLabel("Branch cond: ");
329 branchIfTrueField = new JTextField(instruction.isBranch() ? String.valueOf(instruction.branchIfTrue()) : "", 6);
330 instr2panel.add(branchIfTrueLabel);
331 instr2panel.add(branchIfTrueField);
332
333 JLabel branchOffsetLabel = new JLabel("Branch offset: ");
334 branchOffsetField = new JTextField(instruction.isBranch() ? formatHex(instruction.getBranchOffset()) : "", 6);
335 instr2panel.add(branchOffsetLabel);
336 instr2panel.add(branchOffsetField);
337
338 JLabel storeVarLabel = new JLabel("Store variable: ");
339 storeVarField = new JTextField(instruction.storesResult() ? getVariableName(instruction.getStoreVariable()) : "", 4);
340 instr2panel.add(storeVarLabel);
341 instr2panel.add(storeVarField);
342
343 operandTableModel = new OperandTableModel();
344 operandTableModel.setInstruction(instruction);
345 JTable operandTable = new JTable(operandTableModel);
346 JScrollPane opspane = new JScrollPane(operandTable);
347 opspane.setPreferredSize(new Dimension(10, 80));
348
349 toptopPanel.add(panel);
350 toptopPanel.add(instrpanel);
351 toptopPanel.add(instr2panel);
352 toppanel.add(toptopPanel, BorderLayout.NORTH);
353 toppanel.add(opspane, BorderLayout.CENTER);
354
355 JPanel buttonpanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
356 JPanel varpanel = new JPanel();
357
358 countLabel = new JLabel("i:");
359 countField = new JTextField(String.valueOf(count++), 4);
360
361 JLabel pclabel = new JLabel("PC");
362 pcField = new JTextField(getProgramCounterText(), 20);
363
364 JLabel splabel = new JLabel("SP");
365 spField = new JTextField(getStackString(), 20);
366
367 JLabel rsplabel = new JLabel("RSP");
368 routineStackField = new JTextField(formatHex((short) machine.getRoutineStackPointer()), 20);
369
370 JButton next = new JButton("NEXT");
371
372 panel.add(countLabel);
373 panel.add(countField);
374 panel.add(pclabel);
375 panel.add(pcField);
376 panel.add(splabel);
377 panel.add(spField);
378 panel.add(rsplabel);
379 panel.add(routineStackField);
380
381 localTableModel = new LocalTableModel(machine);
382 JTable localtable = new JTable(localTableModel);
383 JScrollPane localspane = new JScrollPane(localtable);
384 varpanel.add(localspane);
385
386 globalTableModel = new GlobalTableModel(machine);
387 JTable globaltable = new JTable(globalTableModel);
388 JScrollPane globalspane = new JScrollPane(globaltable);
389 varpanel.add(globalspane);
390
391 buttonpanel.add(new JLabel("# steps: "));
392 stepField = new JTextField("1", 5);
393 buttonpanel.add(stepField);
394 buttonpanel.add(next);
395
396 next.addActionListener(this);
397
398 getContentPane().add(toppanel, BorderLayout.NORTH);
399 getContentPane().add(varpanel, BorderLayout.CENTER);
400 getContentPane().add(buttonpanel, BorderLayout.SOUTH);
401
402 OutputFrame outputFrame = new OutputFrame();
403 outputFrame.pack();
404 machine.setOutputStream(1, outputFrame);
405 machine.setInputStream(1, this);
406 machine.selectInputStream(1);
407 outputFrame.setVisible(true);
408
409 ObjectTreeFrame treeFrame = new ObjectTreeFrame(machine);
410 treeFrame.pack();
411 treeFrame.setVisible(true);
412 }
413
414 private String getOpName(AbstractInstruction inst) {
415
416 switch (inst.getInstructionForm()) {
417
418 case LONG:
419 return getLongOpName(inst.getOpcode());
420 case SHORT:
421 return inst.getOperandCount() == OperandCount.C0OP ?
422 getShort0OpName(inst.getOpcode()) : getShort1OpName(inst.getOpcode());
423 default:
424 return getVarOpName(inst.getOpcode());
425 }
426 }
427
428 private String getLongOpName(int opcode) {
429
430 switch (opcode) {
431
432 case LongInstruction.OP_ADD: return "ADD";
433 case LongInstruction.OP_AND: return "AND";
434 case LongInstruction.OP_CLEAR_ATTR: return "CLEAR_ATTR";
435 case LongInstruction.OP_DEC_CHK: return "DEC_CHK";
436 case LongInstruction.OP_DIV: return "DIV";
437 case LongInstruction.OP_GET_NEXT_PROP: return "GET_NEXT_PROP";
438 case LongInstruction.OP_GET_PROP: return "GET_PROP";
439 case LongInstruction.OP_GET_PROP_ADDR: return "GET_PROP_ADDR";
440 case LongInstruction.OP_INC_CHK: return "INC_CHK";
441 case LongInstruction.OP_INSERT_OBJ: return "INSERT_OBJ";
442 case LongInstruction.OP_JE: return "JE";
443 case LongInstruction.OP_JG: return "JG";
444 case LongInstruction.OP_JIN: return "JIN";
445 case LongInstruction.OP_JL: return "JL";
446 case LongInstruction.OP_LOADB: return "LOADB";
447 case LongInstruction.OP_LOADW: return "LOADW";
448 case LongInstruction.OP_MOD: return "MOD";
449 case LongInstruction.OP_MUL: return "MUL";
450 case LongInstruction.OP_OR: return "OR";
451 case LongInstruction.OP_SET_ATTR: return "SET_ATTR";
452 case LongInstruction.OP_STORE: return "STORE";
453 case LongInstruction.OP_SUB: return "SUB";
454 case LongInstruction.OP_TEST: return "TEST";
455 case LongInstruction.OP_TEST_ATTR: return "TEST_ATTR";
456 }
457 return "unknown";
458 }
459
460 private String getShort0OpName(int opcode) {
461
462 switch (opcode) {
463 case Short0Instruction.OP_NEW_LINE: return "NEW_LINE";
464 case PrintLiteralInstruction.OP_PRINT: return "PRINT";
465 case PrintLiteralInstruction.OP_PRINT_RET: return "PRINT_RET";
466 case Short0Instruction.OP_NOP: return "NOP";
467 case Short0Instruction.OP_POP: return "POP";
468 case Short0Instruction.OP_QUIT: return "QUIT";
469 case Short0Instruction.OP_RESTART: return "RESTART";
470 case Short0Instruction.OP_RESTORE: return "RESTORE";
471 case Short0Instruction.OP_RET_POPPED: return "RET_POPPED";
472 case Short0Instruction.OP_RFALSE: return "RFALSE";
473 case Short0Instruction.OP_RTRUE: return "RTRUE";
474 case Short0Instruction.OP_SAVE: return "SAVE";
475 }
476 return "unknown";
477 }
478
479 private String getShort1OpName(int opcode) {
480
481 switch (opcode) {
482
483 case Short1Instruction.OP_DEC: return "DEC";
484 case Short1Instruction.OP_GET_CHILD: return "GET_CHILD";
485 case Short1Instruction.OP_GET_PARENT: return "GET_PARENT";
486 case Short1Instruction.OP_GET_PROP_LEN: return "GET_PROP_LEN";
487 case Short1Instruction.OP_GET_SIBLING: return "GET_SIBLING";
488 case Short1Instruction.OP_INC: return "INC";
489 case Short1Instruction.OP_JUMP: return "JUMP";
490 case Short1Instruction.OP_JZ: return "JZ";
491 case Short1Instruction.OP_LOAD: return "LOAD";
492 case Short1Instruction.OP_NOT: return "NOT";
493 case Short1Instruction.OP_PRINT_ADDR: return "PRINT_ADDR";
494 case Short1Instruction.OP_PRINT_OBJ: return "PRINT_OBJ";
495 case Short1Instruction.OP_PRINT_PADDR: return "PRINT_PADDR";
496 case Short1Instruction.OP_REMOVE_OBJ: return "REMOVE_OBJ";
497 case Short1Instruction.OP_RET: return "RET";
498 }
499 return "unknown";
500 }
501
502 private String getVarOpName(int opcode) {
503
504 switch (opcode) {
505 case VariableInstruction.OP_CALL: return "CALL";
506 case VariableInstruction.OP_INPUTSTREAM: return "INPUTSTREAM";
507 case VariableInstruction.OP_OUTPUTSTREAM: return "OUTPUTSTREAM";
508 case VariableInstruction.OP_PRINT_CHAR: return "PRINT_CHAR";
509 case VariableInstruction.OP_PRINT_NUM: return "PRINT_NUM";
510 case VariableInstruction.OP_PULL: return "PULL";
511 case VariableInstruction.OP_PUSH: return "PUSH";
512 case VariableInstruction.OP_PUT_PROP: return "PUT_PROP";
513 case VariableInstruction.OP_RANDOM: return "RANDOM";
514 case VariableInstruction.OP_SREAD: return "SREAD";
515 case VariableInstruction.OP_STOREB: return "STOREB";
516 case VariableInstruction.OP_STOREW: return "STOREW";
517 }
518 return "unknown";
519 }
520
521 class InputDialog extends JDialog implements ActionListener {
522
523 private static final long serialVersionUID = 1L;
524 private JButton okButton;
525 private JTextField inputField;
526
527 public InputDialog(JFrame parent) {
528 super(parent, "Input", true);
529
530 JPanel inputPanel = new JPanel();
531 inputField = new JTextField(80);
532 inputPanel.add(inputField);
533 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
534 okButton = new JButton("Ok");
535 okButton.addActionListener(this);
536 buttonPanel.add(okButton);
537 getContentPane().add(inputPanel, BorderLayout.CENTER);
538 getContentPane().add(buttonPanel, BorderLayout.SOUTH);
539 getRootPane().setDefaultButton(okButton);
540 }
541
542 public String getInput() {
543
544 return inputField.getText();
545 }
546 public void actionPerformed(ActionEvent e) {
547
548 dispose();
549 }
550 }
551
552 public void readLine(MemoryAccess memaccess, int address, int bufferlen) {
553
554 InputDialog dialog = new InputDialog(this);
555 dialog.pack();
556 dialog.setVisible(true);
557 String input = dialog.getInput();
558 for (int i = 0; i < input.length(); i++) {
559
560 memaccess.writeUnsignedByte(address + i, (short) input.charAt(i));
561 }
562 memaccess.writeUnsignedByte(address + input.length(), (short) 0);
563 }
564 }