View Javadoc

1   /*
2    * $Id: TestFrame.java,v 1.7 2005/10/25 06:46:14 weiju Exp $
3    * 
4    * Created on 13.10.2005
5    * Copyright 2005 by Wei-ju Wu
6    *
7    * This file is part of The Z-machine Preservation Project (ZMPP).
8    *
9    * ZMPP is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   *
14   * ZMPP is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with ZMPP; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
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         // write it again, to avoid that the stack is modified
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 }