generate_arm_multi.c Source File

Back to the index.

generate_arm_multi.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2009 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *
28  * Generation of commonly used ARM load/store multiple instructions.
29  *
30  * The main idea is to first check whether a load/store would be possible
31  * without going outside a page, and if so, use the host_load or _store
32  * arrays for quick access to emulated RAM. Otherwise, fall back to using
33  * the generic bdt_load() or bdt_store().
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <inttypes.h>
39 
40 /*
41  * generate_opcode():
42  *
43  * Given an ARM load/store multiple opcode, produce equivalent "hardcoded"
44  * C code which emulates the opcode.
45  *
46  * TODO:
47  *
48  * o) On 64-bit hosts, load/store two registers at a time. This
49  * feature depends both on the alignment of the base register,
50  * and the specific set of registers being loaded/stored.
51  *
52  * o) Alignment checks. (Optional?)
53  *
54  * o) For accesses that cross page boundaries, use two pages using
55  * the fast method instead of calling the generic function?
56  */
57 void generate_opcode(uint32_t opcode)
58 {
59  int p, u, s, w, load, r, n_regs, i, x;
60 
61  if ((opcode & 0x0e000000) != 0x08000000) {
62  fprintf(stderr, "opcode 0x%08"PRIx32" is not an ldm/stm\n",
63  opcode);
64  exit(1);
65  }
66 
67  r = (opcode >> 16) & 15;
68  p = opcode & 0x01000000? 1 : 0;
69  u = opcode & 0x00800000? 1 : 0;
70  s = opcode & 0x00400000? 1 : 0;
71  w = opcode & 0x00200000? 1 : 0;
72  load = opcode & 0x00100000? 1 : 0;
73  n_regs = 0;
74  for (i=0; i<16; i++)
75  if (opcode & (1 << i))
76  n_regs ++;
77 
78  /* TODO: Check for register pairs, for 64-bit load/stores */
79 
80  if (n_regs == 0) {
81  fprintf(stderr, "opcode 0x%08"PRIx32" has no registers set\n",
82  opcode);
83  exit(1);
84  }
85 
86  if (s) {
87  fprintf(stderr, "opcode 0x%08"PRIx32" has s-bit set\n", opcode);
88  exit(1);
89  }
90 
91  if (r == 15) {
92  fprintf(stderr, "opcode 0x%08"PRIx32" has r=15\n", opcode);
93  exit(1);
94  }
95 
96  printf("\nvoid arm_instr_multi_0x%08"PRIx32"(struct cpu *cpu,"
97  " struct arm_instr_call *ic) {\n", opcode);
98 
99  printf("\tunsigned char *page;\n");
100  printf("\tuint32_t addr = cpu->cd.arm.r[%i];\n", r);
101 
102  if (!load && opcode & 0x8000) {
103  printf("\tuint32_t tmp_pc = ((size_t)ic - (size_t)\n\t"
104  " cpu->cd.arm.cur_ic_page) / sizeof(struct "
105  "arm_instr_call);\n"
106  "\ttmp_pc = ((cpu->pc & ~((ARM_IC_ENTRIES_PER_PAGE-1)"
107  "\n\t << ARM_INSTR_ALIGNMENT_SHIFT)))\n"
108  "\t + (tmp_pc << ARM_INSTR_ALIGNMENT_SHIFT) + 12;\n");
109  }
110 
111  if (p)
112  printf("\taddr %s 4;\n", u? "+=" : "-=");
113 
114  printf("\tpage = cpu->cd.arm.host_%s[addr >> 12];\n",
115  load? "load" : "store");
116 
117  printf("\taddr &= 0xffc;\n");
118 
119  printf("\tif (");
120  switch (p*2 + u) {
121  case 0: /* post-decrement */
122  if (n_regs > 1)
123  printf("addr >= 0x%x && ", 4*(n_regs-1));
124  break;
125  case 1: /* post-increment */
126  if (n_regs > 1)
127  printf("addr <= 0x%x && ", 0x1000 - 4*n_regs);
128  break;
129  case 2: /* pre-decrement */
130  if (n_regs > 1)
131  printf("addr >= 0x%x && ", 4*(n_regs-1));
132  break;
133  case 3: /* pre-increment */
134  if (n_regs > 1)
135  printf("addr <= 0x%x && ", 0x1000 - 4*n_regs);
136  break;
137  }
138  printf("page != NULL) {\n");
139 
140  printf("\t\tuint32_t *p = (uint32_t *) (page + addr);\n");
141 
142  if (u) {
143  x = 0;
144  for (i=0; i<=15; i++) {
145  if (!(opcode & (1 << i)))
146  continue;
147 
148  if (load && w && i == r) {
149  /* Skip the load if we're using writeback. */
150  } else if (load) {
151  if (i == 15)
152  printf("\t\tcpu->pc = p[%i];\n", x);
153  else
154  printf("\t\tcpu->cd.arm.r[%i] = "
155  "p[%i];\n", i, x);
156  } else {
157  if (i == 15)
158  printf("\t\tp[%i] = tmp_pc;\n", x);
159  else
160  printf("\t\tp[%i] = cpu->cd.arm.r"
161  "[%i];\n", x, i);
162  }
163 
164  x ++;
165  }
166  } else {
167  /* Decrementing, but do it incrementing anyway: */
168  x = -n_regs;
169  for (i=0; i<=15; i++) {
170  if (!(opcode & (1 << i)))
171  continue;
172 
173  x ++;
174 
175  if (load && w && i == r) {
176  /* Skip the load if we're using writeback. */
177  } else if (load) {
178  if (i == 15)
179  printf("\t\tcpu->pc = p[%i];\n", x);
180  else
181  printf("\t\tcpu->cd.arm.r[%i] = "
182  "p[%i];\n", i, x);
183  } else {
184  if (i == 15)
185  printf("\t\tp[%i] = tmp_pc;\n", x);
186  else
187  printf("\t\tp[%i] = "
188  "cpu->cd.arm.r[%i];\n", x, i);
189  }
190  }
191  }
192 
193  if (w)
194  printf("\t\tcpu->cd.arm.r[%i] %s %i;\n",
195  r, u? "+=" : "-=", 4*n_regs);
196 
197  if (load && opcode & 0x8000) {
198  printf("\t\tquick_pc_to_pointers(cpu);\n");
199  }
200 
201  printf("\t} else\n");
202  printf("\t\tinstr(bdt_%s)(cpu, ic);\n", load? "load" : "store");
203 
204  printf("}\nY(multi_0x%08"PRIx32")\n", opcode);
205 }
206 
207 
208 /*
209  * main():
210  *
211  * Normal ARM code seems to only use about a few hundred of the 1^24 possible
212  * load/store multiple instructions. (I'm not counting the s-bit now.)
213  * Instead of having a linear array of 100s of entries, we can select a list
214  * to scan based on a few bits (*), and those lists will be shorter.
215  *
216  * (*) By running experiment_arm_multi.c on statistics gathered from running
217  * NetBSD/cats, it seems that choosing the following 8 bits results in
218  * the shortest linear lists:
219  *
220  * xxxx100P USWLnnnn llllllll llllllll
221  * ^ ^ ^ ^ ^ ^ ^ ^ (0x00950154)
222  */
223 int main(int argc, char *argv[])
224 {
225  int i, j;
226  int n_used[256];
227 
228  if (argc < 2) {
229  fprintf(stderr, "usage: %s opcode [..]\n", argv[0]);
230  exit(1);
231  }
232 
233  printf("\n/* AUTOMATICALLY GENERATED! Do not edit. */\n\n"
234  "#include <stdio.h>\n"
235  "#include <stdlib.h>\n"
236  "#include \"cpu.h\"\n"
237  "#include \"misc.h\"\n"
238  "#define DYNTRANS_PC_TO_POINTERS arm_pc_to_pointers\n"
239  "#include \"quick_pc_to_pointers.h\"\n"
240  "#include \"arm_tmphead_1.h\"\n"
241  "\n#define instr(x) arm_instr_ ## x\n");
242  printf("extern void arm_pc_to_pointers(struct cpu *);\n");
243  printf("extern void arm_instr_nop(struct cpu *, "
244  "struct arm_instr_call *);\n");
245  printf("extern void arm_instr_bdt_load(struct cpu *, "
246  "struct arm_instr_call *);\n");
247  printf("extern void arm_instr_bdt_store(struct cpu *, "
248  "struct arm_instr_call *);\n");
249  printf("\n\n");
250 
251  /* Generate the opcode functions: */
252  for (i=1; i<argc; i++)
253  generate_opcode(strtol(argv[i], NULL, 0));
254 
255  /* Generate 256 small lookup tables: */
256  for (j=0; j<256; j++) {
257  int n = 0, zz, zz0;
258  for (i=1; i<argc; i++) {
259  zz = strtol(argv[i], NULL, 0);
260  zz = ((zz & 0x00800000) >> 16)
261  |((zz & 0x00100000) >> 14)
262  |((zz & 0x00040000) >> 13)
263  |((zz & 0x00010000) >> 12)
264  |((zz & 0x00000100) >> 5)
265  |((zz & 0x00000040) >> 4)
266  |((zz & 0x00000010) >> 3)
267  |((zz & 0x00000004) >> 2);
268  if (zz == j)
269  n++;
270  }
271  printf("\nuint32_t multi_opcode_%i[%i] = {\n", j, n+1);
272  for (i=1; i<argc; i++) {
273  zz = zz0 = strtol(argv[i], NULL, 0);
274  zz = ((zz & 0x00800000) >> 16)
275  |((zz & 0x00100000) >> 14)
276  |((zz & 0x00040000) >> 13)
277  |((zz & 0x00010000) >> 12)
278  |((zz & 0x00000100) >> 5)
279  |((zz & 0x00000040) >> 4)
280  |((zz & 0x00000010) >> 3)
281  |((zz & 0x00000004) >> 2);
282  if (zz == j)
283  printf("\t0x%08x,\n", zz0);
284  }
285  printf("0 };\n");
286  }
287 
288  /* Generate 256 tables with function pointers: */
289  for (j=0; j<256; j++) {
290  int n = 0, zz, zz0;
291  for (i=1; i<argc; i++) {
292  zz = strtol(argv[i], NULL, 0);
293  zz = ((zz & 0x00800000) >> 16)
294  |((zz & 0x00100000) >> 14)
295  |((zz & 0x00040000) >> 13)
296  |((zz & 0x00010000) >> 12)
297  |((zz & 0x00000100) >> 5)
298  |((zz & 0x00000040) >> 4)
299  |((zz & 0x00000010) >> 3)
300  |((zz & 0x00000004) >> 2);
301  if (zz == j)
302  n++;
303  }
304  n_used[j] = n;
305  if (n == 0)
306  continue;
307  printf("void (*multi_opcode_f_%i[%i])(struct cpu *,"
308  " struct arm_instr_call *) = {\n", j, n*16);
309  for (i=1; i<argc; i++) {
310  zz = zz0 = strtol(argv[i], NULL, 0);
311  zz = ((zz & 0x00800000) >> 16)
312  |((zz & 0x00100000) >> 14)
313  |((zz & 0x00040000) >> 13)
314  |((zz & 0x00010000) >> 12)
315  |((zz & 0x00000100) >> 5)
316  |((zz & 0x00000040) >> 4)
317  |((zz & 0x00000010) >> 3)
318  |((zz & 0x00000004) >> 2);
319  if (zz == j) {
320  printf("\tarm_instr_multi_0x%08x__eq,\n", zz0);
321  printf("\tarm_instr_multi_0x%08x__ne,\n", zz0);
322  printf("\tarm_instr_multi_0x%08x__cs,\n", zz0);
323  printf("\tarm_instr_multi_0x%08x__cc,\n", zz0);
324  printf("\tarm_instr_multi_0x%08x__mi,\n", zz0);
325  printf("\tarm_instr_multi_0x%08x__pl,\n", zz0);
326  printf("\tarm_instr_multi_0x%08x__vs,\n", zz0);
327  printf("\tarm_instr_multi_0x%08x__vc,\n", zz0);
328  printf("\tarm_instr_multi_0x%08x__hi,\n", zz0);
329  printf("\tarm_instr_multi_0x%08x__ls,\n", zz0);
330  printf("\tarm_instr_multi_0x%08x__ge,\n", zz0);
331  printf("\tarm_instr_multi_0x%08x__lt,\n", zz0);
332  printf("\tarm_instr_multi_0x%08x__gt,\n", zz0);
333  printf("\tarm_instr_multi_0x%08x__le,\n", zz0);
334  printf("\tarm_instr_multi_0x%08x,\n", zz0);
335  printf("\tarm_instr_nop,\n");
336  }
337  }
338  printf("};\n");
339  }
340 
341 
342  printf("\nuint32_t *multi_opcode[256] = {\n");
343  for (i=0; i<256; i++) {
344  printf(" multi_opcode_%i,", i);
345  if ((i % 4) == 0)
346  printf("\n");
347  }
348  printf("};\n");
349 
350  printf("\nvoid (**multi_opcode_f[256])(struct cpu *,"
351  " struct arm_instr_call *) = {\n");
352  for (i=0; i<256; i++) {
353  if (n_used[i] > 0)
354  printf(" multi_opcode_f_%i,", i);
355  else
356  printf(" NULL,");
357  if ((i % 4) == 0)
358  printf("\n");
359  }
360  printf("};\n");
361 
362  return 0;
363 }
364 
int main(int argc, char *argv[])
void generate_opcode(uint32_t opcode)
void load(FILE *fh, unsigned char *ptr, unsigned long sz)

Generated on Sun Sep 30 2018 16:05:18 for GXemul by doxygen 1.8.13