19

I'm trying to get useful results using the Reko decompiler on a dusty old MS-DOS binary compiled with Borland C++ that appears to be performing a lot of floating point arithmetic. I'm seeing code sequences like

mov ax,0x4D8C    ; segment selector
mov es,ax
int 0x3C         ; call x87 emulator??
fld dword ptr [<some address>]
sub sp,8
int 0x39         ; call x87 emulator??

...etc. A cursory search engine search strongly hints that the int instructions are invoking an x87 emulation library; when the x87 is present, it lets the coprocessor execute the instruction, but when it isn't, the emulator emulates.

I am well familiar with how to implement FPU operations, with shifts and whatnot. What I'd like to find out more about is the protocol of these invocations to the emulator. I have been unable to locate documentation. Perhaps one of the members of the RE community does?

5
  • I'd imaging using shifts and masks to extract the exponent and mantissa and then use fixed point math to get the result Mar 22, 2016 at 10:39
  • I understand how to perform FP math using integer registers. What I'm wondering about is the binding mechanism used to invoke the FP emulation -- what registers those int instructions are expecting. Mar 22, 2016 at 10:40
  • go to ralf browns home page and download the packages one of the package will contain int.com which you can use to make int calls like int.com 0x3c -ax 0xxx -bx 0xyyyy cs.cmu.edu/~ralf/files.html todays date INT.COM 0x21 -ah 0x2a AX=2A00 BX=0000 CX=0000 DX=0000 SI=0000 DI=0000 BP=FFAC SP=FFA0 CS=0000 DS=0000 ES=0000 SS=0000 CPU Flags: 0n00oditsz0a0p1c INT: 0x21 AX=2A02 BX=0000 CX=07E0 DX=0316 SI=0000 DI=0000 BP=FFAC SP=FFA0 CS=0000 DS=0000 ES=0000 SS=0000 CPU Flags: 0N11odItSz0A0P1C DOSERR: 0057 (87)
    – blabb
    Mar 22, 2016 at 12:56
  • 3
    This may be helpful: delphigroups.info/2/d7/740.html Mar 22, 2016 at 13:50
  • 1
    @JasonGeffner: thanks, that's exactly the link I was looking for. Seems my copy of Ralf's interrupt list was stale. Mar 22, 2016 at 14:25

1 Answer 1

29

Nothing like asking a question on stackexchange, only to be humiliated by finding the answer (or at least part of it). After finding the following source file, it started making sense:

https://github.com/alexhenrie/wine/blob/master/dlls/krnl386.exe16/fpu.c

On old 8086 machines, where there is no trap for invalid instructions, the Elders of the Past came up with an emulation strategy. All x87 instructions are in the D8-DF range (8 possible values) followed by modrm and other goodness. If you prefix the instruction with a FWAIT (opcode 9B), you guarantee that there always be two bytes of code before the modrm byte, looking something like 9B Dx. However, instead of emitting those two bytes, the compiler emits CD xx, where xx ranges 34-3B (8 possible values). As we all know, CD is the encoding of the x86 int instruction.

When the CPU executes the int instruction and arrives at the handler for 34-3B, it vectors off to the interrupt handler. If there isn't an x87 coprocessor available, the handler will emulate the floating point instruction, maintaining the coprocessor state in memory. If however there is an x87 coprocessor present, the handler will peek at the return stack to see where the int instruction is located, and overwrite it with the appropriate 9B Dx byte sequence, corresponding to the CD 3x byte sequence. It then returns control to the patched instruction so that it gets executed. Now that it has been patched, the instruction is an actual FPU instruction, and future executions of the instructions will no longer take the long detour through the emulator.

The documentation for how to deal with interrupt 3E is still not forthcoming. However, for the time being, I have enough information to implement x87 emulation support in the Reko decompiler.

4
  • 13
    Self-answers aren't (shouldn't be) humiliating -- learning is a good thing :P
    – cat
    Mar 22, 2016 at 15:45
  • Cool - I remember seeing the interrupt calls in the binaries back in the time - as a teenager I could not gigure out why the coprocessor opcode did not show up ever.
    – jsbueno
    Mar 23, 2016 at 0:59
  • You know, I actually remembered that. Not the details, just that it used an INT to replace the fpu opcode.
    – JDługosz
    Mar 23, 2016 at 3:36
  • This has now been implemented in Reko, and appears to work beautifully. Mar 23, 2016 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.