1 /*---------------------------------------------------------------------------+
2 | get_address.c |
3 | |
4 | Get the effective address from an FPU instruction. |
5 | |
6 | Copyright (C) 1992,1993 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
8 | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
9 | |
10 | |
11 +---------------------------------------------------------------------------*/
12
13 /*---------------------------------------------------------------------------+
14 | Note: |
15 | The file contains code which accesses user memory. |
16 | Emulator static data may change when user memory is accessed, due to |
17 | other processes using the emulator while swapping is in progress. |
18 +---------------------------------------------------------------------------*/
19
20
21 #include <linux/stddef.h>
22
23 #include <asm/segment.h>
24
25 #include "fpu_system.h"
26 #include "exception.h"
27 #include "fpu_emu.h"
28
29 static int reg_offset[] = {
30 offsetof(struct info,___eax),
31 offsetof(struct info,___ecx),
32 offsetof(struct info,___edx),
33 offsetof(struct info,___ebx),
34 offsetof(struct info,___esp),
35 offsetof(struct info,___ebp),
36 offsetof(struct info,___esi),
37 offsetof(struct info,___edi)
38 };
39
40 #define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
41
42
43 void *FPU_data_address;
44
45
46 /* Decode the SIB byte. This function assumes mod != 0 */
47 static void *sib(int mod)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
48 {
49 unsigned char ss,index,base;
50 long offset;
51
52 RE_ENTRANT_CHECK_OFF
53 base = get_fs_byte((char *) FPU_EIP); /* The SIB byte */
54 RE_ENTRANT_CHECK_ON
55 FPU_EIP++;
56 ss = base >> 6;
57 index = (base >> 3) & 7;
58 base &= 7;
59
60 if ((mod == 0) && (base == 5))
61 offset = 0; /* No base register */
62 else
63 offset = REG_(base);
64
65 if (index == 4)
66 {
67 /* No index register */
68 /* A non-zero ss is illegal */
69 if ( ss )
70 EXCEPTION(EX_Invalid);
71 }
72 else
73 {
74 offset += (REG_(index)) << ss;
75 }
76
77 if (mod == 1)
78 {
79 /* 8 bit signed displacement */
80 RE_ENTRANT_CHECK_OFF
81 offset += (signed char) get_fs_byte((char *) FPU_EIP);
82 RE_ENTRANT_CHECK_ON
83 FPU_EIP++;
84 }
85 else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
86 {
87 /* 32 bit displacment */
88 RE_ENTRANT_CHECK_OFF
89 offset += (signed) get_fs_long((unsigned long *) FPU_EIP);
90 RE_ENTRANT_CHECK_ON
91 FPU_EIP += 4;
92 }
93
94 return (void *) offset;
95 }
96
97
98 /*
99 MOD R/M byte: MOD == 3 has a special use for the FPU
100 SIB byte used iff R/M = 100b
101
102 7 6 5 4 3 2 1 0
103 ..... ......... .........
104 MOD OPCODE(2) R/M
105
106
107 SIB byte
108
109 7 6 5 4 3 2 1 0
110 ..... ......... .........
111 SS INDEX BASE
112
113 */
114
115 void get_address(unsigned char FPU_modrm)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
116 {
117 unsigned char mod;
118 long *cpu_reg_ptr;
119 int offset = 0; /* Initialized just to stop compiler warnings. */
120
121 mod = (FPU_modrm >> 6) & 3;
122
123 if (FPU_rm == 4 && mod != 3)
124 {
125 FPU_data_address = sib(mod);
126 return;
127 }
128
129 cpu_reg_ptr = & REG_(FPU_rm);
130 switch (mod)
131 {
132 case 0:
133 if (FPU_rm == 5)
134 {
135 /* Special case: disp32 */
136 RE_ENTRANT_CHECK_OFF
137 offset = get_fs_long((unsigned long *) FPU_EIP);
138 RE_ENTRANT_CHECK_ON
139 FPU_EIP += 4;
140 FPU_data_address = (void *) offset;
141 return;
142 }
143 else
144 {
145 FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
146 of the cpu register */
147 return;
148 }
149 case 1:
150 /* 8 bit signed displacement */
151 RE_ENTRANT_CHECK_OFF
152 offset = (signed char) get_fs_byte((char *) FPU_EIP);
153 RE_ENTRANT_CHECK_ON
154 FPU_EIP++;
155 break;
156 case 2:
157 /* 32 bit displacement */
158 RE_ENTRANT_CHECK_OFF
159 offset = (signed) get_fs_long((unsigned long *) FPU_EIP);
160 RE_ENTRANT_CHECK_ON
161 FPU_EIP += 4;
162 break;
163 case 3:
164 /* Not legal for the FPU */
165 EXCEPTION(EX_Invalid);
166 }
167
168 FPU_data_address = offset + (char *)*cpu_reg_ptr;
169 }