1 /****************************************************************************
2  * Copyright (C) 2012 by Matteo Franchin                                    *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 #include <stdio.h>
21 #include <assert.h>
22 
23 #include "types.h"
24 #include "strutils.h"
25 
26 #include "vm_priv.h"
27 #include "vmdasm_priv.h"
28 
29 
30 /**
31  * Structure used to pass arguments to My_Op_Dasm.
32  */
33 typedef struct {
34   BoxVMWord *bytecode;
35   FILE      *output;
36 } MyDasmData;
37 
38 
39 static void
My_Arg_To_Str(char * out,size_t out_size,int arg_format,BoxTypeId args_type,BoxInt arg_value)40 My_Arg_To_Str(char *out, size_t out_size,
41               int arg_format, BoxTypeId args_type, BoxInt arg_value) {
42   BoxInt arg_abs_value = abs(arg_value);
43   char reg_char = "vr"[arg_value >= 0],
44        type_char = "cirpo"[args_type];
45   switch(arg_format) {
46   case BOXCONTCATEG_GREG:
47     sprintf(out, "g%c%c" SInt, reg_char, type_char, arg_abs_value);
48     break;
49   case BOXCONTCATEG_LREG:
50     sprintf(out, "%c%c" SInt, reg_char, type_char, arg_abs_value);
51     break;
52   case BOXCONTCATEG_PTR:
53     if (arg_value < 0)
54       sprintf(out, "%c[ro0 - " SInt "]", type_char, arg_abs_value);
55     else if (arg_value == 0)
56       sprintf(out, "%c[ro0]", type_char);
57     else
58       sprintf(out, "%c[ro0 + " SInt "]", type_char, arg_abs_value);
59     break;
60   case BOXCONTCATEG_IMM:
61     if (args_type == BOXTYPEID_CHAR)
62       arg_value = (BoxInt) ((BoxChar) arg_value);
63     sprintf(out, SInt, arg_value);
64     break;
65   default:
66     abort();
67   }
68 }
69 
70 static void
My_Data_To_Str(char * out,size_t out_size,BoxTypeId t,void * data)71 My_Data_To_Str(char *out, size_t out_size, BoxTypeId t, void *data) {
72   switch (t) {
73   case BOXTYPEID_CHAR:
74     sprintf(out, SChar, *((BoxChar *) data));
75     break;
76   case BOXTYPEID_INT:
77     sprintf(out, SInt, *((BoxInt *) data));
78     break;
79   case BOXTYPEID_REAL:
80     sprintf(out, SReal, *((BoxReal *) data));
81     break;
82   case BOXTYPEID_POINT:
83     sprintf(out, SPoint,
84             ((BoxPoint *) data)->x, ((BoxPoint *) data)->y);
85     break;
86   default:
87     sprintf(out, "???");
88     break;
89   }
90 }
91 
92 static BoxTask
My_Op_Dasm(BoxVMDasm * dasm,void * pass)93 My_Op_Dasm(BoxVMDasm *dasm, void *pass) {
94   MyDasmData *data = pass;
95   FILE *output = data->output;
96   BoxOp *op = & dasm->op;
97   const char *op_name;
98   const size_t arg_buf_size = 64;
99   char arg_buf[BOX_OP_MAX_NUM_ARGS + 1][arg_buf_size];
100   int num_written_bufs;
101   int num_args;
102 
103   if (dasm->op_desc) {
104     op_name  = dasm->op_desc->name;      /* op name */
105     num_args = dasm->op_desc->num_args;  /* num. of args */
106 
107    } else {
108     op_name = "???";
109     num_args = 0;
110   }
111 
112   /* First we do arguments pre-processing. */
113   switch (op->id) {
114   case BOXOP_JMP_I:
115   case BOXOP_JC_I:
116     op->args[0] += dasm->op_pos;
117     op->args[0] *= sizeof(BoxVMWord);
118     break;
119   default:
120     break;
121   }
122 
123   /* Now we convert the arguments to string. */
124   num_written_bufs = 0;
125   if (num_args > 0) {
126     BoxTypeId t = dasm->op_desc->t_id;
127     int i;
128 
129     assert(num_args <= BOX_OP_MAX_NUM_ARGS);
130 
131     for (i = 0; i < num_args; i++)
132       My_Arg_To_Str(arg_buf[i], arg_buf_size,
133                     (op->args_forms >> (2*i)) & 0x3, t, op->args[i]);
134 
135     if (op->has_data)
136       My_Data_To_Str(arg_buf[i++], arg_buf_size, t, op->data);
137 
138     num_written_bufs = i;
139   }
140 
141   /* Finally we compose the instruction string, starting from its name... */
142   fprintf(output, SUInt "\t", (BoxUInt) (dasm->op_pos * sizeof(BoxVMWord)));
143   if (dasm->vm->attr.hexcode)
144     fprintf(output, BoxVMWord_Fmt"\t", data->bytecode[dasm->op_pos]);
145   fprintf(output, "%s", op_name);
146 
147   /* ...and the arguments. */
148   {
149     int i;
150     char *sep = " ";
151 
152     for (i = 0; i < num_written_bufs; i++, sep = ", ")
153       fprintf(output, "%s%s", sep, arg_buf[i]);
154   }
155 
156   /* Now we do any post-processing of the stringified arguments. */
157   switch (op->id) {
158   case BOXOP_CALL_I:
159   case BOXOP_CALL_Iimm:
160     {
161       BoxVMProcTable *pt = & dasm->vm->proc_table;
162       BoxInt call_num = ((op->id == BOXOP_CALL_I) ?
163                          op->args[0] : *((BoxInt *) op->data));
164 
165       if (call_num >= 1 || call_num <= BoxArr_Num_Items(& pt->installed)) {
166         BoxVMProcInstalled *p = BoxArr_Item_Ptr(& pt->installed, call_num);
167         char *call_name, *trunc_name;
168 
169         call_name = (p->desc) ? p->desc : p->name;
170         trunc_name = (call_name) ? Str_Cut(call_name, 40, 85) : NULL;
171         fprintf(output, " ('%.40s')", (trunc_name) ? trunc_name : "?");
172         Box_Mem_Free(trunc_name);
173       }
174     }
175     break;
176 
177   case BOXOP_CREATE_I:
178   case BOXOP_TYPEOF_I:
179     {
180       BoxTypeId type_id = op->args[0];
181       BoxType *type = BoxVM_Get_Installed_Type(dasm->vm, type_id);
182       if (type) {
183         char *type_repr = BoxType_Get_Repr(type);
184         if (type_repr) {
185           char *trunc_type_repr = Str_Cut(type_repr, 40, 85);
186 
187           if (trunc_type_repr) {
188             Box_Mem_Free(type_repr);
189             type_repr = trunc_type_repr;
190           }
191 
192           fprintf(output, " ('%.40s')", type_repr);
193           Box_Mem_Free(type_repr);
194         }
195       }
196     }
197     break;
198 
199   default:
200     break;
201   }
202 
203   fprintf(output, "\n");
204 
205   /* Print the remaining instruction words in hex. */
206   if (dasm->vm->attr.hexcode) {
207     size_t i;
208     for (i = 1; i < op->next; i++)
209       fprintf(output, "\t"BoxVMWord_Fmt"\n",
210               data->bytecode[dasm->op_pos + i]);
211   }
212 
213   return BOXTASK_OK;
214 }
215 
216 /* Traduce il codice binario della VM, in formato testo.
217  * prog e' il puntatore all'inizio del codice, dim e' la dimensione del codice
218  * da tradurre (espresso in "numero di BoxVMWord").
219  */
220 BoxTask
BoxVM_Disassemble(BoxVM * vm,FILE * output,const void * prog,size_t dim)221 BoxVM_Disassemble(BoxVM *vm, FILE *output, const void *prog, size_t dim) {
222   MyDasmData data;
223   data.output = output;
224   data.bytecode = (void *) prog;
225   return BoxVM_Disassemble_Block(vm, prog, dim, My_Op_Dasm, & data);
226 }
227