1#include <stdio.h>
2#include <assert.h>
3#include <string.h>
4
5/* Test case supplied by Sergei Trofimovich */
6
7/*
8 * Real life example (MSDOS file INFO.EXE) has code like this
9 *
10 * I don't know why author/compiler done code like this. Only guess:
11 *    guess 1 (strong :]):
12 *      This archaic code was used by dynamic memory regeneration
13 *      handler (according to code around it's called from
14 *      interrupt handler).
15 *
16 *    guess 2: cache flush (whether processors had caches at that time?)
17 *
18 * a disasmed snippet:
19 *
20 *   mov     byte ptr [bx], 0FFh
21 *   sti
22 *   mov     cx, 0FFFFh         ; 65535
23 *   rep lods byte ptr es:[si]
24 *   jcxz    short somewhere_1  ; it seems code could be
25 *                              ; interrupted here
26 *
27 *   call    something_2
28 *   cmp     dx, 4
29 *   mov     byte ptr [bx], 0
30 *   jmp     somewhere_3
31 */
32
33#define GET_BIT(var, bit_no) ((var >> bit_no) & 1)
34
35static char sz_eflags[] = "        "; // 8 spaces
36static void pp_eflags (unsigned int _8bits_eflags)
37{
38  assert (_8bits_eflags >= 0);
39  assert (_8bits_eflags <= 0xFF);
40  sz_eflags[0] = GET_BIT(_8bits_eflags, 7) ? 'S' : ' ';
41  sz_eflags[1] = GET_BIT(_8bits_eflags, 6) ? 'Z' : ' ';
42  sz_eflags[3] = GET_BIT(_8bits_eflags, 4) ? 'A' : ' ';
43  sz_eflags[5] = GET_BIT(_8bits_eflags, 2) ? 'P' : ' ';
44  sz_eflags[7] = GET_BIT(_8bits_eflags, 0) ? 'C' : ' ';
45}
46
47#define EMIT_CALL(dir_insn, insn, in_eax, in_esi, in_eflags, out_eax, out_esi, out_eflags, count) \
48  asm volatile(                             \
49    "movl %3, %%eax \t\n"                       \
50    "sahf       \t\n" /* loading our eflags */          \
51    "movl %4, %%eax \t\n"                       \
52    "movl %5, %%esi \t\n"                       \
53    "movl %6, %%ecx \t\n"                       \
54                                    \
55    dir_insn "\t\n"                         \
56    insn "\t\n"                           \
57                                    \
58    /* return result */                       \
59    "movl %%eax, %0 \t\n"                       \
60    "lahf       \t\n"                       \
61    "movl %%eax, %1 \t\n"                       \
62    "movl %%esi, %2 \t\n"                       \
63    "cld \t\n" \
64    : "=d"(out_eax),                        \
65      "=b"(out_eflags),                       \
66      "=r"(out_esi)                         \
67                                    \
68    : "m"(in_eflags),                         \
69      "m"(in_eax),                          \
70      "m"(in_esi),                          \
71      "q"(count)                          \
72                                    \
73    : "%eax", "%esi", "%ecx", "cc" /* we mess up EFLAGS */);
74
75const signed char  b_mem_buff[] = {-4, -3, -2, -1, 0xaa, 1, 2, 3, 4};
76const signed long  l_mem_buff[] = {-4, -3, -2, -1, 0xaa, 1, 2, 3, 4};
77const signed short w_mem_buff[] = {-4, -3, -2, -1, 0xaa, 1, 2, 3, 4};
78
79const int lens[] = { 4, 3, 2, 1, 0, 0, 1, 2, 3, 4};
80
81int main ()
82{
83  const signed char * b_center = (signed char *) memchr(b_mem_buff, 0xaa, sizeof (b_mem_buff));
84  const signed char * w_center = (signed char *) memchr(w_mem_buff, 0xaa, sizeof (w_mem_buff));
85  const signed char * l_center = (signed char *) memchr(l_mem_buff, 0xaa, sizeof (l_mem_buff));
86
87  int insn;
88  for (insn = 0; insn < 4; ++insn) //b,w[rep/addr],d,w[addr/rep]
89  {
90    int idx;
91    for (idx = 0; idx < sizeof (lens)/sizeof(lens[0]); ++idx)
92    {
93      unsigned int eflags;
94      unsigned int eax = 0x12348765;
95      unsigned int esi;
96      const char * i_name = NULL;
97      unsigned int resulting_eflags;
98      unsigned int resulting_eax;
99      unsigned int resulting_esi;
100      int len;
101      int df;
102
103      switch (insn)
104      {
105        case 0: //b
106          esi = (unsigned int) b_center;
107          i_name = "lodsb";
108          break;
109        case 1: //w
110          esi = (unsigned int) w_center;
111          i_name = "lodsw[rep/addr]";
112          break;
113        case 2: //d
114          esi = (unsigned int) l_center;
115          i_name = "lodsl";
116          break;
117        case 3: //w
118          esi = (unsigned int) w_center;
119          i_name = "lodsw[addr/rep]";
120          break;
121      }
122
123      eflags = 0;
124      pp_eflags ((eflags >> 8) & 0xFF); // scratching off AH
125      printf ("REP %s (EAX = %08X, EFLAGS = %s) => ", i_name, eax, sz_eflags);
126
127      resulting_eflags = 0;
128      resulting_eax = 0;
129
130      len = lens[idx];
131      df  = (idx >= (sizeof(lens)/sizeof(lens[0]))/2);
132
133      switch (insn)
134      {
135        case 0: //b
136          if (df)
137          {
138            EMIT_CALL("cld",
139                  "rep lodsb",
140                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
141                  len);
142          }
143          else
144          {
145            EMIT_CALL("std",
146                  "rep lodsb",
147                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
148                  len);
149          }
150          break;
151        case 1: //w[rep/addr]
152          if (df)
153          {
154            EMIT_CALL("cld",
155                  // "rep lodsw",
156                  // explicit: rep-pref addr-pref op
157                  ".byte 0x66,0xf3,0xad",
158                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
159                  len);
160          }
161          else
162          {
163            EMIT_CALL("std",
164                  // "rep lodsw",
165                  // explicit: rep-pref addr-pref op
166                  ".byte 0x66,0xf3,0xad",
167                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
168                  len);
169          }
170          break;
171        case 2: //d
172          if (df)
173          {
174            EMIT_CALL("cld",
175                  "rep lodsl",
176                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
177                  len);
178          }
179          else
180          {
181            EMIT_CALL("std",
182                  "rep lodsl",
183                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
184                  len);
185          }
186          break;
187        case 3: //w[addr/rep]
188          if (df)
189          {
190            EMIT_CALL("cld",
191                  // "rep lodsw",
192                  // explicit: rep-pref addr-pref op
193                  ".byte 0xf3,0x66,0xad",
194                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
195                  len);
196          }
197          else
198          {
199            EMIT_CALL("std",
200                  // "rep lodsw",
201                  // explicit: rep-pref addr-pref op
202                  ".byte 0xf3,0x66,0xad",
203                  eax, esi, eflags, resulting_eax, resulting_esi, resulting_eflags,
204                  len);
205          }
206          break;
207      }
208      printf ("DF = %d, count = %2d ", df, len);
209      pp_eflags ((resulting_eflags >> 8) & 0xFF); // scratching off AH
210      printf ("(EAX = %08X, EFLAGS = %s)\n", resulting_eax, sz_eflags);
211    }
212  }
213  return 0;
214}
215