Commit 7d13299d07a9c3c42277207ae7a691f0501a70b2
1 parent
1017ebe9
added translation cache
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@25 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
13 changed files
with
651 additions
and
254 deletions
Makefile
1 | -ARCH=i386 | |
2 | -#ARCH=ppc | |
3 | -HOST_CC=gcc | |
1 | +include config.mak | |
4 | 2 | |
5 | -ifeq ($(ARCH),i386) | |
6 | -CFLAGS=-Wall -O2 -g -fomit-frame-pointer | |
3 | +CFLAGS=-Wall -O2 -g | |
7 | 4 | LDFLAGS=-g |
8 | 5 | LIBS= |
9 | -CC=gcc | |
10 | 6 | DEFINES=-DHAVE_BYTESWAP_H |
7 | + | |
8 | +ifeq ($(ARCH),i386) | |
9 | +CFLAGS+=-fomit-frame-pointer | |
11 | 10 | OP_CFLAGS=$(CFLAGS) -malign-functions=0 -mpreferred-stack-boundary=2 |
12 | 11 | endif |
13 | 12 | |
14 | 13 | ifeq ($(ARCH),ppc) |
15 | -GCC_LIBS_DIR=/usr/netgem/tools/lib/gcc-lib/powerpc-linux/2.95.2 | |
16 | -DIST=/home/fbe/nsv/dist/hw/n6-dtt | |
17 | -CC=powerpc-linux-gcc -msoft-float | |
18 | -CFLAGS=-Wall -pipe -O2 -mcpu=405 -mbig -nostdinc -g -I$(GCC_LIBS_DIR)/include -I$(DIST)/include | |
19 | -LIBS_DIR=$(DIST)/lib | |
20 | -CRT1=$(LIBS_DIR)/crt1.o | |
21 | -CRTI=$(LIBS_DIR)/crti.o | |
22 | -CRTN=$(LIBS_DIR)/crtn.o | |
23 | -CRTBEGIN=$(GCC_LIBS_DIR)/crtbegin.o | |
24 | -CRTEND=$(GCC_LIBS_DIR)/crtend.o | |
25 | -LDFLAGS=-static -g -nostdlib $(CRT1) $(CRTI) $(CRTBEGIN) | |
26 | -LIBS=-L$(LIBS_DIR) -ltinyc -lgcc $(CRTEND) $(CRTN) | |
27 | -DEFINES=-Dsocklen_t=int | |
28 | 14 | OP_CFLAGS=$(CFLAGS) |
29 | 15 | endif |
30 | 16 | |
31 | 17 | ######################################################### |
32 | 18 | |
33 | 19 | DEFINES+=-D_GNU_SOURCE |
34 | -DEFINES+=-DCONFIG_PREFIX=\"/usr/local\" | |
35 | 20 | LDSCRIPT=$(ARCH).ld |
36 | 21 | LIBS+=-ldl -lm |
37 | -VERSION=0.1 | |
22 | + | |
23 | +# profiling code | |
24 | +ifdef TARGET_GPROF | |
25 | +LDFLAGS+=-p | |
26 | +CFLAGS+=-p | |
27 | +endif | |
38 | 28 | |
39 | 29 | OBJS= elfload.o main.o thunk.o syscall.o |
40 | -OBJS+=translate-i386.o op-i386.o | |
30 | +OBJS+=translate-i386.o op-i386.o exec-i386.o | |
41 | 31 | # NOTE: the disassembler code is only needed for debugging |
42 | 32 | OBJS+=i386-dis.o dis-buf.o |
43 | 33 | SRCS = $(OBJS:.o=.c) |
... | ... | @@ -66,8 +56,12 @@ op-i386.o: op-i386.c opreg_template.h ops_template.h |
66 | 56 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
67 | 57 | |
68 | 58 | clean: |
59 | + $(MAKE) -C tests clean | |
69 | 60 | rm -f *.o *~ gemu dyngen TAGS |
70 | 61 | |
62 | +distclean: clean | |
63 | + rm -f config.mak config.h | |
64 | + | |
71 | 65 | # various test targets |
72 | 66 | test speed: gemu |
73 | 67 | make -C tests $@ |
... | ... | @@ -82,7 +76,7 @@ TODO elfload.c main.c signal.c thunk.h\ |
82 | 76 | cpu-i386.h gemu.h op-i386.c syscall-i386.h translate-i386.c\ |
83 | 77 | dis-asm.h gen-i386.h op-i386.h syscall.c\ |
84 | 78 | dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\ |
85 | -i386.ld ppc.ld\ | |
79 | +i386.ld ppc.ld exec-i386.h exec-i386.c configure VERSION \ | |
86 | 80 | tests/Makefile\ |
87 | 81 | tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h\ |
88 | 82 | tests/test-i386-muldiv.h\ | ... | ... |
TODO
1 | -- tests | |
1 | +- optimize translated cache chaining (DLL PLT like system) | |
2 | +- optimize inverse flags propagation (easy by generating intermediate | |
3 | + micro operation array). | |
2 | 4 | - signals |
3 | 5 | - threads |
4 | -- fix printf for doubles (fp87.c bug ?) | |
5 | 6 | - make it self runnable (use same trick as ld.so : include its own relocator and libc) |
6 | 7 | - fix FPU exceptions (in particular: gen_op_fpush not before mem load) |
8 | +- tests | ... | ... |
configure
0 → 100755
1 | +#!/bin/sh | |
2 | +# | |
3 | +# gemu configure script (c) 2003 Fabrice Bellard | |
4 | +# | |
5 | +# set temporary file name | |
6 | +if test ! -z "$TMPDIR" ; then | |
7 | + TMPDIR1="${TMPDIR}" | |
8 | +elif test ! -z "$TEMPDIR" ; then | |
9 | + TMPDIR1="${TEMPDIR}" | |
10 | +else | |
11 | + TMPDIR1="/tmp" | |
12 | +fi | |
13 | + | |
14 | +TMPC="${TMPDIR1}/qemacs-conf-${RANDOM}-$$-${RANDOM}.c" | |
15 | +TMPO="${TMPDIR1}/qemacs-conf-${RANDOM}-$$-${RANDOM}.o" | |
16 | +TMPS="${TMPDIR1}/qemacs-conf-${RANDOM}-$$-${RANDOM}.S" | |
17 | +TMPH="${TMPDIR1}/qemacs-conf-${RANDOM}-$$-${RANDOM}.h" | |
18 | + | |
19 | +# default parameters | |
20 | +prefix="/usr/local" | |
21 | +cross_prefix="" | |
22 | +cc="gcc" | |
23 | +host_cc="gcc" | |
24 | +ar="ar" | |
25 | +make="make" | |
26 | +strip="strip" | |
27 | +cpu=`uname -m` | |
28 | +case "$cpu" in | |
29 | + i386|i486|i586|i686|i86pc|BePC) | |
30 | + cpu="x86" | |
31 | + ;; | |
32 | + armv4l) | |
33 | + cpu="armv4l" | |
34 | + ;; | |
35 | + alpha) | |
36 | + cpu="alpha" | |
37 | + ;; | |
38 | + "Power Macintosh"|ppc) | |
39 | + cpu="powerpc" | |
40 | + ;; | |
41 | + mips) | |
42 | + cpu="mips" | |
43 | + ;; | |
44 | + *) | |
45 | + cpu="unknown" | |
46 | + ;; | |
47 | +esac | |
48 | +gprof="no" | |
49 | +bigendian="no" | |
50 | + | |
51 | +# OS specific | |
52 | +targetos=`uname -s` | |
53 | +case $targetos in | |
54 | +BeOS) | |
55 | +prefix="/boot/home/config" | |
56 | +# helps building libavcodec | |
57 | +CFLAGS="-O2 -DPIC" | |
58 | +# no need for libm, but the inet stuff | |
59 | +# Check for BONE | |
60 | +if (echo $BEINCLUDES|grep 'headers/be/bone' >/dev/null); then | |
61 | +extralibs="-lbind -lsocket" | |
62 | +else | |
63 | +echo "Not sure building for net_server will succeed... good luck." | |
64 | +extralibs="-lsocket" | |
65 | +fi ;; | |
66 | +BSD/OS) | |
67 | +extralibs="-lpoll -lgnugetopt -lm" | |
68 | +make="gmake" | |
69 | +;; | |
70 | +*) ;; | |
71 | +esac | |
72 | + | |
73 | +# find source path | |
74 | +# XXX: we assume an absolute path is given when launching configure, | |
75 | +# except in './configure' case. | |
76 | +source_path=${0%configure} | |
77 | +source_path=${source_path%/} | |
78 | +source_path_used="yes" | |
79 | +if test -z "$source_path" -o "$source_path" = "." ; then | |
80 | + source_path=`pwd` | |
81 | + source_path_used="no" | |
82 | +fi | |
83 | + | |
84 | +for opt do | |
85 | + case "$opt" in | |
86 | + --prefix=*) prefix=`echo $opt | cut -d '=' -f 2` | |
87 | + ;; | |
88 | + --source-path=*) source_path=`echo $opt | cut -d '=' -f 2` | |
89 | + ;; | |
90 | + --cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2` | |
91 | + ;; | |
92 | + --cc=*) cc=`echo $opt | cut -d '=' -f 2` | |
93 | + ;; | |
94 | + --make=*) make=`echo $opt | cut -d '=' -f 2` | |
95 | + ;; | |
96 | + --extra-cflags=*) CFLAGS="${opt#--extra-cflags=}" | |
97 | + ;; | |
98 | + --extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}" | |
99 | + ;; | |
100 | + --extra-libs=*) extralibs=${opt#--extra-libs=} | |
101 | + ;; | |
102 | + --cpu=*) cpu=`echo $opt | cut -d '=' -f 2` | |
103 | + ;; | |
104 | + --enable-gprof) gprof="yes" | |
105 | + ;; | |
106 | + esac | |
107 | +done | |
108 | + | |
109 | +# Checking for CFLAGS | |
110 | +if test -z "$CFLAGS"; then | |
111 | + CFLAGS="-O2" | |
112 | +fi | |
113 | + | |
114 | +cc="${cross_prefix}${cc}" | |
115 | +ar="${cross_prefix}${ar}" | |
116 | +strip="${cross_prefix}${strip}" | |
117 | + | |
118 | +if test -z "$cross_prefix" ; then | |
119 | + | |
120 | +# --- | |
121 | +# big/little endian test | |
122 | +cat > $TMPC << EOF | |
123 | +#include <inttypes.h> | |
124 | +int main(int argc, char ** argv){ | |
125 | + volatile uint32_t i=0x01234567; | |
126 | + return (*((uint8_t*)(&i))) == 0x67; | |
127 | +} | |
128 | +EOF | |
129 | + | |
130 | +if $cc -o $TMPE $TMPC 2>/dev/null ; then | |
131 | +$TMPE && bigendian="yes" | |
132 | +else | |
133 | +echo big/little test failed | |
134 | +fi | |
135 | + | |
136 | +else | |
137 | + | |
138 | +# if cross compiling, cannot launch a program, so make a static guess | |
139 | +if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then | |
140 | + bigendian="yes" | |
141 | +fi | |
142 | + | |
143 | +fi | |
144 | + | |
145 | +if test x"$1" = x"-h" -o x"$1" = x"--help" ; then | |
146 | +cat << EOF | |
147 | + | |
148 | +Usage: configure [options] | |
149 | +Options: [defaults in brackets after descriptions] | |
150 | + | |
151 | +EOF | |
152 | +echo "Standard options:" | |
153 | +echo " --help print this message" | |
154 | +echo " --prefix=PREFIX install in PREFIX [$prefix]" | |
155 | +echo " for audio/video/image support" | |
156 | +echo "" | |
157 | +echo "Advanced options (experts only):" | |
158 | +echo " --source-path=PATH path of source code [$source_path]" | |
159 | +echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" | |
160 | +echo " --cc=CC use C compiler CC [$cc]" | |
161 | +echo " --make=MAKE use specified make [$make]" | |
162 | +echo "" | |
163 | +echo "NOTE: The object files are build at the place where configure is launched" | |
164 | +exit 1 | |
165 | +fi | |
166 | + | |
167 | +echo "Install prefix $prefix" | |
168 | +echo "Source path $source_path" | |
169 | +echo "C compiler $cc" | |
170 | +echo "make $make" | |
171 | +echo "CPU $cpu" | |
172 | +echo "Big Endian $bigendian" | |
173 | +echo "gprof enabled $gprof" | |
174 | + | |
175 | +echo "Creating config.mak and config.h" | |
176 | + | |
177 | +echo "# Automatically generated by configure - do not modify" > config.mak | |
178 | +echo "/* Automatically generated by configure - do not modify */" > $TMPH | |
179 | + | |
180 | +echo "prefix=$prefix" >> config.mak | |
181 | +echo "#define CONFIG_GEMU_PREFIX \"$prefix\"" >> $TMPH | |
182 | +echo "MAKE=$make" >> config.mak | |
183 | +echo "CC=$cc" >> config.mak | |
184 | +echo "HOST_CC=$host_cc" >> config.mak | |
185 | +echo "AR=$ar" >> config.mak | |
186 | +echo "STRIP=$strip -s -R .comment -R .note" >> config.mak | |
187 | +echo "CFLAGS=$CFLAGS" >> config.mak | |
188 | +echo "LDFLAGS=$LDFLAGS" >> config.mak | |
189 | +if test "$cpu" = "x86" ; then | |
190 | + echo "ARCH=i386" >> config.mak | |
191 | +elif test "$cpu" = "armv4l" ; then | |
192 | + echo "ARCH=arm" >> config.mak | |
193 | +elif test "$cpu" = "powerpc" ; then | |
194 | + echo "ARCH=ppc" > config.mak | |
195 | +elif test "$cpu" = "mips" ; then | |
196 | + echo "ARCH=mips" > config.mak | |
197 | +else | |
198 | + echo "Unsupported CPU" | |
199 | + exit 1 | |
200 | +fi | |
201 | +if test "$bigendian" = "yes" ; then | |
202 | + echo "WORDS_BIGENDIAN=yes" >> config.mak | |
203 | + echo "#define WORDS_BIGENDIAN 1" >> $TMPH | |
204 | +fi | |
205 | +if test "$gprof" = "yes" ; then | |
206 | + echo "TARGET_GPROF=yes" >> config.mak | |
207 | + echo "#define HAVE_GPROF 1" >> $TMPH | |
208 | +fi | |
209 | +echo -n "VERSION=" >>config.mak | |
210 | +head $source_path/VERSION >>config.mak | |
211 | +echo "" >>config.mak | |
212 | +echo -n "#define GEMU_VERSION \"" >> $TMPH | |
213 | +head $source_path/VERSION >> $TMPH | |
214 | +echo "\"" >> $TMPH | |
215 | +if test "$network" = "yes" ; then | |
216 | + echo "#define CONFIG_NETWORK 1" >> $TMPH | |
217 | + echo "CONFIG_NETWORK=yes" >> config.mak | |
218 | +fi | |
219 | + | |
220 | +# build tree in object directory if source path is different from current one | |
221 | +if test "$source_path_used" = "yes" ; then | |
222 | + DIRS="tests" | |
223 | + FILES="Makefile tests/Makefile" | |
224 | + for dir in $DIRS ; do | |
225 | + mkdir -p $dir | |
226 | + done | |
227 | + for f in $FILES ; do | |
228 | + ln -sf $source_path/$f $f | |
229 | + done | |
230 | +fi | |
231 | +echo "SRC_PATH=$source_path" >> config.mak | |
232 | + | |
233 | +diff $TMPH config.h >/dev/null 2>&1 | |
234 | +if test $? -ne 0 ; then | |
235 | + mv -f $TMPH config.h | |
236 | +else | |
237 | + echo "config.h is unchanged" | |
238 | +fi | |
239 | + | |
240 | +rm -f $TMPH | ... | ... |
cpu-i386.h
... | ... | @@ -244,5 +244,6 @@ void cpu_x86_close(CPUX86State *s); |
244 | 244 | /* internal functions */ |
245 | 245 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
246 | 246 | int *gen_code_size_ptr, uint8_t *pc_start); |
247 | +void cpu_x86_tblocks_init(void); | |
247 | 248 | |
248 | 249 | #endif /* CPU_I386_H */ | ... | ... |
dyngen.c
1 | +/* | |
2 | + * Generic Dynamic compiler generator | |
3 | + * | |
4 | + * Copyright (c) 2003 Fabrice Bellard | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
1 | 20 | #include <stdlib.h> |
2 | 21 | #include <stdio.h> |
3 | 22 | #include <stdarg.h> | ... | ... |
exec-i386.c
0 → 100644
1 | +/* | |
2 | + * i386 emulator main execution loop | |
3 | + * | |
4 | + * Copyright (c) 2003 Fabrice Bellard | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | +#include "exec-i386.h" | |
21 | + | |
22 | +#define DEBUG_EXEC | |
23 | +#define DEBUG_FLUSH | |
24 | + | |
25 | +/* main execution loop */ | |
26 | + | |
27 | +/* maximum total translate dcode allocated */ | |
28 | +#define CODE_GEN_BUFFER_SIZE (2048 * 1024) | |
29 | +//#define CODE_GEN_BUFFER_SIZE (128 * 1024) | |
30 | +#define CODE_GEN_MAX_SIZE 65536 | |
31 | +#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ | |
32 | + | |
33 | +/* threshold to flush the translated code buffer */ | |
34 | +#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) | |
35 | + | |
36 | +#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) | |
37 | +#define CODE_GEN_HASH_BITS 15 | |
38 | +#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) | |
39 | +typedef struct TranslationBlock { | |
40 | + unsigned long pc; /* simulated PC corresponding to this block */ | |
41 | + uint8_t *tc_ptr; /* pointer to the translated code */ | |
42 | + struct TranslationBlock *hash_next; /* next matching block */ | |
43 | +} TranslationBlock; | |
44 | + | |
45 | +TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; | |
46 | +TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; | |
47 | +int nb_tbs; | |
48 | + | |
49 | +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; | |
50 | +uint8_t *code_gen_ptr; | |
51 | + | |
52 | +#ifdef DEBUG_EXEC | |
53 | +static const char *cc_op_str[] = { | |
54 | + "DYNAMIC", | |
55 | + "EFLAGS", | |
56 | + "MUL", | |
57 | + "ADDB", | |
58 | + "ADDW", | |
59 | + "ADDL", | |
60 | + "ADCB", | |
61 | + "ADCW", | |
62 | + "ADCL", | |
63 | + "SUBB", | |
64 | + "SUBW", | |
65 | + "SUBL", | |
66 | + "SBBB", | |
67 | + "SBBW", | |
68 | + "SBBL", | |
69 | + "LOGICB", | |
70 | + "LOGICW", | |
71 | + "LOGICL", | |
72 | + "INCB", | |
73 | + "INCW", | |
74 | + "INCL", | |
75 | + "DECB", | |
76 | + "DECW", | |
77 | + "DECL", | |
78 | + "SHLB", | |
79 | + "SHLW", | |
80 | + "SHLL", | |
81 | + "SARB", | |
82 | + "SARW", | |
83 | + "SARL", | |
84 | +}; | |
85 | + | |
86 | +static void cpu_x86_dump_state(void) | |
87 | +{ | |
88 | + int eflags; | |
89 | + eflags = cc_table[CC_OP].compute_all(); | |
90 | + eflags |= (DF & DIRECTION_FLAG); | |
91 | + fprintf(logfile, | |
92 | + "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n" | |
93 | + "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n" | |
94 | + "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n", | |
95 | + env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], | |
96 | + env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP], | |
97 | + env->cc_src, env->cc_dst, cc_op_str[env->cc_op], | |
98 | + eflags & DIRECTION_FLAG ? 'D' : '-', | |
99 | + eflags & CC_O ? 'O' : '-', | |
100 | + eflags & CC_S ? 'S' : '-', | |
101 | + eflags & CC_Z ? 'Z' : '-', | |
102 | + eflags & CC_A ? 'A' : '-', | |
103 | + eflags & CC_P ? 'P' : '-', | |
104 | + eflags & CC_C ? 'C' : '-' | |
105 | + ); | |
106 | +#if 1 | |
107 | + fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n", | |
108 | + (double)ST0, (double)ST1, (double)ST(2), (double)ST(3)); | |
109 | +#endif | |
110 | +} | |
111 | + | |
112 | +#endif | |
113 | + | |
114 | +void cpu_x86_tblocks_init(void) | |
115 | +{ | |
116 | + if (!code_gen_ptr) { | |
117 | + code_gen_ptr = code_gen_buffer; | |
118 | + } | |
119 | +} | |
120 | + | |
121 | +/* flush all the translation blocks */ | |
122 | +static void tb_flush(void) | |
123 | +{ | |
124 | + int i; | |
125 | +#ifdef DEBUG_FLUSH | |
126 | + printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", | |
127 | + code_gen_ptr - code_gen_buffer, | |
128 | + nb_tbs, | |
129 | + (code_gen_ptr - code_gen_buffer) / nb_tbs); | |
130 | +#endif | |
131 | + nb_tbs = 0; | |
132 | + for(i = 0;i < CODE_GEN_HASH_SIZE; i++) | |
133 | + tb_hash[i] = NULL; | |
134 | + code_gen_ptr = code_gen_buffer; | |
135 | + /* XXX: flush processor icache at this point */ | |
136 | +} | |
137 | + | |
138 | +/* find a translation block in the translation cache. If not found, | |
139 | + allocate a new one */ | |
140 | +static inline TranslationBlock *tb_find_and_alloc(unsigned long pc) | |
141 | +{ | |
142 | + TranslationBlock **ptb, *tb; | |
143 | + unsigned int h; | |
144 | + | |
145 | + h = pc & (CODE_GEN_HASH_SIZE - 1); | |
146 | + ptb = &tb_hash[h]; | |
147 | + for(;;) { | |
148 | + tb = *ptb; | |
149 | + if (!tb) | |
150 | + break; | |
151 | + if (tb->pc == pc) | |
152 | + return tb; | |
153 | + ptb = &tb->hash_next; | |
154 | + } | |
155 | + if (nb_tbs >= CODE_GEN_MAX_BLOCKS || | |
156 | + (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) | |
157 | + tb_flush(); | |
158 | + tb = &tbs[nb_tbs++]; | |
159 | + *ptb = tb; | |
160 | + tb->pc = pc; | |
161 | + tb->tc_ptr = NULL; | |
162 | + tb->hash_next = NULL; | |
163 | + return tb; | |
164 | +} | |
165 | + | |
166 | +int cpu_x86_exec(CPUX86State *env1) | |
167 | +{ | |
168 | + int saved_T0, saved_T1, saved_A0; | |
169 | + CPUX86State *saved_env; | |
170 | + int code_gen_size, ret; | |
171 | + void (*gen_func)(void); | |
172 | + TranslationBlock *tb; | |
173 | + uint8_t *tc_ptr; | |
174 | + | |
175 | + /* first we save global registers */ | |
176 | + saved_T0 = T0; | |
177 | + saved_T1 = T1; | |
178 | + saved_A0 = A0; | |
179 | + saved_env = env; | |
180 | + env = env1; | |
181 | + | |
182 | + /* prepare setjmp context for exception handling */ | |
183 | + if (setjmp(env->jmp_env) == 0) { | |
184 | + for(;;) { | |
185 | +#ifdef DEBUG_EXEC | |
186 | + if (loglevel) { | |
187 | + cpu_x86_dump_state(); | |
188 | + } | |
189 | +#endif | |
190 | + tb = tb_find_and_alloc((unsigned long)env->pc); | |
191 | + tc_ptr = tb->tc_ptr; | |
192 | + if (!tb->tc_ptr) { | |
193 | + /* if no translated code available, then translate it now */ | |
194 | + tc_ptr = code_gen_ptr; | |
195 | + cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, | |
196 | + &code_gen_size, (uint8_t *)env->pc); | |
197 | + tb->tc_ptr = tc_ptr; | |
198 | + code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); | |
199 | + } | |
200 | + /* execute the generated code */ | |
201 | + gen_func = (void *)tc_ptr; | |
202 | + gen_func(); | |
203 | + } | |
204 | + } | |
205 | + ret = env->exception_index; | |
206 | + | |
207 | + /* restore global registers */ | |
208 | + T0 = saved_T0; | |
209 | + T1 = saved_T1; | |
210 | + A0 = saved_A0; | |
211 | + env = saved_env; | |
212 | + return ret; | |
213 | +} | ... | ... |
exec-i386.h
0 → 100644
1 | +/* i386 execution defines */ | |
2 | + | |
3 | +typedef unsigned char uint8_t; | |
4 | +typedef unsigned short uint16_t; | |
5 | +typedef unsigned int uint32_t; | |
6 | +typedef unsigned long long uint64_t; | |
7 | + | |
8 | +typedef signed char int8_t; | |
9 | +typedef signed short int16_t; | |
10 | +typedef signed int int32_t; | |
11 | +typedef signed long long int64_t; | |
12 | + | |
13 | +#define bswap32(x) \ | |
14 | +({ \ | |
15 | + uint32_t __x = (x); \ | |
16 | + ((uint32_t)( \ | |
17 | + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ | |
18 | + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ | |
19 | + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ | |
20 | + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ | |
21 | +}) | |
22 | + | |
23 | +#define NULL 0 | |
24 | +#include <fenv.h> | |
25 | + | |
26 | +typedef struct FILE FILE; | |
27 | +extern FILE *logfile; | |
28 | +extern int loglevel; | |
29 | +extern int fprintf(FILE *, const char *, ...); | |
30 | + | |
31 | +#ifdef __i386__ | |
32 | +register unsigned int T0 asm("ebx"); | |
33 | +register unsigned int T1 asm("esi"); | |
34 | +register unsigned int A0 asm("edi"); | |
35 | +register struct CPUX86State *env asm("ebp"); | |
36 | +#endif | |
37 | +#ifdef __powerpc__ | |
38 | +register unsigned int T0 asm("r24"); | |
39 | +register unsigned int T1 asm("r25"); | |
40 | +register unsigned int A0 asm("r26"); | |
41 | +register struct CPUX86State *env asm("r27"); | |
42 | +#endif | |
43 | +#ifdef __arm__ | |
44 | +register unsigned int T0 asm("r4"); | |
45 | +register unsigned int T1 asm("r5"); | |
46 | +register unsigned int A0 asm("r6"); | |
47 | +register struct CPUX86State *env asm("r7"); | |
48 | +#endif | |
49 | +#ifdef __mips__ | |
50 | +register unsigned int T0 asm("s0"); | |
51 | +register unsigned int T1 asm("s1"); | |
52 | +register unsigned int A0 asm("s2"); | |
53 | +register struct CPUX86State *env asm("s3"); | |
54 | +#endif | |
55 | +#ifdef __sparc__ | |
56 | +register unsigned int T0 asm("l0"); | |
57 | +register unsigned int T1 asm("l1"); | |
58 | +register unsigned int A0 asm("l2"); | |
59 | +register struct CPUX86State *env asm("l3"); | |
60 | +#endif | |
61 | + | |
62 | +/* force GCC to generate only one epilog at the end of the function */ | |
63 | +#define FORCE_RET() asm volatile (""); | |
64 | + | |
65 | +#ifndef OPPROTO | |
66 | +#define OPPROTO | |
67 | +#endif | |
68 | + | |
69 | +#define xglue(x, y) x ## y | |
70 | +#define glue(x, y) xglue(x, y) | |
71 | + | |
72 | +#define EAX (env->regs[R_EAX]) | |
73 | +#define ECX (env->regs[R_ECX]) | |
74 | +#define EDX (env->regs[R_EDX]) | |
75 | +#define EBX (env->regs[R_EBX]) | |
76 | +#define ESP (env->regs[R_ESP]) | |
77 | +#define EBP (env->regs[R_EBP]) | |
78 | +#define ESI (env->regs[R_ESI]) | |
79 | +#define EDI (env->regs[R_EDI]) | |
80 | +#define PC (env->pc) | |
81 | +#define DF (env->df) | |
82 | + | |
83 | +#define CC_SRC (env->cc_src) | |
84 | +#define CC_DST (env->cc_dst) | |
85 | +#define CC_OP (env->cc_op) | |
86 | + | |
87 | +/* float macros */ | |
88 | +#define FT0 (env->ft0) | |
89 | +#define ST0 (env->fpregs[env->fpstt]) | |
90 | +#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7]) | |
91 | +#define ST1 ST(1) | |
92 | + | |
93 | +extern int __op_param1, __op_param2, __op_param3; | |
94 | +#define PARAM1 ((long)(&__op_param1)) | |
95 | +#define PARAM2 ((long)(&__op_param2)) | |
96 | +#define PARAM3 ((long)(&__op_param3)) | |
97 | + | |
98 | +#include "cpu-i386.h" | |
99 | + | |
100 | +typedef struct CCTable { | |
101 | + int (*compute_all)(void); /* return all the flags */ | |
102 | + int (*compute_c)(void); /* return the C flag */ | |
103 | +} CCTable; | |
104 | + | |
105 | +extern CCTable cc_table[]; | ... | ... |
linux-user/main.c
... | ... | @@ -87,7 +87,7 @@ int cpu_x86_inl(int addr) |
87 | 87 | |
88 | 88 | void usage(void) |
89 | 89 | { |
90 | - printf("gemu version 0.1, Copyright (c) 2003 Fabrice Bellard\n" | |
90 | + printf("gemu version" GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n" | |
91 | 91 | "usage: gemu [-d] program [arguments...]\n" |
92 | 92 | "Linux x86 emulator\n" |
93 | 93 | ); | ... | ... |
linux-user/syscall.c
op-i386.c
1 | -#define DEBUG_EXEC | |
2 | - | |
3 | -typedef unsigned char uint8_t; | |
4 | -typedef unsigned short uint16_t; | |
5 | -typedef unsigned int uint32_t; | |
6 | -typedef unsigned long long uint64_t; | |
7 | - | |
8 | -typedef signed char int8_t; | |
9 | -typedef signed short int16_t; | |
10 | -typedef signed int int32_t; | |
11 | -typedef signed long long int64_t; | |
12 | - | |
13 | -#define bswap32(x) \ | |
14 | -({ \ | |
15 | - uint32_t __x = (x); \ | |
16 | - ((uint32_t)( \ | |
17 | - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ | |
18 | - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ | |
19 | - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ | |
20 | - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ | |
21 | -}) | |
22 | - | |
23 | -#define NULL 0 | |
24 | -#include <fenv.h> | |
25 | - | |
26 | -typedef struct FILE FILE; | |
27 | -extern FILE *logfile; | |
28 | -extern int loglevel; | |
29 | -extern int fprintf(FILE *, const char *, ...); | |
30 | - | |
31 | -#ifdef __i386__ | |
32 | -register unsigned int T0 asm("ebx"); | |
33 | -register unsigned int T1 asm("esi"); | |
34 | -register unsigned int A0 asm("edi"); | |
35 | -register struct CPUX86State *env asm("ebp"); | |
36 | -#endif | |
37 | -#ifdef __powerpc__ | |
38 | -register unsigned int T0 asm("r24"); | |
39 | -register unsigned int T1 asm("r25"); | |
40 | -register unsigned int A0 asm("r26"); | |
41 | -register struct CPUX86State *env asm("r27"); | |
42 | -#endif | |
43 | -#ifdef __arm__ | |
44 | -register unsigned int T0 asm("r4"); | |
45 | -register unsigned int T1 asm("r5"); | |
46 | -register unsigned int A0 asm("r6"); | |
47 | -register struct CPUX86State *env asm("r7"); | |
48 | -#endif | |
49 | -#ifdef __mips__ | |
50 | -register unsigned int T0 asm("s0"); | |
51 | -register unsigned int T1 asm("s1"); | |
52 | -register unsigned int A0 asm("s2"); | |
53 | -register struct CPUX86State *env asm("s3"); | |
54 | -#endif | |
55 | -#ifdef __sparc__ | |
56 | -register unsigned int T0 asm("l0"); | |
57 | -register unsigned int T1 asm("l1"); | |
58 | -register unsigned int A0 asm("l2"); | |
59 | -register struct CPUX86State *env asm("l3"); | |
60 | -#endif | |
61 | - | |
62 | -/* force GCC to generate only one epilog at the end of the function */ | |
63 | -#define FORCE_RET() asm volatile (""); | |
64 | - | |
65 | -#ifndef OPPROTO | |
66 | -#define OPPROTO | |
67 | -#endif | |
68 | - | |
69 | -#define xglue(x, y) x ## y | |
70 | -#define glue(x, y) xglue(x, y) | |
71 | - | |
72 | -#define EAX (env->regs[R_EAX]) | |
73 | -#define ECX (env->regs[R_ECX]) | |
74 | -#define EDX (env->regs[R_EDX]) | |
75 | -#define EBX (env->regs[R_EBX]) | |
76 | -#define ESP (env->regs[R_ESP]) | |
77 | -#define EBP (env->regs[R_EBP]) | |
78 | -#define ESI (env->regs[R_ESI]) | |
79 | -#define EDI (env->regs[R_EDI]) | |
80 | -#define PC (env->pc) | |
81 | -#define DF (env->df) | |
82 | - | |
83 | -#define CC_SRC (env->cc_src) | |
84 | -#define CC_DST (env->cc_dst) | |
85 | -#define CC_OP (env->cc_op) | |
86 | - | |
87 | -/* float macros */ | |
88 | -#define FT0 (env->ft0) | |
89 | -#define ST0 (env->fpregs[env->fpstt]) | |
90 | -#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7]) | |
91 | -#define ST1 ST(1) | |
92 | - | |
93 | -extern int __op_param1, __op_param2, __op_param3; | |
94 | -#define PARAM1 ((long)(&__op_param1)) | |
95 | -#define PARAM2 ((long)(&__op_param2)) | |
96 | -#define PARAM3 ((long)(&__op_param3)) | |
97 | - | |
98 | -#include "cpu-i386.h" | |
99 | - | |
100 | -typedef struct CCTable { | |
101 | - int (*compute_all)(void); /* return all the flags */ | |
102 | - int (*compute_c)(void); /* return the C flag */ | |
103 | -} CCTable; | |
1 | +/* | |
2 | + * i386 micro operations | |
3 | + * | |
4 | + * Copyright (c) 2003 Fabrice Bellard | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | +#include "exec-i386.h" | |
104 | 21 | |
105 | 22 | /* NOTE: data are not static to force relocation generation by GCC */ |
106 | -extern CCTable cc_table[]; | |
107 | 23 | |
108 | 24 | uint8_t parity_table[256] = { |
109 | 25 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, |
... | ... | @@ -1878,100 +1794,3 @@ void OPPROTO op_fldcw_A0(void) |
1878 | 1794 | fesetround(rnd_type); |
1879 | 1795 | } |
1880 | 1796 | |
1881 | -/* main execution loop */ | |
1882 | -uint8_t code_gen_buffer[65536]; | |
1883 | - | |
1884 | -#ifdef DEBUG_EXEC | |
1885 | -static const char *cc_op_str[] = { | |
1886 | - "DYNAMIC", | |
1887 | - "EFLAGS", | |
1888 | - "MUL", | |
1889 | - "ADDB", | |
1890 | - "ADDW", | |
1891 | - "ADDL", | |
1892 | - "ADCB", | |
1893 | - "ADCW", | |
1894 | - "ADCL", | |
1895 | - "SUBB", | |
1896 | - "SUBW", | |
1897 | - "SUBL", | |
1898 | - "SBBB", | |
1899 | - "SBBW", | |
1900 | - "SBBL", | |
1901 | - "LOGICB", | |
1902 | - "LOGICW", | |
1903 | - "LOGICL", | |
1904 | - "INCB", | |
1905 | - "INCW", | |
1906 | - "INCL", | |
1907 | - "DECB", | |
1908 | - "DECW", | |
1909 | - "DECL", | |
1910 | - "SHLB", | |
1911 | - "SHLW", | |
1912 | - "SHLL", | |
1913 | - "SARB", | |
1914 | - "SARW", | |
1915 | - "SARL", | |
1916 | -}; | |
1917 | -#endif | |
1918 | - | |
1919 | -int cpu_x86_exec(CPUX86State *env1) | |
1920 | -{ | |
1921 | - int saved_T0, saved_T1, saved_A0; | |
1922 | - CPUX86State *saved_env; | |
1923 | - int code_gen_size, ret; | |
1924 | - void (*gen_func)(void); | |
1925 | - | |
1926 | - /* first we save global registers */ | |
1927 | - saved_T0 = T0; | |
1928 | - saved_T1 = T1; | |
1929 | - saved_A0 = A0; | |
1930 | - saved_env = env; | |
1931 | - env = env1; | |
1932 | - | |
1933 | - /* prepare setjmp context for exception handling */ | |
1934 | - if (setjmp(env->jmp_env) == 0) { | |
1935 | - for(;;) { | |
1936 | -#ifdef DEBUG_EXEC | |
1937 | - if (loglevel) { | |
1938 | - int eflags; | |
1939 | - eflags = cc_table[CC_OP].compute_all(); | |
1940 | - eflags |= (DF & DIRECTION_FLAG); | |
1941 | - fprintf(logfile, | |
1942 | - "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n" | |
1943 | - "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n" | |
1944 | - "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n", | |
1945 | - env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], | |
1946 | - env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP], | |
1947 | - env->cc_src, env->cc_dst, cc_op_str[env->cc_op], | |
1948 | - eflags & DIRECTION_FLAG ? 'D' : '-', | |
1949 | - eflags & CC_O ? 'O' : '-', | |
1950 | - eflags & CC_S ? 'S' : '-', | |
1951 | - eflags & CC_Z ? 'Z' : '-', | |
1952 | - eflags & CC_A ? 'A' : '-', | |
1953 | - eflags & CC_P ? 'P' : '-', | |
1954 | - eflags & CC_C ? 'C' : '-' | |
1955 | - ); | |
1956 | -#if 1 | |
1957 | - fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n", | |
1958 | - (double)ST0, (double)ST1, (double)ST(2), (double)ST(3)); | |
1959 | -#endif | |
1960 | - } | |
1961 | -#endif | |
1962 | - cpu_x86_gen_code(code_gen_buffer, sizeof(code_gen_buffer), | |
1963 | - &code_gen_size, (uint8_t *)env->pc); | |
1964 | - /* execute the generated code */ | |
1965 | - gen_func = (void *)code_gen_buffer; | |
1966 | - gen_func(); | |
1967 | - } | |
1968 | - } | |
1969 | - ret = env->exception_index; | |
1970 | - | |
1971 | - /* restore global registers */ | |
1972 | - T0 = saved_T0; | |
1973 | - T1 = saved_T1; | |
1974 | - A0 = saved_A0; | |
1975 | - env = saved_env; | |
1976 | - return ret; | |
1977 | -} | ... | ... |
tests/Makefile
1 | -CC=gcc | |
1 | +include ../config.mak | |
2 | + | |
2 | 3 | CFLAGS=-Wall -O2 -g |
3 | 4 | LDFLAGS= |
4 | 5 | |
6 | +ifeq ($(ARCH),i386) | |
5 | 7 | TESTS=hello test2 sha1 test-i386 |
6 | -TESTS+=op-i386.o #op-i386.o op-ppc.o op-arm.o op-mips.o op-sparc.o | |
8 | +endif | |
7 | 9 | |
8 | 10 | GEMU=../gemu |
9 | 11 | |
... | ... | @@ -24,22 +26,6 @@ test: test-i386 |
24 | 26 | $(GEMU) test-i386 > test-i386.out |
25 | 27 | @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi |
26 | 28 | |
27 | -# dyngen tests | |
28 | -op-i386.o: op.c | |
29 | - gcc $(CFLAGS) -c -o $@ $< | |
30 | - | |
31 | -op-ppc.o: op.c | |
32 | - powerpc-linux-gcc $(CFLAGS) -c -o $@ $< | |
33 | - | |
34 | -op-arm.o: op.c | |
35 | - arm-linux-gcc $(CFLAGS) -c -o $@ $< | |
36 | - | |
37 | -op-mips.o: op.c | |
38 | - mips-linux-gcc $(CFLAGS) -mno-abicalls -c -o $@ $< | |
39 | - | |
40 | -op-sparc.o: op.c | |
41 | - sparc-linux-gcc $(CFLAGS) -mflat -c -o $@ $< | |
42 | - | |
43 | 29 | # speed test |
44 | 30 | sha1: sha1.c |
45 | 31 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< |
... | ... | @@ -48,6 +34,5 @@ speed: sha1 |
48 | 34 | time ./sha1 |
49 | 35 | time $(GEMU) sha1 |
50 | 36 | |
51 | -# interpreter test | |
52 | -interp: interp.c interploop.c | |
53 | - $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -o $@ $^ | |
37 | +clean: | |
38 | + rm -f *~ *.o $(TESTS) | ... | ... |
thunk.h
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 | #define THUNK_H |
3 | 3 | |
4 | 4 | #include <inttypes.h> |
5 | -#include <endian.h> | |
5 | +#include "config.h" | |
6 | 6 | |
7 | 7 | #ifdef HAVE_BYTESWAP_H |
8 | 8 | #include <byteswap.h> |
... | ... | @@ -42,11 +42,6 @@ |
42 | 42 | |
43 | 43 | #endif |
44 | 44 | |
45 | -#undef WORDS_BIGENDIAN | |
46 | -#if __BYTE_ORDER == __BIG_ENDIAN | |
47 | -#define WORDS_BIGENDIAN | |
48 | -#endif | |
49 | - | |
50 | 45 | #ifdef WORDS_BIGENDIAN |
51 | 46 | #define BSWAP_NEEDED |
52 | 47 | #endif | ... | ... |
translate-i386.c
1 | +/* | |
2 | + * i386 translation | |
3 | + * | |
4 | + * Copyright (c) 2003 Fabrice Bellard | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
1 | 20 | #include <stdarg.h> |
2 | 21 | #include <stdlib.h> |
3 | 22 | #include <stdio.h> |
... | ... | @@ -2591,6 +2610,8 @@ CPUX86State *cpu_x86_init(void) |
2591 | 2610 | CPUX86State *env; |
2592 | 2611 | int i; |
2593 | 2612 | |
2613 | + cpu_x86_tblocks_init(); | |
2614 | + | |
2594 | 2615 | env = malloc(sizeof(CPUX86State)); |
2595 | 2616 | if (!env) |
2596 | 2617 | return NULL; | ... | ... |