/[gxemul]/trunk/src/cpu_arm_instr.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/cpu_arm_instr.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (12 years, 1 month ago) by dpavlin
File MIME type: text/plain
File size: 30015 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.905 2005/08/16 09:16:24 debug Exp $
20050628	Continuing the work on the ARM translation engine. end_of_page
		works. Experimenting with load/store translation caches
		(virtual -> physical -> host).
20050629	More ARM stuff (memory access translation cache, mostly). This
		might break a lot of stuff elsewhere, probably some MIPS-
		related translation things.
20050630	Many load/stores are now automatically generated and included
		into cpu_arm_instr.c; 1024 functions in total (!).
		Fixes based on feedback from Alec Voropay: only print 8 hex
		digits instead of 16 in some cases when emulating 32-bit
		machines; similar 8 vs 16 digit fix for breakpoint addresses;
		4Kc has 16 TLB entries, not 48; the MIPS config select1
		register is now printed with "reg ,0".
		Also changing many other occurances of 16 vs 8 digit output.
		Adding cache associativity fields to mips_cpu_types.h; updating
		some other cache fields; making the output of
		mips_cpu_dumpinfo() look nicer.
		Generalizing the bintrans stuff for device accesses to also
		work with the new translation system. (This might also break
		some MIPS things.)
		Adding multi-load/store instructions to the ARM disassembler
		and the translator, and some optimizations of various kinds.
20050701	Adding a simple dev_disk (it can read/write sectors from
		disk images).
20050712	Adding dev_ether (a simple ethernet send/receive device).
		Debugger command "ninstrs" for toggling show_nr_of_instructions
		during runtime.
		Removing the framebuffer logo.
20050713	Continuing on dev_ether.
		Adding a dummy cpu_alpha (again).
20050714	More work on cpu_alpha.
20050715	More work on cpu_alpha. Many instructions work, enough to run
		a simple framebuffer fill test (similar to the ARM test).
20050716	More Alpha stuff.
20050717	Minor updates (Alpha stuff).
20050718	Minor updates (Alpha stuff).
20050719	Generalizing some Alpha instructions.
20050720	More Alpha-related updates.
20050721	Continuing on cpu_alpha. Importing rpb.h from NetBSD/alpha.
20050722	Alpha-related updates: userland stuff (Hello World using
		write() compiled statically for FreeBSD/Alpha runs fine), and
		more instructions are now implemented.
20050723	Fixing ldq_u and stq_u.
		Adding more instructions (conditional moves, masks, extracts,
		shifts).
20050724	More FreeBSD/Alpha userland stuff, and adding some more
		instructions (inserts).
20050725	Continuing on the Alpha stuff. (Adding dummy ldt/stt.)
		Adding a -A command line option to turn off alignment checks
		in some cases (for translated code).
		Trying to remove the old bintrans code which updated the pc
		and nr_of_executed_instructions for every instruction.
20050726	Making another attempt att removing the pc/nr of instructions
		code. This time it worked, huge performance increase for
		artificial test code, but performance loss for real-world
		code :-( so I'm scrapping that code for now.
		Tiny performance increase on Alpha (by using ret instead of
		jmp, to play nice with the Alpha's branch prediction) for the
		old MIPS bintrans backend.
20050727	Various minor fixes and cleanups.
20050728	Switching from a 2-level virtual to host/physical translation
		system for ARM emulation, to a 1-level translation.
		Trying to switch from 2-level to 1-level for the MIPS bintrans
		system as well (Alpha only, so far), but there is at least one
		problem: caches and/or how they work with device mappings.
20050730	Doing the 2-level to 1-level conversion for the i386 backend.
		The cache/device bug is still there for R2K/3K :(
		Various other minor updates (Malta etc).
		The mc146818 clock now updates the UIP bit in a way which works
		better with Linux for at least sgimips and Malta emulation.
		Beginning the work on refactoring the dyntrans system.
20050731	Continuing the dyntrans refactoring.
		Fixing a small but serious host alignment bug in memory_rw.
		Adding support for big-endian load/stores to the i386 bintrans
		backend.
		Another minor i386 bintrans backend update: stores from the
		zero register are now one (or two) loads shorter.
		The slt and sltu instructions were incorrectly implemented for
		the i386 backend; only using them for 32-bit mode for now.
20050801	Continuing the dyntrans refactoring.
		Cleanup of the ns16550 serial controller (removing unnecessary
		code).
		Bugfix (memory corruption bug) in dev_gt, and a patch/hack from
		Alec Voropay for Linux/Malta.
20050802	More cleanup/refactoring of the dyntrans subsystem: adding
		phys_page pointers to the lookup tables, for quick jumps
		between translated pages.
		Better fix for the ns16550 device (but still no real FIFO
		functionality).
		Converting cpu_ppc to the new dyntrans system. This means that
		I will have to start from scratch with implementing each
		instruction, and figure out how to implement dual 64/32-bit
		modes etc.
		Removing the URISC CPU family, because it was useless.
20050803	When selecting a machine type, the main type can now be omitted
		if the subtype name is unique. (I.e. -E can be omitted.)
		Fixing a dyntrans/device update bug. (Writes to offset 0 of
		a device could sometimes go unnoticed.)
		Adding an experimental "instruction combination" hack for
		ARM for memset-like byte fill loops.
20050804	Minor progress on cpu_alpha and related things.
		Finally fixing the MIPS dmult/dmultu bugs.
		Fixing some minor TODOs.
20050805	Generalizing the 8259 PIC. It now also works with Cobalt
		and evbmips emulation, in addition to the x86 hack.
		Finally converting the ns16550 device to use devinit.
		Continuing the work on the dyntrans system. Thinking about
		how to add breakpoints.
20050806	More dyntrans updates. Breakpoints seem to work now.
20050807	Minor updates: cpu_alpha and related things; removing
		dev_malta (as it isn't used any more).
		Dyntrans: working on general "show trace tree" support.
		The trace tree stuff now works with both the old MIPS code and
		with newer dyntrans modes. :)
		Continuing on Alpha-related stuff (trying to get *BSD to boot
		a bit further, adding more instructions, etc).
20050808	Adding a dummy IA64 cpu family, and continuing the refactoring
		of the dyntrans system.
		Removing the regression test stuff, because it was more or
		less useless.
		Adding loadlinked/storeconditional type instructions to the
		Alpha emulation. (Needed for Linux/alpha. Not very well tested
		yet.)
20050809	The function call trace tree now prints a per-function nr of
		arguments. (Semi-meaningless, since that data isn't read yet
		from the ELFs; some hardcoded symbols such as memcpy() and
		strlen() work fine, though.)
		More dyntrans refactoring; taking out more of the things that
		are common to all cpu families.
20050810	Working on adding support for "dual mode" for PPC dyntrans
		(i.e. both 64-bit and 32-bit modes).
		(Re)adding some simple PPC instructions.
20050811	Adding a dummy M68K cpu family. The dyntrans system isn't ready
		for variable-length ISAs yet, so it's completely bogus so far.
		Re-adding more PPC instructions.
		Adding a hack to src/file.c which allows OpenBSD/mac68k a.out
		kernels to be loaded.
		Beginning to add PPC loads/stores. So far they only work in
		32-bit mode.
20050812	The configure file option "add_remote" now accepts symbolic
		host names, in addition to numeric IPv4 addresses.
		Re-adding more PPC instructions.
20050814	Continuing to port back more PPC instructions.
		Found and fixed the cache/device write-update bug for 32-bit
		MIPS bintrans. :-)
		Triggered a really weird and annoying bug in Compaq's C
		compiler; ccc sometimes outputs code which loads from an
		address _before_ checking whether the pointer was NULL or not.
		(I'm not sure how to handle this problem.)
20050815	Removing all of the old x86 instruction execution code; adding
		a new (dummy) dyntrans module for x86.
		Taking the first steps to extend the dyntrans system to support
		variable-length instructions.
		Slowly preparing for the next release.
20050816	Adding a dummy SPARC cpu module.
		Minor updates (documentation etc) for the release.

==============  RELEASE 0.3.5  ==============


1 /*
2 * Copyright (C) 2005 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 * $Id: cpu_arm_instr.c,v 1.51 2005/08/16 05:37:10 debug Exp $
29 *
30 * ARM instructions.
31 *
32 * Individual functions should keep track of cpu->n_translated_instrs.
33 * (If no instruction was executed, then it should be decreased. If, say, 4
34 * instructions were combined into one function and executed, then it should
35 * be increased by 3.)
36 */
37
38
39 /*
40 * Helper definitions:
41 *
42 * Each instruction is defined like this:
43 *
44 * X(foo)
45 * {
46 * code for foo;
47 * }
48 * Y(foo)
49 *
50 * The Y macro defines 14 copies of the instruction, one for each possible
51 * condition code. (The NV condition code is not included, and the AL code
52 * uses the main foo function.) Y also defines an array with pointers to
53 * all of these functions.
54 */
55
56 #define Y(n) void arm_instr_ ## n ## __eq(struct cpu *cpu, \
57 struct arm_instr_call *ic) \
58 { if (cpu->cd.arm.flags & ARM_FLAG_Z) \
59 arm_instr_ ## n (cpu, ic); } \
60 void arm_instr_ ## n ## __ne(struct cpu *cpu, \
61 struct arm_instr_call *ic) \
62 { if (!(cpu->cd.arm.flags & ARM_FLAG_Z)) \
63 arm_instr_ ## n (cpu, ic); } \
64 void arm_instr_ ## n ## __cs(struct cpu *cpu, \
65 struct arm_instr_call *ic) \
66 { if (cpu->cd.arm.flags & ARM_FLAG_C) \
67 arm_instr_ ## n (cpu, ic); } \
68 void arm_instr_ ## n ## __cc(struct cpu *cpu, \
69 struct arm_instr_call *ic) \
70 { if (!(cpu->cd.arm.flags & ARM_FLAG_C)) \
71 arm_instr_ ## n (cpu, ic); } \
72 void arm_instr_ ## n ## __mi(struct cpu *cpu, \
73 struct arm_instr_call *ic) \
74 { if (cpu->cd.arm.flags & ARM_FLAG_N) \
75 arm_instr_ ## n (cpu, ic); } \
76 void arm_instr_ ## n ## __pl(struct cpu *cpu, \
77 struct arm_instr_call *ic) \
78 { if (!(cpu->cd.arm.flags & ARM_FLAG_N)) \
79 arm_instr_ ## n (cpu, ic); } \
80 void arm_instr_ ## n ## __vs(struct cpu *cpu, \
81 struct arm_instr_call *ic) \
82 { if (cpu->cd.arm.flags & ARM_FLAG_V) \
83 arm_instr_ ## n (cpu, ic); } \
84 void arm_instr_ ## n ## __vc(struct cpu *cpu, \
85 struct arm_instr_call *ic) \
86 { if (!(cpu->cd.arm.flags & ARM_FLAG_V)) \
87 arm_instr_ ## n (cpu, ic); } \
88 void arm_instr_ ## n ## __hi(struct cpu *cpu, \
89 struct arm_instr_call *ic) \
90 { if (cpu->cd.arm.flags & ARM_FLAG_C && \
91 !(cpu->cd.arm.flags & ARM_FLAG_Z)) \
92 arm_instr_ ## n (cpu, ic); } \
93 void arm_instr_ ## n ## __ls(struct cpu *cpu, \
94 struct arm_instr_call *ic) \
95 { if (cpu->cd.arm.flags & ARM_FLAG_Z && \
96 !(cpu->cd.arm.flags & ARM_FLAG_C)) \
97 arm_instr_ ## n (cpu, ic); } \
98 void arm_instr_ ## n ## __ge(struct cpu *cpu, \
99 struct arm_instr_call *ic) \
100 { if (((cpu->cd.arm.flags & ARM_FLAG_N)?1:0) == \
101 ((cpu->cd.arm.flags & ARM_FLAG_V)?1:0)) \
102 arm_instr_ ## n (cpu, ic); } \
103 void arm_instr_ ## n ## __lt(struct cpu *cpu, \
104 struct arm_instr_call *ic) \
105 { if (((cpu->cd.arm.flags & ARM_FLAG_N)?1:0) != \
106 ((cpu->cd.arm.flags & ARM_FLAG_V)?1:0)) \
107 arm_instr_ ## n (cpu, ic); } \
108 void arm_instr_ ## n ## __gt(struct cpu *cpu, \
109 struct arm_instr_call *ic) \
110 { if (((cpu->cd.arm.flags & ARM_FLAG_N)?1:0) == \
111 ((cpu->cd.arm.flags & ARM_FLAG_V)?1:0) && \
112 !(cpu->cd.arm.flags & ARM_FLAG_Z)) \
113 arm_instr_ ## n (cpu, ic); } \
114 void arm_instr_ ## n ## __le(struct cpu *cpu, \
115 struct arm_instr_call *ic) \
116 { if (((cpu->cd.arm.flags & ARM_FLAG_N)?1:0) != \
117 ((cpu->cd.arm.flags & ARM_FLAG_V)?1:0) || \
118 (cpu->cd.arm.flags & ARM_FLAG_Z)) \
119 arm_instr_ ## n (cpu, ic); } \
120 void (*arm_cond_instr_ ## n [16])(struct cpu *, \
121 struct arm_instr_call *) = { \
122 arm_instr_ ## n ## __eq, arm_instr_ ## n ## __ne, \
123 arm_instr_ ## n ## __cs, arm_instr_ ## n ## __cc, \
124 arm_instr_ ## n ## __mi, arm_instr_ ## n ## __pl, \
125 arm_instr_ ## n ## __vs, arm_instr_ ## n ## __vc, \
126 arm_instr_ ## n ## __hi, arm_instr_ ## n ## __ls, \
127 arm_instr_ ## n ## __ge, arm_instr_ ## n ## __lt, \
128 arm_instr_ ## n ## __gt, arm_instr_ ## n ## __le, \
129 arm_instr_ ## n , arm_instr_nop };
130
131 #define cond_instr(n) ( arm_cond_instr_ ## n [condition_code] )
132
133
134 /*****************************************************************************/
135
136
137 /*
138 * nop: Do nothing.
139 */
140 X(nop)
141 {
142 }
143
144
145 /*
146 * b: Branch (to a different translated page)
147 *
148 * arg[0] = relative offset
149 */
150 X(b)
151 {
152 uint32_t low_pc;
153
154 /* Calculate new PC from this instruction + arg[0] */
155 low_pc = ((size_t)ic - (size_t)
156 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
157 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
158 cpu->cd.arm.r[ARM_PC] += (low_pc << 2);
159 cpu->cd.arm.r[ARM_PC] += (int32_t)ic->arg[0];
160 cpu->pc = cpu->cd.arm.r[ARM_PC];
161
162 /* Find the new physical page and update the translation pointers: */
163 arm_pc_to_pointers(cpu);
164 }
165 Y(b)
166
167
168 /*
169 * b_samepage: Branch (to within the same translated page)
170 *
171 * arg[0] = pointer to new arm_instr_call
172 */
173 X(b_samepage)
174 {
175 cpu->cd.arm.next_ic = (struct arm_instr_call *) ic->arg[0];
176 }
177 Y(b_samepage)
178
179
180 /*
181 * bl: Branch and Link (to a different translated page)
182 *
183 * arg[0] = relative address
184 */
185 X(bl)
186 {
187 uint32_t lr, low_pc;
188
189 /* Figure out what the return (link) address will be: */
190 low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t)
191 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
192 lr = cpu->cd.arm.r[ARM_PC];
193 lr &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
194 lr += (low_pc << 2);
195
196 /* Link: */
197 cpu->cd.arm.r[ARM_LR] = lr;
198
199 /* Calculate new PC from this instruction + arg[0] */
200 cpu->pc = cpu->cd.arm.r[ARM_PC] = lr - 4 + (int32_t)ic->arg[0];
201
202 /* Find the new physical page and update the translation pointers: */
203 arm_pc_to_pointers(cpu);
204 }
205 Y(bl)
206
207
208 /*
209 * bl_trace: Branch and Link (to a different translated page), with trace
210 *
211 * Same as for bl.
212 */
213 X(bl_trace)
214 {
215 uint32_t lr, low_pc;
216
217 /* Figure out what the return (link) address will be: */
218 low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t)
219 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
220 lr = cpu->cd.arm.r[ARM_PC];
221 lr &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
222 lr += (low_pc << 2);
223
224 /* Link: */
225 cpu->cd.arm.r[ARM_LR] = lr;
226
227 /* Calculate new PC from this instruction + arg[0] */
228 cpu->pc = cpu->cd.arm.r[ARM_PC] = lr - 4 + (int32_t)ic->arg[0];
229
230 cpu_functioncall_trace(cpu, cpu->pc);
231
232 /* Find the new physical page and update the translation pointers: */
233 arm_pc_to_pointers(cpu);
234 }
235 Y(bl_trace)
236
237
238 /*
239 * bl_samepage: A branch + link within the same page
240 *
241 * arg[0] = pointer to new arm_instr_call
242 */
243 X(bl_samepage)
244 {
245 uint32_t lr, low_pc;
246
247 /* Figure out what the return (link) address will be: */
248 low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t)
249 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
250 lr = cpu->cd.arm.r[ARM_PC];
251 lr &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
252 lr += (low_pc << 2);
253
254 /* Link: */
255 cpu->cd.arm.r[ARM_LR] = lr;
256
257 /* Branch: */
258 cpu->cd.arm.next_ic = (struct arm_instr_call *) ic->arg[0];
259 }
260 Y(bl_samepage)
261
262
263 /*
264 * bl_samepage_trace: Branch and Link (to the same page), with trace
265 *
266 * Same as for bl_samepage.
267 */
268 X(bl_samepage_trace)
269 {
270 uint32_t tmp_pc, lr, low_pc;
271
272 /* Figure out what the return (link) address will be: */
273 low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t)
274 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
275 lr = cpu->cd.arm.r[ARM_PC];
276 lr &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
277 lr += (low_pc << 2);
278
279 /* Link: */
280 cpu->cd.arm.r[ARM_LR] = lr;
281
282 /* Branch: */
283 cpu->cd.arm.next_ic = (struct arm_instr_call *) ic->arg[0];
284
285 low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t)
286 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
287 tmp_pc = cpu->cd.arm.r[ARM_PC];
288 tmp_pc &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
289 tmp_pc += (low_pc << 2);
290 cpu_functioncall_trace(cpu, tmp_pc);
291 }
292 Y(bl_samepage_trace)
293
294
295 /*
296 * mov_pc: "mov pc,reg"
297 *
298 * arg[0] = pointer to uint32_t in host memory of source register
299 */
300 X(mov_pc)
301 {
302 uint32_t old_pc = cpu->cd.arm.r[ARM_PC];
303 uint32_t mask_within_page = ((ARM_IC_ENTRIES_PER_PAGE-1) << 2) | 3;
304
305 /* Update the PC register: */
306 cpu->pc = cpu->cd.arm.r[ARM_PC] = *((uint32_t *)ic->arg[0]);
307
308 /*
309 * Is this a return to code within the same page? Then there is no
310 * need to update all pointers, just next_ic.
311 */
312 if ((old_pc & ~mask_within_page) == (cpu->pc & ~mask_within_page)) {
313 cpu->cd.arm.next_ic = cpu->cd.arm.cur_ic_page +
314 ((cpu->pc & mask_within_page) >> 2);
315 } else {
316 /* Find the new physical page and update pointers: */
317 arm_pc_to_pointers(cpu);
318 }
319 }
320 Y(mov_pc)
321
322
323 /*
324 * ret_trace: "mov pc,lr" with trace enabled
325 *
326 * arg[0] = ignored (similar to mov_pc above)
327 */
328 X(ret_trace)
329 {
330 uint32_t old_pc = cpu->cd.arm.r[ARM_PC];
331 uint32_t mask_within_page = ((ARM_IC_ENTRIES_PER_PAGE-1) << 2) | 3;
332
333 /* Update the PC register: */
334 cpu->pc = cpu->cd.arm.r[ARM_PC] = cpu->cd.arm.r[ARM_LR];
335
336 cpu_functioncall_trace_return(cpu);
337
338 /*
339 * Is this a return to code within the same page? Then there is no
340 * need to update all pointers, just next_ic.
341 */
342 if ((old_pc & ~mask_within_page) == (cpu->pc & ~mask_within_page)) {
343 cpu->cd.arm.next_ic = cpu->cd.arm.cur_ic_page +
344 ((cpu->pc & mask_within_page) >> 2);
345 } else {
346 /* Find the new physical page and update pointers: */
347 arm_pc_to_pointers(cpu);
348 }
349 }
350 Y(ret_trace)
351
352
353 /*
354 * mov_regreg:
355 *
356 * arg[0] = pointer to uint32_t in host memory of destination register
357 * arg[1] = pointer to uint32_t in host memory of source register
358 */
359 X(mov_regreg)
360 {
361 *((uint32_t *)ic->arg[0]) = *((uint32_t *)ic->arg[1]);
362 }
363 Y(mov_regreg)
364
365
366 /*
367 * mov: Set a 32-bit register to a 32-bit value.
368 *
369 * arg[0] = pointer to uint32_t in host memory
370 * arg[1] = 32-bit value
371 */
372 X(mov)
373 {
374 *((uint32_t *)ic->arg[0]) = ic->arg[1];
375 }
376 Y(mov)
377
378
379 /*
380 * clear: Set a 32-bit register to 0. (A "mov" to fixed value zero.)
381 *
382 * arg[0] = pointer to uint32_t in host memory
383 */
384 X(clear)
385 {
386 *((uint32_t *)ic->arg[0]) = 0;
387 }
388 Y(clear)
389
390
391 #include "tmp_arm_include.c"
392
393
394 #define A__NAME arm_instr_store_w0_byte_u1_p0_imm_fixinc1
395 #define A__NAME__general arm_instr_store_w0_byte_u1_p0_imm_fixinc1__general
396 #define A__B
397 #define A__U
398 #define A__NOCONDITIONS
399 #define A__FIXINC 1
400 #include "cpu_arm_instr_loadstore.c"
401 #undef A__NOCONDITIONS
402 #undef A__B
403 #undef A__U
404 #undef A__NAME__general
405 #undef A__NAME
406
407
408 /*
409 * load_byte_imm_pcrel:
410 * Like load_byte_imm, but the source address is the PC register.
411 * Before loading, we have to synchronize the PC register and add 8.
412 *
413 * arg[0] = pointer to ARM_PC (not used here)
414 * arg[1] = 32-bit offset
415 * arg[2] = pointer to uint32_t in host memory where to store the value
416 */
417 X(load_byte_imm_pcrel)
418 {
419 uint32_t low_pc, addr;
420 unsigned char data[1];
421
422 low_pc = ((size_t)ic - (size_t)
423 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
424 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
425 cpu->cd.arm.r[ARM_PC] += (low_pc << 2);
426
427 addr = cpu->cd.arm.r[ARM_PC] + 8 + ic->arg[1];
428 if (!cpu->memory_rw(cpu, cpu->mem, addr, data, sizeof(data),
429 MEM_READ, CACHE_DATA)) {
430 fatal("load failed: TODO\n");
431 exit(1);
432 }
433 *((uint32_t *)ic->arg[2]) = data[0];
434 }
435 Y(load_byte_imm_pcrel)
436
437
438 /*
439 * load_word_imm_pcrel:
440 * Like load_word_imm, but the source address is the PC register.
441 * Before loading, we have to synchronize the PC register and add 8.
442 *
443 * arg[0] = pointer to ARM_PC (not used here)
444 * arg[1] = 32-bit offset
445 * arg[2] = pointer to uint32_t in host memory where to store the value
446 */
447 X(load_word_imm_pcrel)
448 {
449 uint32_t low_pc, addr;
450 unsigned char data[sizeof(uint32_t)];
451
452 low_pc = ((size_t)ic - (size_t)
453 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
454 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
455 cpu->cd.arm.r[ARM_PC] += (low_pc << 2);
456
457 addr = cpu->cd.arm.r[ARM_PC] + 8 + ic->arg[1];
458 if (!cpu->memory_rw(cpu, cpu->mem, addr, data, sizeof(data),
459 MEM_READ, CACHE_DATA)) {
460 fatal("load failed: TODO\n");
461 exit(1);
462 }
463 /* TODO: Big endian */
464 *((uint32_t *)ic->arg[2]) = data[0] + (data[1] << 8) +
465 (data[2] << 16) + (data[3] << 24);
466 }
467 Y(load_word_imm_pcrel)
468
469
470 /*
471 * bdt_load: Block Data Transfer, Load
472 *
473 * arg[0] = pointer to uint32_t in host memory, pointing to the base register
474 * arg[1] = 32-bit instruction word. Most bits are read from this.
475 */
476 X(bdt_load)
477 {
478 unsigned char data[4];
479 uint32_t *np = (uint32_t *)ic->arg[0];
480 uint32_t addr = *np;
481 uint32_t iw = ic->arg[1]; /* xxxx100P USWLnnnn llllllll llllllll */
482 int p_bit = iw & 0x01000000;
483 int u_bit = iw & 0x00800000;
484 int s_bit = iw & 0x00400000;
485 int w_bit = iw & 0x00200000;
486 int i;
487
488 if (s_bit) {
489 fatal("bdt: TODO: s-bit\n");
490 exit(1);
491 }
492
493 for (i=(u_bit? 0 : 15); i>=0 && i<=15; i+=(u_bit? 1 : -1))
494 if ((iw >> i) & 1) {
495 /* Load register i: */
496 if (p_bit) {
497 if (u_bit)
498 addr += sizeof(uint32_t);
499 else
500 addr -= sizeof(uint32_t);
501 }
502 if (!cpu->memory_rw(cpu, cpu->mem, addr, data,
503 sizeof(data), MEM_READ, CACHE_DATA)) {
504 fatal("bdt: load failed: TODO\n");
505 exit(1);
506 }
507 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
508 cpu->cd.arm.r[i] = data[0] +
509 (data[1] << 8) + (data[2] << 16)
510 + (data[3] << 24);
511 } else {
512 cpu->cd.arm.r[i] = data[3] +
513 (data[2] << 8) + (data[1] << 16)
514 + (data[0] << 24);
515 }
516 /* NOTE: Special case: */
517 if (i == ARM_PC) {
518 cpu->cd.arm.r[ARM_PC] &= ~3;
519 cpu->pc = cpu->cd.arm.r[ARM_PC];
520 /* TODO: There is no need to update the
521 pointers if this is a return to the
522 same page! */
523 /* Find the new physical page and update the
524 translation pointers: */
525 arm_pc_to_pointers(cpu);
526 }
527 if (!p_bit) {
528 if (u_bit)
529 addr += sizeof(uint32_t);
530 else
531 addr -= sizeof(uint32_t);
532 }
533 }
534
535 if (w_bit)
536 *np = addr;
537 }
538 Y(bdt_load)
539
540
541 /*
542 * bdt_store: Block Data Transfer, Store
543 *
544 * arg[0] = pointer to uint32_t in host memory, pointing to the base register
545 * arg[1] = 32-bit instruction word. Most bits are read from this.
546 */
547 X(bdt_store)
548 {
549 unsigned char data[4];
550 uint32_t *np = (uint32_t *)ic->arg[0];
551 uint32_t addr = *np;
552 uint32_t iw = ic->arg[1]; /* xxxx100P USWLnnnn llllllll llllllll */
553 int p_bit = iw & 0x01000000;
554 int u_bit = iw & 0x00800000;
555 int s_bit = iw & 0x00400000;
556 int w_bit = iw & 0x00200000;
557 int i;
558
559 if (s_bit) {
560 fatal("bdt: TODO: s-bit\n");
561 exit(1);
562 }
563
564 if (iw & 0x8000) {
565 /* Synchronize the program counter: */
566 uint32_t low_pc = ((size_t)ic - (size_t)
567 cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call);
568 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
569 cpu->cd.arm.r[ARM_PC] += (low_pc << 2);
570 cpu->pc = cpu->cd.arm.r[ARM_PC];
571 }
572
573 for (i=(u_bit? 0 : 15); i>=0 && i<=15; i+=(u_bit? 1 : -1))
574 if ((iw >> i) & 1) {
575 /* Store register i: */
576 uint32_t value = cpu->cd.arm.r[i];
577 if (i == ARM_PC)
578 value += 12; /* TODO: 8 on some ARMs? */
579 if (p_bit) {
580 if (u_bit)
581 addr += sizeof(uint32_t);
582 else
583 addr -= sizeof(uint32_t);
584 }
585 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
586 data[0] = value;
587 data[1] = value >> 8;
588 data[2] = value >> 16;
589 data[3] = value >> 24;
590 } else {
591 data[0] = value >> 24;
592 data[1] = value >> 16;
593 data[2] = value >> 8;
594 data[3] = value;
595 }
596 if (!cpu->memory_rw(cpu, cpu->mem, addr, data,
597 sizeof(data), MEM_WRITE, CACHE_DATA)) {
598 fatal("bdt: store failed: TODO\n");
599 exit(1);
600 }
601 if (!p_bit) {
602 if (u_bit)
603 addr += sizeof(uint32_t);
604 else
605 addr -= sizeof(uint32_t);
606 }
607 }
608
609 if (w_bit)
610 *np = addr;
611 }
612 Y(bdt_store)
613
614
615 /*
616 * cmps: Compare a 32-bit register to a 32-bit value. (Subtraction.)
617 *
618 * arg[0] = pointer to uint32_t in host memory
619 * arg[1] = 32-bit value
620 */
621 X(cmps)
622 {
623 uint32_t a, b, c;
624 int v, n;
625 a = *((uint32_t *)ic->arg[0]);
626 b = ic->arg[1];
627
628 cpu->cd.arm.flags &=
629 ~(ARM_FLAG_Z | ARM_FLAG_N | ARM_FLAG_V | ARM_FLAG_C);
630 c = a - b;
631 if (a > b)
632 cpu->cd.arm.flags |= ARM_FLAG_C;
633 if (c == 0)
634 cpu->cd.arm.flags |= ARM_FLAG_Z;
635 if ((int32_t)c < 0) {
636 cpu->cd.arm.flags |= ARM_FLAG_N;
637 n = 1;
638 } else
639 n = 0;
640 if ((int32_t)a >= (int32_t)b)
641 v = n;
642 else
643 v = !n;
644 if (v)
645 cpu->cd.arm.flags |= ARM_FLAG_V;
646 }
647 Y(cmps)
648
649
650 #include "cpu_arm_instr_cmps.c"
651
652
653 /*
654 * sub: Subtract an immediate value from a 32-bit word, and store the
655 * result in a 32-bit word.
656 *
657 * arg[0] = pointer to destination uint32_t in host memory
658 * arg[1] = pointer to source uint32_t in host memory
659 * arg[2] = 32-bit value
660 */
661 X(sub)
662 {
663 *((uint32_t *)ic->arg[0]) = *((uint32_t *)ic->arg[1]) - ic->arg[2];
664 }
665 Y(sub)
666 X(sub_self)
667 {
668 *((uint32_t *)ic->arg[0]) -= ic->arg[2];
669 }
670 Y(sub_self)
671
672
673 /*
674 * add: Add an immediate value to a 32-bit word, and store the
675 * result in a 32-bit word.
676 *
677 * arg[0] = pointer to destination uint32_t in host memory
678 * arg[1] = pointer to source uint32_t in host memory
679 * arg[2] = 32-bit value
680 */
681 X(add)
682 {
683 *((uint32_t *)ic->arg[0]) = *((uint32_t *)ic->arg[1]) + ic->arg[2];
684 }
685 Y(add)
686 X(add_self)
687 {
688 *((uint32_t *)ic->arg[0]) += ic->arg[2];
689 }
690 Y(add_self)
691
692
693 #include "tmp_arm_include_self.c"
694
695
696 /*****************************************************************************/
697
698
699 /*
700 * mov_2: Double "mov".
701 *
702 * The current and the next arm_instr_call are treated as "mov"s.
703 */
704 X(mov_2)
705 {
706 *((uint32_t *)ic[0].arg[0]) = ic[0].arg[1];
707 *((uint32_t *)ic[1].arg[0]) = ic[1].arg[1];
708 cpu->cd.arm.next_ic ++;
709 cpu->n_translated_instrs ++;
710 }
711
712
713 /*
714 * fill_loop_test:
715 *
716 * A byte-fill loop. Fills at most one page at a time. If the page was not
717 * in the host_store table, then the original sequence (beginning with
718 * cmps r2,#0) is executed instead.
719 *
720 * Z:cmps r2,#0 ic[0]
721 * strb rX,[rY],#1 ic[1]
722 * sub r2,r2,#1 ic[2]
723 * bgt Z ic[3]
724 */
725 X(fill_loop_test)
726 {
727 uint32_t addr, a, n, ofs, maxlen;
728 unsigned char *page;
729
730 addr = *((uint32_t *)ic[1].arg[0]);
731 n = cpu->cd.arm.r[2] + 1;
732 ofs = addr & 0xfff;
733 maxlen = 4096 - ofs;
734 if (n > maxlen)
735 n = maxlen;
736
737 page = cpu->cd.arm.host_store[addr >> 12];
738 if (page == NULL) {
739 arm_cmps_0[2](cpu, ic);
740 return;
741 }
742
743 /* printf("x = %x, n = %i\n", *((uint32_t *)ic[1].arg[2]), n); */
744 memset(page + ofs, *((uint32_t *)ic[1].arg[2]), n);
745
746 *((uint32_t *)ic[1].arg[0]) = addr + n;
747
748 cpu->cd.arm.r[2] -= n;
749 cpu->n_translated_instrs += (4 * n);
750
751 a = cpu->cd.arm.r[2];
752
753 cpu->cd.arm.flags &=
754 ~(ARM_FLAG_Z | ARM_FLAG_N | ARM_FLAG_V | ARM_FLAG_C);
755 if (a != 0)
756 cpu->cd.arm.flags |= ARM_FLAG_C;
757 else
758 cpu->cd.arm.flags |= ARM_FLAG_Z;
759 if ((int32_t)a < 0)
760 cpu->cd.arm.flags |= ARM_FLAG_N;
761
762 cpu->n_translated_instrs --;
763
764 if ((int32_t)a > 0)
765 cpu->cd.arm.next_ic --;
766 else
767 cpu->cd.arm.next_ic += 3;
768 }
769
770
771 /*****************************************************************************/
772
773
774 X(end_of_page)
775 {
776 /* Update the PC: (offset 0, but on the next page) */
777 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
778 cpu->cd.arm.r[ARM_PC] += (ARM_IC_ENTRIES_PER_PAGE << 2);
779 cpu->pc = cpu->cd.arm.r[ARM_PC];
780
781 /* Find the new physical page and update the translation pointers: */
782 arm_pc_to_pointers(cpu);
783
784 /* end_of_page doesn't count as an executed instruction: */
785 cpu->n_translated_instrs --;
786 }
787
788
789 /*****************************************************************************/
790
791
792 /*
793 * arm_combine_instructions():
794 *
795 * Combine two or more instructions, if possible, into a single function call.
796 */
797 void arm_combine_instructions(struct cpu *cpu, struct arm_instr_call *ic,
798 uint32_t addr)
799 {
800 int n_back;
801 n_back = (addr >> 2) & (ARM_IC_ENTRIES_PER_PAGE-1);
802
803 if (n_back >= 1) {
804 /* Double "mov": */
805 if (ic[-1].f == instr(mov) || ic[-1].f == instr(clear)) {
806 if (ic[-1].f == instr(mov) && ic[0].f == instr(mov)) {
807 ic[-1].f = instr(mov_2);
808 combined;
809 }
810 if (ic[-1].f == instr(clear) && ic[0].f == instr(mov)) {
811 ic[-1].f = instr(mov_2);
812 ic[-1].arg[1] = 0;
813 combined;
814 }
815 if (ic[-1].f == instr(mov) && ic[0].f == instr(clear)) {
816 ic[-1].f = instr(mov_2);
817 ic[0].arg[1] = 0;
818 combined;
819 }
820 if (ic[-1].f == instr(clear) && ic[0].f==instr(clear)) {
821 ic[-1].f = instr(mov_2);
822 ic[-1].arg[1] = 0;
823 ic[0].arg[1] = 0;
824 combined;
825 }
826 }
827 }
828
829 if (n_back >= 3) {
830 if (ic[-3].f == arm_cmps_0[2] &&
831 ic[-2].f == instr(store_w0_byte_u1_p0_imm) &&
832 ic[-2].arg[1] == 1 &&
833 ic[-1].f == arm_sub_self_1[2] &&
834 ic[ 0].f == instr(b_samepage__gt) &&
835 ic[ 0].arg[0] == (size_t)&ic[-3]) {
836 ic[-3].f = instr(fill_loop_test);
837 combined;
838 }
839 }
840
841 /* TODO: Combine forward as well */
842 }
843
844
845 /*****************************************************************************/
846
847
848 /*
849 * arm_instr_to_be_translated():
850 *
851 * Translate an instruction word into an arm_instr_call. ic is filled in with
852 * valid data for the translated instruction, or a "nothing" instruction if
853 * there was a translation failure. The newly translated instruction is then
854 * executed.
855 */
856 X(to_be_translated)
857 {
858 uint32_t addr, low_pc, iword, imm;
859 unsigned char *page;
860 unsigned char ib[4];
861 int condition_code, main_opcode, secondary_opcode, s_bit, r16, r12, r8;
862 int p_bit, u_bit, b_bit, w_bit, l_bit;
863 void (*samepage_function)(struct cpu *, struct arm_instr_call *);
864
865 /* Figure out the (virtual) address of the instruction: */
866 low_pc = ((size_t)ic - (size_t)cpu->cd.arm.cur_ic_page)
867 / sizeof(struct arm_instr_call);
868 addr = cpu->cd.arm.r[ARM_PC] & ~((ARM_IC_ENTRIES_PER_PAGE-1) <<
869 ARM_INSTR_ALIGNMENT_SHIFT);
870 addr += (low_pc << ARM_INSTR_ALIGNMENT_SHIFT);
871 cpu->pc = cpu->cd.arm.r[ARM_PC] = addr;
872 addr &= ~0x3;
873
874 /* Read the instruction word from memory: */
875 page = cpu->cd.arm.host_load[addr >> 12];
876
877 if (page != NULL) {
878 /* fatal("TRANSLATION HIT!\n"); */
879 memcpy(ib, page + (addr & 0xfff), sizeof(ib));
880 } else {
881 /* fatal("TRANSLATION MISS!\n"); */
882 if (!cpu->memory_rw(cpu, cpu->mem, addr, &ib[0],
883 sizeof(ib), MEM_READ, CACHE_INSTRUCTION)) {
884 fatal("to_be_translated(): "
885 "read failed: TODO\n");
886 goto bad;
887 }
888 }
889
890 if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
891 iword = ib[0] + (ib[1]<<8) + (ib[2]<<16) + (ib[3]<<24);
892 else
893 iword = ib[3] + (ib[2]<<8) + (ib[1]<<16) + (ib[0]<<24);
894
895 /* fatal("{ ARM translating pc=0x%08x iword=0x%08x }\n",
896 addr, iword); */
897
898
899 #define DYNTRANS_TO_BE_TRANSLATED_HEAD
900 #include "cpu_dyntrans.c"
901 #undef DYNTRANS_TO_BE_TRANSLATED_HEAD
902
903
904 /* The idea of taking bits 27..24 was found here:
905 http://armphetamine.sourceforge.net/oldinfo.html */
906 condition_code = iword >> 28;
907 main_opcode = (iword >> 24) & 15;
908 secondary_opcode = (iword >> 21) & 15;
909 u_bit = (iword >> 23) & 1;
910 b_bit = (iword >> 22) & 1;
911 w_bit = (iword >> 21) & 1;
912 s_bit = l_bit = (iword >> 20) & 1;
913 r16 = (iword >> 16) & 15;
914 r12 = (iword >> 12) & 15;
915 r8 = (iword >> 8) & 15;
916
917 if (condition_code == 0xf) {
918 fatal("TODO: ARM condition code 0x%x\n",
919 condition_code);
920 goto bad;
921 }
922
923
924 /*
925 * Translate the instruction:
926 */
927
928 switch (main_opcode) {
929
930 case 0x0:
931 case 0x1:
932 case 0x2:
933 case 0x3:
934 if ((main_opcode & 2) == 0) {
935 if ((iword & 0x0ffffff0) == 0x01a0f000) {
936 /* Hardcoded: mov pc, rX */
937 if ((iword & 15) == ARM_PC) {
938 fatal("mov pc,pc?\n");
939 goto bad;
940 }
941 ic->f = cond_instr(mov_pc);
942 ic->arg[0] = (size_t)
943 (&cpu->cd.arm.r[iword & 15]);
944 if ((iword & 15) == ARM_LR &&
945 cpu->machine->show_trace_tree)
946 ic->f = cond_instr(ret_trace);
947 } else if ((iword & 0x0fff0ff0) == 0x01a00000) {
948 /* Hardcoded: mov reg,reg */
949 if ((iword & 15) == ARM_PC) {
950 fatal("mov reg,pc?\n");
951 goto bad;
952 }
953 ic->f = cond_instr(mov_regreg);
954 ic->arg[0] = (size_t)
955 (&cpu->cd.arm.r[r12]);
956 ic->arg[1] = (size_t)
957 (&cpu->cd.arm.r[iword & 15]);
958 } else {
959 fatal("REGISTER FORM! TODO\n");
960 goto bad;
961 }
962 break;
963 }
964 imm = iword & 0xff;
965 r8 <<= 1;
966 while (r8-- > 0)
967 imm = (imm >> 1) | ((imm & 1) << 31);
968 switch (secondary_opcode) {
969 case 0x2: /* SUB */
970 case 0x4: /* ADD */
971 if (s_bit) {
972 fatal("add/sub s_bit: TODO\n");
973 goto bad;
974 }
975 if (r12 == ARM_PC || r16 == ARM_PC) {
976 fatal("add/sub: PC\n");
977 goto bad;
978 }
979 switch (secondary_opcode) {
980 case 0x2:
981 ic->f = cond_instr(sub);
982 if (r12 == r16) {
983 ic->f = cond_instr(sub_self);
984 if (imm == 1 && r12 != ARM_PC)
985 ic->f = arm_sub_self_1[r12];
986 if (imm == 4 && r12 != ARM_PC)
987 ic->f = arm_sub_self_4[r12];
988 }
989 break;
990 case 0x4:
991 ic->f = cond_instr(add);
992 if (r12 == r16) {
993 ic->f = cond_instr(add_self);
994 if (imm == 1 && r12 != ARM_PC)
995 ic->f = arm_add_self_1[r12];
996 if (imm == 4 && r12 != ARM_PC)
997 ic->f = arm_add_self_4[r12];
998 }
999 break;
1000 }
1001 ic->arg[0] = (size_t)(&cpu->cd.arm.r[r12]);
1002 ic->arg[1] = (size_t)(&cpu->cd.arm.r[r16]);
1003 ic->arg[2] = imm;
1004 break;
1005 case 0xa: /* CMP */
1006 if (!s_bit) {
1007 fatal("cmp !s_bit: TODO\n");
1008 goto bad;
1009 }
1010 ic->f = cond_instr(cmps);
1011 ic->arg[0] = (size_t)(&cpu->cd.arm.r[r16]);
1012 ic->arg[1] = imm;
1013 if (imm == 0 && r16 != ARM_PC)
1014 ic->f = arm_cmps_0[r16];
1015 break;
1016 case 0xd: /* MOV */
1017 if (s_bit) {
1018 fatal("mov s_bit: TODO\n");
1019 goto bad;
1020 }
1021 if (r12 == ARM_PC) {
1022 fatal("TODO: mov used as branch\n");
1023 goto bad;
1024 } else {
1025 ic->f = cond_instr(mov);
1026 ic->arg[0] = (size_t)(&cpu->cd.arm.r[r12]);
1027 ic->arg[1] = imm;
1028 if (imm == 0)
1029 ic->f = cond_instr(clear);
1030 }
1031 break;
1032 default:goto bad;
1033 }
1034 break;
1035
1036 case 0x4: /* Load and store... */
1037 case 0x5: /* xxxx010P UBWLnnnn ddddoooo oooooooo Immediate */
1038 case 0x6: /* xxxx011P UBWLnnnn ddddcccc ctt0mmmm Register */
1039 case 0x7:
1040 p_bit = main_opcode & 1;
1041 ic->f = load_store_instr[((iword >> 16) & 0x3f0)
1042 + condition_code];
1043 imm = iword & 0xfff;
1044 if (!u_bit)
1045 imm = (int32_t)0-imm;
1046 if (main_opcode < 6) {
1047 /* Immediate: */
1048 ic->arg[0] = (size_t)(&cpu->cd.arm.r[r16]);
1049 ic->arg[1] = (size_t)(imm);
1050 ic->arg[2] = (size_t)(&cpu->cd.arm.r[r12]);
1051 }
1052 if (main_opcode == 4 && b_bit) {
1053 /* Post-index, immediate: */
1054 if (imm == 1 && !w_bit && l_bit)
1055 ic->f = instr(store_w0_byte_u1_p0_imm_fixinc1);
1056 if (w_bit) {
1057 fatal("load/store: T-bit\n");
1058 goto bad;
1059 }
1060 if (r16 == ARM_PC) {
1061 fatal("load/store writeback PC: error\n");
1062 goto bad;
1063 }
1064 } else if (main_opcode == 5) {
1065 /* Pre-index, immediate: */
1066 /* ldr(b) Rd,[Rn,#imm] */
1067 if (l_bit) {
1068 if (r12 == ARM_PC) {
1069 fatal("WARNING: ldr to pc register?\n");
1070 goto bad;
1071 }
1072 if (r16 == ARM_PC) {
1073 if (w_bit) {
1074 fatal("w bit load etc\n");
1075 goto bad;
1076 }
1077 ic->f = b_bit?
1078 cond_instr(load_byte_imm_pcrel) :
1079 cond_instr(load_word_imm_pcrel);
1080 }
1081 } else {
1082 if (r12 == ARM_PC) {
1083 fatal("TODO: store pc\n");
1084 goto bad;
1085 }
1086 if (r16 == ARM_PC) {
1087 fatal("TODO: store pc rel\n");
1088 goto bad;
1089 }
1090 }
1091 } else {
1092 fatal("Specific Load/store TODO\n");
1093 goto bad;
1094 }
1095 break;
1096
1097 case 0x8: /* Multiple load/store... (Block data transfer) */
1098 case 0x9: /* xxxx100P USWLnnnn llllllll llllllll */
1099 if (l_bit)
1100 ic->f = cond_instr(bdt_load);
1101 else
1102 ic->f = cond_instr(bdt_store);
1103 ic->arg[0] = (size_t)(&cpu->cd.arm.r[r16]);
1104 ic->arg[1] = (size_t)iword;
1105 if (r16 == ARM_PC) {
1106 fatal("TODO: bdt with PC as base\n");
1107 goto bad;
1108 }
1109 break;
1110
1111 case 0xa: /* B: branch */
1112 case 0xb: /* BL: branch+link */
1113 if (main_opcode == 0x0a) {
1114 ic->f = cond_instr(b);
1115 samepage_function = cond_instr(b_samepage);
1116 } else {
1117 if (cpu->machine->show_trace_tree) {
1118 ic->f = cond_instr(bl_trace);
1119 samepage_function =
1120 cond_instr(bl_samepage_trace);
1121 } else {
1122 ic->f = cond_instr(bl);
1123 samepage_function = cond_instr(bl_samepage);
1124 }
1125 }
1126
1127 ic->arg[0] = (iword & 0x00ffffff) << 2;
1128 /* Sign-extend: */
1129 if (ic->arg[0] & 0x02000000)
1130 ic->arg[0] |= 0xfc000000;
1131 /*
1132 * Branches are calculated as PC + 8 + offset.
1133 */
1134 ic->arg[0] = (int32_t)(ic->arg[0] + 8);
1135
1136 /* Special case: branch within the same page: */
1137 {
1138 uint32_t mask_within_page =
1139 ((ARM_IC_ENTRIES_PER_PAGE-1) << 2) | 3;
1140 uint32_t old_pc = addr;
1141 uint32_t new_pc = old_pc + (int32_t)ic->arg[0];
1142 if ((old_pc & ~mask_within_page) ==
1143 (new_pc & ~mask_within_page)) {
1144 ic->f = samepage_function;
1145 ic->arg[0] = (size_t) (
1146 cpu->cd.arm.cur_ic_page +
1147 ((new_pc & mask_within_page) >> 2));
1148 }
1149 }
1150 break;
1151
1152 default:goto bad;
1153 }
1154
1155
1156 #define DYNTRANS_TO_BE_TRANSLATED_TAIL
1157 #include "cpu_dyntrans.c"
1158 #undef DYNTRANS_TO_BE_TRANSLATED_TAIL
1159 }
1160

  ViewVC Help
Powered by ViewVC 1.1.26