/[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

Annotation of /trunk/src/cpu_arm_instr.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (hide annotations)
Mon Oct 8 16:18:38 2007 UTC (16 years, 6 months 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 dpavlin 10 /*
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 dpavlin 12 * $Id: cpu_arm_instr.c,v 1.51 2005/08/16 05:37:10 debug Exp $
29 dpavlin 10 *
30     * ARM instructions.
31     *
32 dpavlin 12 * Individual functions should keep track of cpu->n_translated_instrs.
33 dpavlin 10 * (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 dpavlin 12 uint32_t low_pc;
153 dpavlin 10
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 dpavlin 12 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
158 dpavlin 10 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 dpavlin 12 /* Find the new physical page and update the translation pointers: */
163     arm_pc_to_pointers(cpu);
164 dpavlin 10 }
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 dpavlin 12 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 dpavlin 10 }
205     Y(bl)
206    
207    
208     /*
209 dpavlin 12 * 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 dpavlin 10 * 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 dpavlin 12 lr &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
252 dpavlin 10 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 dpavlin 12 * bl_samepage_trace: Branch and Link (to the same page), with trace
265 dpavlin 10 *
266 dpavlin 12 * Same as for bl_samepage.
267 dpavlin 10 */
268 dpavlin 12 X(bl_samepage_trace)
269 dpavlin 10 {
270 dpavlin 12 uint32_t tmp_pc, lr, low_pc;
271 dpavlin 10
272 dpavlin 12 /* 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 dpavlin 10
279 dpavlin 12 /* Link: */
280     cpu->cd.arm.r[ARM_LR] = lr;
281 dpavlin 10
282 dpavlin 12 /* Branch: */
283     cpu->cd.arm.next_ic = (struct arm_instr_call *) ic->arg[0];
284 dpavlin 10
285 dpavlin 12 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 dpavlin 10 }
292 dpavlin 12 Y(bl_samepage_trace)
293 dpavlin 10
294    
295     /*
296 dpavlin 12 * mov_pc: "mov pc,reg"
297 dpavlin 10 *
298 dpavlin 12 * arg[0] = pointer to uint32_t in host memory of source register
299 dpavlin 10 */
300 dpavlin 12 X(mov_pc)
301 dpavlin 10 {
302 dpavlin 12 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 dpavlin 10
305 dpavlin 12 /* Update the PC register: */
306     cpu->pc = cpu->cd.arm.r[ARM_PC] = *((uint32_t *)ic->arg[0]);
307 dpavlin 10
308 dpavlin 12 /*
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 dpavlin 10 }
319     }
320 dpavlin 12 Y(mov_pc)
321 dpavlin 10
322    
323     /*
324 dpavlin 12 * ret_trace: "mov pc,lr" with trace enabled
325 dpavlin 10 *
326 dpavlin 12 * arg[0] = ignored (similar to mov_pc above)
327 dpavlin 10 */
328 dpavlin 12 X(ret_trace)
329 dpavlin 10 {
330 dpavlin 12 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 dpavlin 10
333 dpavlin 12 /* Update the PC register: */
334     cpu->pc = cpu->cd.arm.r[ARM_PC] = cpu->cd.arm.r[ARM_LR];
335 dpavlin 10
336 dpavlin 12 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 dpavlin 10 }
349     }
350 dpavlin 12 Y(ret_trace)
351 dpavlin 10
352    
353     /*
354 dpavlin 12 * mov_regreg:
355 dpavlin 10 *
356 dpavlin 12 * 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 dpavlin 10 */
359 dpavlin 12 X(mov_regreg)
360 dpavlin 10 {
361 dpavlin 12 *((uint32_t *)ic->arg[0]) = *((uint32_t *)ic->arg[1]);
362 dpavlin 10 }
363 dpavlin 12 Y(mov_regreg)
364 dpavlin 10
365    
366     /*
367 dpavlin 12 * mov: Set a 32-bit register to a 32-bit value.
368 dpavlin 10 *
369 dpavlin 12 * arg[0] = pointer to uint32_t in host memory
370     * arg[1] = 32-bit value
371 dpavlin 10 */
372 dpavlin 12 X(mov)
373 dpavlin 10 {
374 dpavlin 12 *((uint32_t *)ic->arg[0]) = ic->arg[1];
375 dpavlin 10 }
376 dpavlin 12 Y(mov)
377 dpavlin 10
378    
379     /*
380 dpavlin 12 * clear: Set a 32-bit register to 0. (A "mov" to fixed value zero.)
381 dpavlin 10 *
382 dpavlin 12 * arg[0] = pointer to uint32_t in host memory
383 dpavlin 10 */
384 dpavlin 12 X(clear)
385 dpavlin 10 {
386 dpavlin 12 *((uint32_t *)ic->arg[0]) = 0;
387 dpavlin 10 }
388 dpavlin 12 Y(clear)
389 dpavlin 10
390    
391 dpavlin 12 #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 dpavlin 10 /*
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 dpavlin 12 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
425 dpavlin 10 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 dpavlin 12 cpu->cd.arm.r[ARM_PC] &= ~((ARM_IC_ENTRIES_PER_PAGE-1) << 2);
455 dpavlin 10 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 dpavlin 12 * 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 dpavlin 10 * 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 dpavlin 12 c = a - b;
631     if (a > b)
632     cpu->cd.arm.flags |= ARM_FLAG_C;
633 dpavlin 10 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 dpavlin 12 else
643     v = !n;
644 dpavlin 10 if (v)
645     cpu->cd.arm.flags |= ARM_FLAG_V;
646     }
647     Y(cmps)
648    
649    
650 dpavlin 12 #include "cpu_arm_instr_cmps.c"
651    
652    
653 dpavlin 10 /*
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 dpavlin 12 #include "tmp_arm_include_self.c"
694    
695    
696 dpavlin 10 /*****************************************************************************/
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 dpavlin 12 cpu->n_translated_instrs ++;
710 dpavlin 10 }
711    
712    
713 dpavlin 12 /*
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 dpavlin 10
730 dpavlin 12 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 dpavlin 10
737 dpavlin 12 page = cpu->cd.arm.host_store[addr >> 12];
738     if (page == NULL) {
739     arm_cmps_0[2](cpu, ic);
740     return;
741     }
742 dpavlin 10
743 dpavlin 12 /* 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 dpavlin 10 }
769    
770    
771 dpavlin 12 /*****************************************************************************/
772    
773    
774 dpavlin 10 X(end_of_page)
775     {
776 dpavlin 12 /* 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 dpavlin 10 cpu->pc = cpu->cd.arm.r[ARM_PC];
780    
781 dpavlin 12 /* Find the new physical page and update the translation pointers: */
782     arm_pc_to_pointers(cpu);
783 dpavlin 10
784 dpavlin 12 /* end_of_page doesn't count as an executed instruction: */
785     cpu->n_translated_instrs --;
786 dpavlin 10 }
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 dpavlin 12 void arm_combine_instructions(struct cpu *cpu, struct arm_instr_call *ic,
798     uint32_t addr)
799 dpavlin 10 {
800     int n_back;
801 dpavlin 12 n_back = (addr >> 2) & (ARM_IC_ENTRIES_PER_PAGE-1);
802 dpavlin 10
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 dpavlin 12
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 dpavlin 10 }
843    
844    
845 dpavlin 12 /*****************************************************************************/
846    
847    
848 dpavlin 10 /*
849 dpavlin 12 * arm_instr_to_be_translated():
850 dpavlin 10 *
851 dpavlin 12 * 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 dpavlin 10 */
856 dpavlin 12 X(to_be_translated)
857 dpavlin 10 {
858     uint32_t addr, low_pc, iword, imm;
859 dpavlin 12 unsigned char *page;
860 dpavlin 10 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 dpavlin 12 /* Figure out the (virtual) address of the instruction: */
866 dpavlin 10 low_pc = ((size_t)ic - (size_t)cpu->cd.arm.cur_ic_page)
867     / sizeof(struct arm_instr_call);
868 dpavlin 12 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 dpavlin 10
874     /* Read the instruction word from memory: */
875 dpavlin 12 page = cpu->cd.arm.host_load[addr >> 12];
876 dpavlin 10
877 dpavlin 12 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 dpavlin 10 }
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 dpavlin 12
899     #define DYNTRANS_TO_BE_TRANSLATED_HEAD
900     #include "cpu_dyntrans.c"
901     #undef DYNTRANS_TO_BE_TRANSLATED_HEAD
902    
903    
904 dpavlin 10 /* 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 dpavlin 12 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 dpavlin 10 }
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 dpavlin 12 fatal("add/sub s_bit: TODO\n");
973 dpavlin 10 goto bad;
974     }
975 dpavlin 12 if (r12 == ARM_PC || r16 == ARM_PC) {
976     fatal("add/sub: PC\n");
977     goto bad;
978     }
979 dpavlin 10 switch (secondary_opcode) {
980     case 0x2:
981 dpavlin 12 ic->f = cond_instr(sub);
982     if (r12 == r16) {
983 dpavlin 10 ic->f = cond_instr(sub_self);
984 dpavlin 12 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 dpavlin 10 break;
990     case 0x4:
991 dpavlin 12 ic->f = cond_instr(add);
992     if (r12 == r16) {
993 dpavlin 10 ic->f = cond_instr(add_self);
994 dpavlin 12 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 dpavlin 10 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 dpavlin 12 if (imm == 0 && r16 != ARM_PC)
1014     ic->f = arm_cmps_0[r16];
1015 dpavlin 10 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 dpavlin 12 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 dpavlin 10 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 dpavlin 12 if (imm == 1 && !w_bit && l_bit)
1055     ic->f = instr(store_w0_byte_u1_p0_imm_fixinc1);
1056 dpavlin 10 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 dpavlin 12 if (r12 == ARM_PC) {
1069 dpavlin 10 fatal("WARNING: ldr to pc register?\n");
1070 dpavlin 12 goto bad;
1071 dpavlin 10 }
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 dpavlin 12 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 dpavlin 10 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 dpavlin 12 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 dpavlin 10 }
1126    
1127     ic->arg[0] = (iword & 0x00ffffff) << 2;
1128     /* Sign-extend: */
1129     if (ic->arg[0] & 0x02000000)
1130     ic->arg[0] |= 0xfc000000;
1131 dpavlin 12 /*
1132     * Branches are calculated as PC + 8 + offset.
1133     */
1134 dpavlin 10 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 dpavlin 12 ((ARM_IC_ENTRIES_PER_PAGE-1) << 2) | 3;
1140 dpavlin 10 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 dpavlin 12 #define DYNTRANS_TO_BE_TRANSLATED_TAIL
1157     #include "cpu_dyntrans.c"
1158     #undef DYNTRANS_TO_BE_TRANSLATED_TAIL
1159 dpavlin 10 }
1160    

  ViewVC Help
Powered by ViewVC 1.1.26