https://github.com/akkartik/mu/blob/main/023float.cc
  1 //: floating-point operations
  2 
  3 //:: copy
  4 
  5 :(before "End Initialize Op Names")
  6 put_new(Name_f3_0f, "10", "copy xm32 to x32 (movss)");
  7 put_new(Name_f3_0f, "11", "copy x32 to xm32 (movss)");
  8 
  9 :(code)
 10 void test_copy_x32_to_x32() {
 11   Xmm[3] = 0.5;
 12   run(
 13       "== code 0x1\n"  // code segment
 14       // op     ModR/M  SIB   displacement  immediate
 15       "f3 0f 11 d8                                    \n"  // copy XMM3 to XMM0
 16       // ModR/M in binary: 11 (direct mode) 011 (src XMM3) 000 (dest XMM0)
 17   );
 18   CHECK_TRACE_CONTENTS(
 19       "run: copy XMM3 to x/m32\n"
 20       "run: x/m32 is XMM0\n"
 21       "run: storing 0.5\n"
 22   );
 23 }
 24 
 25 :(before "End Three-Byte Opcodes Starting With f3 0f")
 26 case 0x10: {  // copy x/m32 to x32
 27   const uint8_t modrm = next();
 28   const uint8_t rdest = (modrm>>3)&0x7;
 29   trace(Callstack_depth+1, "run") << "copy x/m32 to " << Xname[rdest] << end();
 30   float* src = effective_address_float(modrm);
 31   Xmm[rdest] = *src;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(float) == 4 on the host as well.
 32   trace(Callstack_depth+1, "run") << "storing " << Xmm[rdest] << end();
 33   break;
 34 }
 35 case 0x11: {  // copy x32 to x/m32
 36   const uint8_t modrm = next();
 37   const uint8_t rsrc = (modrm>>3)&0x7;
 38   trace(Callstack_depth+1, "run") << "copy " << Xname[rsrc] << " to x/m32" << end();
 39   float* dest = effective_address_float(modrm);
 40   *dest = Xmm[rsrc];  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(float) == 4 on the host as well.
 41   trace(Callstack_depth+1, "run") << "storing " << *dest << end();
 42   break;
 43 }
 44 
 45 :(code)
 46 void test_copy_x32_to_mem_at_xm32() {
 47   Xmm[3] = 0.5;
 48   Reg[EAX].i = 0x60;
 49   run(
 50       "== code 0x1\n"
 51       // op     ModR/M  SIB   displacement  immediate
 52       "f3 0f 11 18                                    \n"  // copy XMM3 to *EAX
 53       // ModR/M in binary: 00 (indirect mode) 011 (src XMM3) 000 (dest EAX)
 54   );
 55   CHECK_TRACE_CONTENTS(
 56       "run: copy XMM3 to x/m32\n"
 57       "run: effective address is 0x00000060 (EAX)\n"
 58       "run: storing 0.5\n"
 59   );
 60 }
 61 
 62 void test_copy_mem_at_xm32_to_x32() {
 63   Reg[EAX].i = 0x2000;
 64   run(
 65       "== code 0x1\n"
 66       // op     ModR/M  SIB   displacement  immediate
 67       "f3 0f 10 18                                    \n"  // copy *EAX to XMM3
 68       "== data 0x2000\n"
 69       "00 00 00 3f\n"  // 0x3f000000 = 0.5
 70   );
 71   CHECK_TRACE_CONTENTS(
 72       "run: copy x/m32 to XMM3\n"
 73       "run: effective address is 0x00002000 (EAX)\n"
 74       "run: storing 0.5\n"
 75   );
 76 }
 77 
 78 //:: convert to floating point
 79 
 80 :(before "End Initialize Op Names")
 81 put_new(Name_f3_0f, "2a", "convert integer to floating-point (cvtsi2ss)");
 82 
 83 :(code)
 84 void test_cvtsi2ss() {
 85   Reg[EAX].i = 10;
 86   run(
 87       "== code 0x1\n"
 88       // op     ModR/M  SIB   displacement  immediate
 89       "f3 0f 2a c0                                    \n"
 90       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 000 (EAX)
 91   );
 92   CHECK_TRACE_CONTENTS(
 93       "run: convert r/m32 to XMM0\n"
 94       "run: r/m32 is EAX\n"
 95       "run: XMM0 is now 10\n"
 96   );
 97 }
 98 
 99 :(before "End Three-Byte Opcodes Starting With f3 0f")
100 case 0x2a: {  // convert integer to float
101   const uint8_t modrm = next();
102   const uint8_t dest = (modrm>>3)&0x7;
103   trace(Callstack_depth+1, "run") << "convert r/m32 to " << Xname[dest] << end();
104   const int32_t* src = effective_address(modrm);
105   Xmm[dest] = *src;
106   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
107   break;
108 }
109 
110 //:: convert floating point to int
111 
112 :(before "End Initialize Op Names")
113 put_new(Name_f3_0f, "2d", "convert floating-point to int (cvtss2si)");
114 put_new(Name_f3_0f, "2c", "truncate floating-point to int (cvttss2si)");
115 
116 :(code)
117 void test_cvtss2si() {
118   Xmm[0] = 9.8;
119   run(
120       "== code 0x1\n"
121       // op     ModR/M  SIB   displacement  immediate
122       "f3 0f 2d c0                                    \n"
123       // ModR/M in binary: 11 (direct mode) 000 (EAX) 000 (XMM0)
124   );
125   CHECK_TRACE_CONTENTS(
126       "run: convert x/m32 to EAX\n"
127       "run: x/m32 is XMM0\n"
128       "run: EAX is now 0x0000000a\n"
129   );
130 }
131 
132 :(before "End Three-Byte Opcodes Starting With f3 0f")
133 case 0x2d: {  // convert float to integer
134   const uint8_t modrm = next();
135   const uint8_t dest = (modrm>>3)&0x7;
136   trace(Callstack_depth+1, "run") << "convert x/m32 to " << rname(dest) << end();
137   const float* src = effective_address_float(modrm);
138   Reg[dest].i = round(*src);
139   trace(Callstack_depth+1, "run") << rname(dest) << " is now 0x" << HEXWORD << Reg[dest].i << end();
140   break;
141 }
142 
143 :(code)
144 void test_cvttss2si() {
145   Xmm[0] = 9.8;
146   run(
147       "== code 0x1\n"
148       // op     ModR/M  SIB   displacement  immediate
149       "f3 0f 2c c0                                    \n"
150       // ModR/M in binary: 11 (direct mode) 000 (EAX) 000 (XMM0)
151   );
152   CHECK_TRACE_CONTENTS(
153       "run: truncate x/m32 to EAX\n"
154       "run: x/m32 is XMM0\n"
155       "run: EAX is now 0x00000009\n"
156   );
157 }
158 
159 :(before "End Three-Byte Opcodes Starting With f3 0f")
160 case 0x2c: {  // truncate float to integer
161   const uint8_t modrm = next();
162   const uint8_t dest = (modrm>>3)&0x7;
163   trace(Callstack_depth+1, "run") << "truncate x/m32 to " << rname(dest) << end();
164   const float* src = effective_address_float(modrm);
165   Reg[dest].i = trunc(*src);
166   trace(Callstack_depth+1, "run") << rname(dest) << " is now 0x" << HEXWORD << Reg[dest].i << end();
167   break;
168 }
169 
170 //:: add
171 
172 :(before "End Initialize Op Names")
173 put_new(Name_f3_0f, "58", "add floats (addss)");
174 
175 :(code)
176 void test_addss() {
177   Xmm[0] = 3.0;
178   Xmm[1] = 2.0;
179   run(
180       "== code 0x1\n"
181       // op     ModR/M  SIB   displacement  immediate
182       "f3 0f 58 c1                                    \n"
183       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
184   );
185   CHECK_TRACE_CONTENTS(
186       "run: add x/m32 to XMM0\n"
187       "run: x/m32 is XMM1\n"
188       "run: XMM0 is now 5\n"
189   );
190 }
191 
192 :(before "End Three-Byte Opcodes Starting With f3 0f")
193 case 0x58: {  // add x/m32 to x32
194   const uint8_t modrm = next();
195   const uint8_t dest = (modrm>>3)&0x7;
196   trace(Callstack_depth+1, "run") << "add x/m32 to " << Xname[dest] << end();
197   const float* src = effective_address_float(modrm);
198   Xmm[dest] += *src;
199   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
200   break;
201 }
202 
203 //:: subtract
204 
205 :(before "End Initialize Op Names")
206 put_new(Name_f3_0f, "5c", "subtract floats (subss)");
207 
208 :(code)
209 void test_subss() {
210   Xmm[0] = 3.0;
211   Xmm[1] = 2.0;
212   run(
213       "== code 0x1\n"
214       // op     ModR/M  SIB   displacement  immediate
215       "f3 0f 5c c1                                    \n"
216       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
217   );
218   CHECK_TRACE_CONTENTS(
219       "run: subtract x/m32 from XMM0\n"
220       "run: x/m32 is XMM1\n"
221       "run: XMM0 is now 1\n"
222   );
223 }
224 
225 :(before "End Three-Byte Opcodes Starting With f3 0f")
226 case 0x5c: {  // subtract x/m32 from x32
227   const uint8_t modrm = next();
228   const uint8_t dest = (modrm>>3)&0x7;
229   trace(Callstack_depth+1, "run") << "subtract x/m32 from " << Xname[dest] << end();
230   const float* src = effective_address_float(modrm);
231   Xmm[dest] -= *src;
232   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
233   break;
234 }
235 
236 //:: multiply
237 
238 :(before "End Initialize Op Names")
239 put_new(Name_f3_0f, "59", "multiply floats (mulss)");
240 
241 :(code)
242 void test_mulss() {
243   Xmm[0] = 3.0;
244   Xmm[1] = 2.0;
245   run(
246       "== code 0x1\n"
247       // op     ModR/M  SIB   displacement  immediate
248       "f3 0f 59 c1                                    \n"
249       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
250   );
251   CHECK_TRACE_CONTENTS(
252       "run: multiply XMM0 by x/m32\n"
253       "run: x/m32 is XMM1\n"
254       "run: XMM0 is now 6\n"
255   );
256 }
257 
258 :(before "End Three-Byte Opcodes Starting With f3 0f")
259 case 0x59: {  // multiply x32 by x/m32
260   const uint8_t modrm = next();
261   const uint8_t dest = (modrm>>3)&0x7;
262   trace(Callstack_depth+1, "run") << "multiply " << Xname[dest] << " by x/m32" << end();
263   const float* src = effective_address_float(modrm);
264   Xmm[dest] *= *src;
265   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
266   break;
267 }
268 
269 //:: divide
270 
271 :(before "End Initialize Op Names")
272 put_new(Name_f3_0f, "5e", "divide floats (divss)");
273 
274 :(code)
275 void test_divss() {
276   Xmm[0] = 3.0;
277   Xmm[1] = 2.0;
278   run(
279       "== code 0x1\n"
280       // op     ModR/M  SIB   displacement  immediate
281       "f3 0f 5e c1                                    \n"
282       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
283   );
284   CHECK_TRACE_CONTENTS(
285       "run: divide XMM0 by x/m32\n"
286       "run: x/m32 is XMM1\n"
287       "run: XMM0 is now 1.5\n"
288   );
289 }
290 
291 :(before "End Three-Byte Opcodes Starting With f3 0f")
292 case 0x5e: {  // divide x32 by x/m32
293   const uint8_t modrm = next();
294   const uint8_t dest = (modrm>>3)&0x7;
295   trace(Callstack_depth+1, "run") << "divide " << Xname[dest] << " by x/m32" << end();
296   const float* src = effective_address_float(modrm);
297   Xmm[dest] /= *src;
298   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
299   break;
300 }
301 
302 //:: min
303 
304 :(before "End Initialize Op Names")
305 put_new(Name_f3_0f, "5d", "minimum of two floats (minss)");
306 
307 :(code)
308 void test_minss() {
309   Xmm[0] = 3.0;
310   Xmm[1] = 2.0;
311   run(
312       "== code 0x1\n"
313       // op     ModR/M  SIB   displacement  immediate
314       "f3 0f 5d c1                                    \n"
315       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
316   );
317   CHECK_TRACE_CONTENTS(
318       "run: minimum of XMM0 and x/m32\n"
319       "run: x/m32 is XMM1\n"
320       "run: XMM0 is now 2\n"
321   );
322 }
323 
324 :(before "End Three-Byte Opcodes Starting With f3 0f")
325 case 0x5d: {  // minimum of x32, x/m32
326   const uint8_t modrm = next();
327   const uint8_t dest = (modrm>>3)&0x7;
328   trace(Callstack_depth+1, "run") << "minimum of " << Xname[dest] << " and x/m32" << end();
329   const float* src = effective_address_float(modrm);
330   Xmm[dest] = min(Xmm[dest], *src);
331   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
332   break;
333 }
334 
335 //:: max
336 
337 :(before "End Initialize Op Names")
338 put_new(Name_f3_0f, "5f", "maximum of two floats (maxss)");
339 
340 :(code)
341 void test_maxss() {
342   Xmm[0] = 3.0;
343   Xmm[1] = 2.0;
344   run(
345       "== code 0x1\n"
346       // op     ModR/M  SIB   displacement  immediate
347       "f3 0f 5f c1                                    \n"
348       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
349   );
350   CHECK_TRACE_CONTENTS(
351       "run: maximum of XMM0 and x/m32\n"
352       "run: x/m32 is XMM1\n"
353       "run: XMM0 is now 3\n"
354   );
355 }
356 
357 :(before "End Three-Byte Opcodes Starting With f3 0f")
358 case 0x5f: {  // maximum of x32, x/m32
359   const uint8_t modrm = next();
360   const uint8_t dest = (modrm>>3)&0x7;
361   trace(Callstack_depth+1, "run") << "maximum of " << Xname[dest] << " and x/m32" << end();
362   const float* src = effective_address_float(modrm);
363   Xmm[dest] = max(Xmm[dest], *src);
364   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
365   break;
366 }
367 
368 //:: reciprocal
369 
370 :(before "End Initialize Op Names")
371 put_new(Name_f3_0f, "53", "reciprocal of float (rcpss)");
372 
373 :(code)
374 void test_rcpss() {
375   Xmm[1] = 2.0;
376   run(
377       "== code 0x1\n"
378       // op     ModR/M  SIB   displacement  immediate
379       "f3 0f 53 c1                                    \n"
380       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
381   );
382   CHECK_TRACE_CONTENTS(
383       "run: reciprocal of x/m32 into XMM0\n"
384       "run: x/m32 is XMM1\n"
385       "run: XMM0 is now 0.5\n"
386   );
387 }
388 
389 :(before "End Three-Byte Opcodes Starting With f3 0f")
390 case 0x53: {  // reciprocal of x/m32 into x32
391   const uint8_t modrm = next();
392   const uint8_t dest = (modrm>>3)&0x7;
393   trace(Callstack_depth+1, "run") << "reciprocal of x/m32 into " << Xname[dest] << end();
394   const float* src = effective_address_float(modrm);
395   Xmm[dest] = 1.0 / *src;
396   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
397   break;
398 }
399 
400 //:: square root
401 
402 :(before "End Initialize Op Names")
403 put_new(Name_f3_0f, "51", "square root of float (sqrtss)");
404 
405 :(code)
406 void test_sqrtss() {
407   Xmm[1] = 2.0;
408   run(
409       "== code 0x1\n"
410       // op     ModR/M  SIB   displacement  immediate
411       "f3 0f 51 c1                                    \n"
412       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
413   );
414   CHECK_TRACE_CONTENTS(
415       "run: square root of x/m32 into XMM0\n"
416       "run: x/m32 is XMM1\n"
417       "run: XMM0 is now 1.41421\n"
418   );
419 }
420 
421 :(before "End Three-Byte Opcodes Starting With f3 0f")
422 case 0x51: {  // square root of x/m32 into x32
423   const uint8_t modrm = next();
424   const uint8_t dest = (modrm>>3)&0x7;
425   trace(Callstack_depth+1, "run") << "square root of x/m32 into " << Xname[dest] << end();
426   const float* src = effective_address_float(modrm);
427   Xmm[dest] = sqrt(*src);
428   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
429   break;
430 }
431 
432 :(before "End Includes")
433 #include <math.h>
434 
435 //:: inverse square root
436 
437 :(before "End Initialize Op Names")
438 put_new(Name_f3_0f, "52", "inverse square root of float (rsqrtss)");
439 
440 :(code)
441 void test_rsqrtss() {
442   Xmm[1] = 0.01;
443   run(
444       "== code 0x1\n"
445       // op     ModR/M  SIB   displacement  immediate
446       "f3 0f 52 c1                                    \n"
447       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
448   );
449   CHECK_TRACE_CONTENTS(
450       "run: inverse square root of x/m32 into XMM0\n"
451       "run: x/m32 is XMM1\n"
452       "run: XMM0 is now 10\n"
453   );
454 }
455 
456 :(before "End Three-Byte Opcodes Starting With f3 0f")
457 case 0x52: {  // inverse square root of x/m32 into x32
458   const uint8_t modrm = next();
459   const uint8_t dest = (modrm>>3)&0x7;
460   trace(Callstack_depth+1, "run") << "inverse square root of x/m32 into " << Xname[dest] << end();
461   const float* src = effective_address_float(modrm);
462   Xmm[dest] = 1.0 / sqrt(*src);
463   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
464   break;
465 }
466 
467 :(code)
468 float* effective_address_float(uint8_t modrm) {
469   const uint8_t mod = (modrm>>6);
470   // ignore middle 3 'reg opcode' bits
471   const uint8_t rm = modrm & 0x7;
472   if (mod == 3) {
473     // mod 3 is just register direct addressing
474     trace(Callstack_depth+1, "run") << "x/m32 is " << Xname[rm] << end();
475     return &Xmm[rm];
476   }
477   uint32_t addr = effective_address_number(modrm);
478   trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_f32(addr) << end();
479   return mem_addr_f32(addr);
480 }
481 
482 //: compare
483 
484 :(before "End Initialize Op Names")
485 put_new(Name_0f, "2f", "compare: set SF if x32 < xm32 (comiss)");
486 
487 :(code)
488 void test_compare_x32_with_mem_at_rm32() {
489   Reg[EAX].i = 0x2000;
490   Xmm[3] = 0.5;
491   run(
492       "== code 0x1\n"
493       // op     ModR/M  SIB   displacement  immediate
494       "  0f 2f  18                                    \n"  // compare XMM3 with *EAX
495       // ModR/M in binary: 00 (indirect mode) 011 (lhs XMM3) 000 (rhs EAX)
496       "== data 0x2000\n"
497       "00 00 00 00\n"  // 0x00000000 = 0.0
498   );
499   CHECK_TRACE_CONTENTS(
500       "run: compare XMM3 with x/m32\n"
501       "run: effective address is 0x00002000 (EAX)\n"
502       "run: SF=0; ZF=0; CF=0; OF=0\n"
503   );
504 }
505 
506 :(before "End Two-Byte Opcodes Starting With 0f")
507 case 0x2f: {  // set SF if x32 < x/m32
508   const uint8_t modrm = next();
509   const uint8_t reg1 = (modrm>>3)&0x7;
510   trace(Callstack_depth+1, "run") << "compare " << Xname[reg1] << " with x/m32" << end();
511   const float* arg2 = effective_address_float(modrm);
512   // Flag settings carefully copied from the Intel manual.
513   // See also https://stackoverflow.com/questions/7057501/x86-assembler-floating-point-compare/7057771#7057771
514   SF = ZF = CF = OF = false;
515   if (Xmm[reg1] == *arg2) ZF = true;
516   if (Xmm[reg1] < *arg2) CF = true;
517   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
518   break;
519 }