View Issue Details

IDProjectCategoryView StatusLast Update
0031918FPCRTLpublic2017-08-03 05:32
ReporterMax NazhalovAssigned ToPierre Muller 
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionreopened 
Platformx86OSOS Version
Product Version3.1.1Product Buildr36312 
Target Version4.0.0Fixed in Version4.0.0 
Summary0031918: [msdos] r36271 prevents usage of INT 10h as BIOS video and screen services call
DescriptionIf the program uses CRT unit, or issues INT 10h directly, it crashes with RTE 207.
See example in steps to reproduce.
Tested for all memory models (note however, compact/large/huge mmodels either produce incorect stack backtrace or hang).

Reverting r36271 seems resolve the problem (at least for tiny mmodel -- other did not tried).
Steps To Reproduceuses crt;
begin
  writeln('screen ',ScreenWidth,'x',ScreenHeight);
end.
TagsNo tags attached.
Fixed in Revision36813
FPCOldBugId
FPCTarget
Attached Files
  • take1.diff (8,736 bytes)
    Index: prt0comn.asm
    ===================================================================
    --- prt0comn.asm	(revision 36787)
    +++ prt0comn.asm	(working copy)
    @@ -73,7 +73,6 @@
             extern __SaveInt75
     
             extern __fpu_status
    -        extern __fpu_control
     
             extern FPC_HANDLE_I8086_ERROR
     
    @@ -283,7 +282,8 @@
     
             global FPC_INT00_HANDLER
     FPC_INT00_HANDLER:
    -        sub sp, 4  ; reserve space on the stack for the retf
    +        pushf
    +        sub sp, 4  ; reserve space on the stack for the iret
     
             push cx
             push ds
    @@ -310,7 +310,7 @@
     %ifndef __FAR_CODE__
             ; check whether we're coming from the same code segment
             mov bp, sp
    -        mov cx, [bp + 3*2 + 6]  ; get caller segment
    +        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
             mov bp, cs
             cmp bp, cx
             jne .call_previous_handler00
    @@ -318,12 +318,12 @@
     
             ; Call Fpc_Handle_I8086_Error, with err=0
             mov bp, sp
    -        mov cx, [bp + 3*2 + 4]  ; get caller offset
    +        mov cx, [bp + 3*2 + 6]  ; get caller offset
     %ifdef __FAR_CODE__
    -        mov dx, [bp + 3*2 + 6]  ; get caller segment
    +        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
     %endif
             pop bp
    -        add sp, 2*2 + 4 + 6
    +        add sp, 2*2 + 6 + 6
             xor ax, ax
             push ax
             mov ax, 0
    @@ -333,6 +333,7 @@
     %endif
             push cx
             cld
    +        sti
     %ifdef __FAR_CODE__
             jmp far FPC_HANDLE_I8086_ERROR
     %else
    @@ -348,11 +349,12 @@
             pop bp
             pop ds
             pop cx
    -        retf  ; jumps to the previous handler with all registers and stack intact
    +        iret  ; jumps to the previous handler with all registers and stack intact
     
             global FPC_INT10_HANDLER
     FPC_INT10_HANDLER:
    -        sub sp, 4  ; reserve space on the stack for the retf
    +        pushf
    +        sub sp, 4  ; reserve space on the stack for the iret
     
             push cx
             push ds
    @@ -368,21 +370,8 @@
     %endif
             mov ds, bp
             ; Check that an unmasked exception is indeed set
    -        ; First load FPU control register to __fpu_control
    -        fnstcw word [__fpu_control]
    -        ; Move control register value to bx register
    -        mov bx,word [__fpu_control]
    -        ; Now load status register to ax
             fnstsw word [__fpu_status]
    -        ; Only the exception part is useful, clear other parts
    -        and bx,3fh
    -        ; at least one same bit must also be set in bx
    -        ; in that case with a bit set in status register
    -        ; which means an exception has been generated by the FPU
    -        ; and the exeception is not masked, as the same
    -        ; bit is set in control register
    -        mov ax,word [__fpu_status]
    -        and ax,bx
    +        test byte [__fpu_status], 80h ; really just this bit is enough, see i8087 datasheet
             je  .call_previous_handler10
     
     %ifdef __NEAR_DATA__
    @@ -396,7 +385,7 @@
     %ifndef __FAR_CODE__
             ; check whether we're coming from the same code segment
             mov bp, sp
    -        mov cx, [bp + 3*2 + 6]  ; get caller segment
    +        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
             mov bp, cs
             cmp bp, cx
             jne .call_previous_handler10
    @@ -404,12 +393,12 @@
     
             ; Call Fpc_Handle_I8086_Error, with err=$10
             mov bp, sp
    -        mov cx, [bp + 3*2 + 4]  ; get caller offset
    +        mov cx, [bp + 3*2 + 6]  ; get caller offset
     %ifdef __FAR_CODE__
    -        mov dx, [bp + 3*2 + 6]  ; get caller segment
    +        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
     %endif
             pop bp
    -        add sp, 2*2 + 4 + 6
    +        add sp, 2*2 + 6 + 6
             xor ax, ax
             push ax
             mov ax, 10h
    @@ -419,6 +408,7 @@
     %endif
             push cx
             cld
    +        sti
     %ifdef __FAR_CODE__
             jmp far FPC_HANDLE_I8086_ERROR
     %else
    @@ -434,11 +424,12 @@
             pop bp
             pop ds
             pop cx
    -        retf  ; jumps to the previous handler with all registers and stack intact
    +        iret  ; jumps to the previous handler with all registers and stack intact
     
             global FPC_INT75_HANDLER
     FPC_INT75_HANDLER:
    -        sub sp, 4  ; reserve space on the stack for the retf
    +        pushf
    +        sub sp, 4  ; reserve space on the stack for the iret
     
             push cx
             push ds
    @@ -465,7 +456,7 @@
     %ifndef __FAR_CODE__
             ; check whether we're coming from the same code segment
             mov bp, sp
    -        mov cx, [bp + 3*2 + 6]  ; get caller segment
    +        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
             mov bp, cs
             cmp bp, cx
             jne .call_previous_handler75
    @@ -473,12 +464,12 @@
     
             ; Call Fpc_Handle_I8086_Error, with err=$75
             mov bp, sp
    -        mov cx, [bp + 3*2 + 4]  ; get caller offset
    +        mov cx, [bp + 3*2 + 6]  ; get caller offset
     %ifdef __FAR_CODE__
    -        mov dx, [bp + 3*2 + 6]  ; get caller segment
    +        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
     %endif
             pop bp
    -        add sp, 2*2 + 4 + 6
    +        add sp, 2*2 + 6 + 6
             xor ax, ax
             push ax
             mov ax, 75h
    @@ -488,6 +479,14 @@
     %endif
             push cx
             cld
    +
    +        ; Reset IRQ/#IGNNE latch; signal EOI; enable interrupts
    +        mov al,20h
    +        out 0F0h,al ; al=any
    +        out 0A0h,al
    +        out 020h,al
    +        sti
    +
     %ifdef __FAR_CODE__
             jmp far FPC_HANDLE_I8086_ERROR
     %else
    @@ -509,9 +508,9 @@
     
             global FPC_INSTALL_INTERRUPT_HANDLERS
     FPC_INSTALL_INTERRUPT_HANDLERS:
    -        push ds
     
     %ifdef __HUGE__
    +        push ds
             mov ax, SYSTEM_DATA
             mov ds, ax
     %endif
    @@ -525,14 +524,16 @@
     
             ; install the new int 00 handler
     %ifndef __TINY__
    +        push ds
             push cs
             pop ds
     %endif
             mov dx, FPC_INT00_HANDLER
    -        pop ds
    -        push ds
             mov ax, 2500h
             int 21h
    +%ifndef __TINY__
    +        pop ds
    +%endif
     
             ; save old int $10 handler
             mov ax, 3510h
    @@ -543,14 +544,16 @@
     
             ; install the new int $10 handler
     %ifndef __TINY__
    +        push ds
             push cs
             pop ds
     %endif
             mov dx, FPC_INT10_HANDLER
             mov ax, 2510h
             int 21h
    +%ifndef __TINY__
             pop ds
    -        push ds
    +%endif
     
             ; save old int $75 handler
             mov ax, 3575h
    @@ -561,15 +564,21 @@
     
             ; install the new int $75 handler
     %ifndef __TINY__
    +        push ds
             push cs
             pop ds
     %endif
             mov dx, FPC_INT75_HANDLER
             mov ax, 2575h
             int 21h
    +%ifndef __TINY__
    +        pop ds
    +%endif
     
     
    +%ifdef __HUGE__
             pop ds
    +%endif
     %ifdef __FAR_CODE__
             retf
     %else
    @@ -579,26 +588,33 @@
     
             global FPC_RESTORE_INTERRUPT_HANDLERS
     FPC_RESTORE_INTERRUPT_HANDLERS:
    -        push ds
     
     %ifdef __HUGE__
    +        push ds
             mov ax, SYSTEM_DATA
             mov ds, ax
     %endif
    -
    +        push ds
             mov ax, 2500h
             lds dx, [__SaveInt00]
             int 21h
    +        pop ds
     
    +        push ds
             mov ax, 2510h
             lds dx, [__SaveInt10]
             int 21h
    +        pop ds
     
    +        push ds
             mov ax, 2575h
             lds dx, [__SaveInt75]
             int 21h
    +        pop ds
     
    +%ifdef __HUGE__
             pop ds
    +%endif
     %ifdef __FAR_CODE__
             retf
     %else
    Index: system.pp
    ===================================================================
    --- system.pp	(revision 36787)
    +++ system.pp	(working copy)
    @@ -84,7 +84,6 @@
       SaveInt10: FarPointer;public name '__SaveInt10';
       SaveInt75: FarPointer;public name '__SaveInt75';
       fpu_status: word;public name '__fpu_status';
    -  fpu_control: word;public name '__fpu_control';
     
     
       AllFilesMask: string [3];
    @@ -182,7 +181,7 @@
           fldcw   localfpucw
           fwait
         end;
    -    if Test8087 < 2 then
    +    if Test8087 < 3 then { i8087/i80287 do not have "native" exception mode (CR0:NE) }
           begin
             restore_old_int10:=true;
           end
    @@ -239,13 +238,11 @@
               end;
             { Restore previous interrupt 06 handler }
             asm
    -          push es
    -          mov bx,word [prevInt06]
    -          mov dx,word [prevInt06+2]
    -          mov es,dx
    +          push ds
               mov ax, $2506
    +          lds dx,[prevInt06]
               int $21
    -          pop es
    +          pop ds
             end;
           end;
           { Special handler of interrupt $10
    @@ -254,13 +251,11 @@
           {$ifndef TEST_FPU_INT10}
           if restore_old_int10 then
             asm
    -          push es
    -          mov bx,word [SaveInt10]
    -          mov dx,word [SaveInt10+2]
    -          mov es,dx
    +          push ds
               mov ax, $2510
    +          lds dx,[SaveInt10]
               int $21
    -          pop es
    +          pop ds
             end;
           {$endif ndef TEST_FPU_INT10}
       end;
    
    take1.diff (8,736 bytes)
  • fpuxtest.pp (3,839 bytes)
    // fpc -Pi8086 -WmTiny fpuxtest.pp
    
    {$mode ObjFPC}
    {$asmmode Intel}
    
    const
      C_FPU_INT02 = $01;
      C_FPU_INT10 = $02;
      C_FPU_INT75 = $04;
    
      CPU_CR0_NE = $0020;
      FPU_SW_ES  = $0080;
    
    function get_MSW(): word; assembler;
    asm
      db $0F, $01, $E0 // smsw ax
    end;
    
    function probe_FPU_exception_route(): byte; assembler;
    // Note: DOSBox currently [0.74] does not emulate FPU exceptions at all
    var
      save_i02, save_i75: farpointer;
      fpu_cw, fpu_cw_ex: word;
      pic_m, pic_s: byte;
    asm
      mov    byte ptr @@triggered,0
      cli
      fnclex
      // save current INT handlers
      mov    ax,3502h
      int    21h
      mov    word ptr save_i02,bx
      mov    word ptr save_i02[2],es
      mov    ax,3510h
      int    21h
      mov    word ptr @@save_i10,bx
      mov    word ptr @@save_i10[2],es
      mov    ax,3575h
      int    21h
      mov    word ptr save_i75,bx
      mov    word ptr save_i75[2],es
      // setup local INT handlers
      push   ds
      push   cs
      pop    ds
      lea    dx,@@handle_i02
      mov    ax,2502h
      int    21h
      lea    dx,@@handle_i10
      mov    ax,2510h
      int    21h
      lea    dx,@@handle_i75
      mov    ax,2575h
      int    21h
      pop    ds
      // save FPU config word
      fstcw  [fpu_cw]
      mov    dx,[fpu_cw]
      and    dl,0C0h // all exceptions unmasked
      mov    [fpu_cw_ex],dx
      // save PIC IRQ masks
      in     al,021h // master
      mov    [pic_m],al
      in     al,0A1h // slave
      mov    [pic_s],al
      // enable FPU IRQ
      mov    al,[pic_m]
      and    al,0FBh // IRQ2 [cascade]
      out    21h,al
      mov    al,[pic_s]
      and    al,0DFh // IRQ13
      out    0A1h,al
      // reinit FPU unmasking all exception conditions
      finit
      fldcw  [fpu_cw_ex]
      // perform invalid FPU op
      fldz
      fdivp  st,st(0) // 0.0/0.0 & pop
      sti // early STI to avoid NTVDM hang on next FWAIT
      fwait // dispatch INT02/INT10 if any
    //sti
      mov    cx,16 // wait for pending INT75 if any
    @@wait_exception:
      test   byte ptr @@triggered,C_FPU_INT75
      jnz    @@exception_fired
      loop   @@wait_exception
    @@exception_fired:
      // clean-up
      fnclex
      finit
      fldcw  [fpu_cw]
      fwait
      mov    al,[pic_m]
      out    021h,al
      mov    al,[pic_s]
      out    0A1h,al
      push   ds
      mov    ax,2502h
      lds    dx,save_i02
      int    21h
      mov    ax,2510h
      lds    dx,@@save_i10
      int    21h
      mov    ax,2575h
      lds    dx,save_i75
      int    21h
      pop    ds
      mov    al,@@triggered
      jmp    @@done
    @@handle_i10: // i80387+ native mode exception
      push   ax
      mov    ah,C_FPU_INT10
      jmp    @@handle_fault
    @@handle_i75: // i80287+ IRQ
      push   ax
      mov    ax,C_FPU_INT75 shl 8
      out    0F0h,al // reset IRQ/#IGNNE latch
      mov    al,20h
      out    0A0h,al
      out    020h,al
      jmp    @@handle_fault
    @@handle_i02: // i8087 trap
      push   ax
      mov    ah,C_FPU_INT02
    @@handle_fault: // common
      pushf
      push   ax
      db     0DFh, 0E0h // fnstsw ax
      test   al,FPU_SW_ES
      pop    ax
      jnz    @@FPU_fault
      // chain to INT10
      test   ah,C_FPU_INT10
      jz     @@ignore
      popf
      pop    ax
      db     0EAh // jmp far X:X
    @@save_i10:
      dd     0
    @@FPU_fault:
      or     byte ptr @@triggered,ah
      fnclex
    @@ignore:
      popf
      pop    ax
      iret
    @@triggered:
      db     0
    @@done:
    end;
    
    var
      x: byte;
    begin
      if Test8086>1 then // i386+
        writeln('CR0::NE = ',get_MSW()and CPU_CR0_NE<>0);
      if Test8087<1 then
        begin
          writeln('No FPU detected.');
          exit;
        end;
      write('Probing FPU exception route..');
      x := probe_FPU_exception_route();
      if x and C_FPU_INT02<>0 then
        write(' INT02');
      if x and C_FPU_INT10<>0 then
        write(' INT10');
      if x and C_FPU_INT75<>0 then
        write(' INT75');
      case x of 
        0: write(' ?none?');
        C_FPU_INT02,
        C_FPU_INT10,
        C_FPU_INT75:;
      else
        write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
      end;
      writeln();
    end.
    
    fpuxtest.pp (3,839 bytes)
  • fpuxtest-v2.pp (8,489 bytes)
    // fpc -Pi8086 -WmTiny fpuxtest.pp
    
    {$mode ObjFPC}
    {$asmmode Intel}
    
    const
      C_FPU_INT02 = $01;
      C_FPU_INT10 = $02;
      C_FPU_INT75 = $04;
    
      CPU_CR0_NE  = $0020;
      FPU_INV_E   = $0001;
      FPU_DEN_E   = $0002;
      FPU_DVZ_E   = $0004;
      FPU_OVF_E   = $0008;
      FPU_UNF_E   = $0010;
      FPU_PRC_E   = $0020;
      FPU_STACK_E = $0040;
      FPU_SW_ES   = $0080;
      FPU_ANY_E   = $00FF;
    
    var
      save_i02, save_i10, save_i75: farpointer;
      fpu_cw, fpu_cw_ex, fpu_sw: word;
      pic_m, pic_s: byte;
    
    function get_MSW(): word; assembler;
    asm
      db $0F, $01, $E0 // smsw ax
    end;
    
    procedure set_MSW(value : word); assembler;
    asm
      mov word ptr value,ax
      db $0F, $01, $F0 // lmsw ax
    end;
    
    function get_CR0 : dword; assembler;
    asm
      db $0f, $20, $c0 // mov eax,cr0
      db $66
      mov dx,ax // mov edx,eax
      mov cl,8
      db $66
      shr dx,cl
      db $66
      shr dx,cl // shr edx,16
    end;
    
    procedure set_CR0(value : dword); assembler;
    asm
      db $66
      mov word ptr value, ax //mov dword ptr value,eax
      db $0f, $22, $C0 // mov cr0, eax
    end;
    
    
    function probe_FPU_exception_route(): byte; assembler;
    // Note: DOSBox currently [0.74] does not emulate FPU exceptions at all
    asm
      mov    byte ptr @@triggered,0
      mov    ax,ds
      mov    word ptr @@saved_ds,ax
      cli
      fnclex
      // save current INT handlers
      mov    ax,3502h
      int    21h
      mov    word ptr save_i02,bx
      mov    word ptr save_i02[2],es
      mov    ax,3510h
      int    21h
      mov    word ptr save_i10,bx
      mov    word ptr save_i10[2],es
      mov    word ptr @@save_i10,bx
      mov    word ptr @@save_i10[2],es
      mov    ax,3575h
      int    21h
      mov    word ptr save_i75,bx
      mov    word ptr save_i75[2],es
      // setup local INT handlers
      push   ds
      push   cs
      pop    ds
      lea    dx,@@handle_i02
      mov    ax,2502h
      int    21h
      lea    dx,@@handle_i10
      mov    ax,2510h
      int    21h
      lea    dx,@@handle_i75
      mov    ax,2575h
      int    21h
      pop    ds
      // save FPU config word
      fstcw  [fpu_cw]
      mov    dx,[fpu_cw]
      and    dl,0C0h // all exceptions unmasked
      mov    [fpu_cw_ex],dx
      // save PIC IRQ masks
      in     al,021h // master
      mov    [pic_m],al
      in     al,0A1h // slave
      mov    [pic_s],al
      // enable FPU IRQ
      mov    al,[pic_m]
      and    al,0FBh // IRQ2 [cascade]
      out    21h,al
      mov    al,[pic_s]
      and    al,0DFh // IRQ13
      out    0A1h,al
      // reinit FPU unmasking all exception conditions
      finit
      fldcw  [fpu_cw_ex]
      push   sp
      pop    ax
      mov    word ptr @@saved_sp,ax
      // perform invalid FPU op
      fldz
      fdivp  st,st(0) // 0.0/0.0 & pop
      sti // early STI to avoid NTVDM hang on next FWAIT
      fwait // dispatch INT02/INT10 if any
    //sti
      mov    cx,16 // wait for pending INT75 if any
    @@wait_exception:
      test   byte ptr @@triggered,C_FPU_INT75
      jnz    @@exception_fired1
      loop   @@wait_exception
    @@exception_fired1:
      push   ax
    @@exception_fired:
      pop    ax
      // Reload DS
      mov    bx,word ptr @@saved_ds
      mov    ds,bx
      // Reload SP
      mov    bx,word ptr @@saved_sp
      mov    sp,bx
      push   ax
      // clean-up
      fnclex
      finit
      fldcw  [fpu_cw]
      fwait
      mov    al,[pic_m]
      out    021h,al
      mov    al,[pic_s]
      out    0A1h,al
      mov    ax, word ptr @@loc_fpu_sw
      mov    [fpu_sw],ax
      push   ds
      mov    ax,2502h
      lds    dx,save_i02
      int    21h
      mov    ax,2510h
      lds    dx,@@save_i10
      int    21h
      mov    ax,2575h
      lds    dx,save_i75
      int    21h
      pop    ds
      pop    ax
      mov    al,@@triggered
      jmp    @@done
    
    @@handle_i10: // i80387+ native mode exception
      push   ax
      mov    ah,C_FPU_INT10
      jmp    @@handle_fault
    @@handle_i75: // i80287+ IRQ
      push   ax
      mov    ax,C_FPU_INT75 shl 8
      out    0F0h,al // reset IRQ/#IGNNE latch
      mov    al,20h
      out    0A0h,al
      out    020h,al
      jmp    @@handle_fault
    @@handle_i02: // i8087 trap
      push   ax
      mov    ah,C_FPU_INT02
    @@handle_fault: // common
      pushf
      push   ax
      db     0DFh, 0E0h // fnstsw ax
      mov    word ptr @@loc_fpu_sw,ax
      and    ax,FPU_ANY_E
      pop    ax
      jnz    @@FPU_fault1
      // chain to INT10
      test   ah,C_FPU_INT10
      jz     @@ignore
      popf
      pop    ax
      db     0EAh // jmp far X:X
    @@save_i10:
    dd     0
    @@FPU_fault1:
      popf
    @@FPU_fault:
      or     byte ptr @@triggered,ah
      fnclex
      jmp    @@exception_fired
    @@ignore:
      popf
      pop    ax
      iret
    @@triggered:
      db     0
    @@saved_ds:
      dw     0
    @@saved_sp:
      dw     0
    @@loc_fpu_sw:
      dw     0
    @@done:
    end;
    
    var
      x: byte;
    begin
      if Test8086>1 then // i386+
        begin
          writeln('MSW CR0::NE = ',(get_MSW()and CPU_CR0_NE)<>0);
          set_MSW(get_MSW() or CPU_CR0_NE);
          if (get_MSW() and CPU_CR0_NE)=0 then
            writeln('Impossible to set MSW CR0::NE bit')
          else
            writeln('Successfully set MSW CR0::NE bit');
        end;
      if Test8087<1 then
        begin
          writeln('No FPU detected.');
          exit;
        end;
      if Test8087>1 then // i386+
        begin
          try
            writeln('direct CR0::NE = ',(get_CR0()and CPU_CR0_NE)<>0);
            set_CR0(get_CR0() or CPU_CR0_NE);
            if (get_CR0() and CPU_CR0_NE)=0 then
              writeln('Impossible to set CRO::NE bit')
            else
              writeln('Successfully set CR0::NE bit');
          except
            writeln('mov cr0,ax instruction not supported');
          end;
        end;
      write('Probing FPU exception route..');
      x := probe_FPU_exception_route();
      writeln('x=',x,' $',hexstr(x,2));
      if x and C_FPU_INT02<>0 then
        write(' INT02');
      if x and C_FPU_INT10<>0 then
        write(' INT10');
      if x and C_FPU_INT75<>0 then
        write(' INT75');
      case x of
        0: write(' ?none?');
        C_FPU_INT02,
        C_FPU_INT10,
        C_FPU_INT75:;
      else
        write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
      end;
      writeln;
      writeln('FPU Status word=',hexstr(fpu_sw,8));
      if (fpu_sw and FPU_ANY_E) = 0 then
        writeln('No status exception bit set');
    
      if (fpu_sw and FPU_SW_ES) = 0 then
        writeln('Status summary exception bit not set');
      if (fpu_sw and FPU_INV_E)<>0 then
        writeln('Invalid Bit Exception set');
      if (fpu_sw and FPU_DEN_E)<>0 then
        writeln('Denormal Bit Exception set');
      if (fpu_sw and FPU_DVZ_E)<>0 then
        writeln('Division by zero Bit Exception set');
      if (fpu_sw and FPU_OVF_E)<>0 then
        writeln('Overflow Bit Exception set');
      if (fpu_sw and FPU_UNF_E)<>0 then
        writeln('Underflow Bit Exception set');
      if (fpu_sw and FPU_PRC_E)<>0 then
        writeln('Precision Bit Exception set');
      if (fpu_sw and FPU_STACK_E)<>0 then
        writeln('FPU stack Bit Exception set');
    
      if Test8087>1 then // i386+
        begin
          try
            writeln('direct CR0::NE = ',(get_CR0() and CPU_CR0_NE)<>0);
            set_CR0(get_CR0() and (not CPU_CR0_NE));
            if (get_CR0() and CPU_CR0_NE)<>0 then
              writeln('Impossible to reset CRO::NE bit')
            else
              writeln('Successfully reset CR0::NE bit');
          except
            writeln('mov cr0,ax instruction not supported');
          end;
          write('Probing FPU exception route..');
          x := probe_FPU_exception_route();
          writeln('x=',x,' $',hexstr(x,2));
          if x and C_FPU_INT02<>0 then
            write(' INT02');
          if x and C_FPU_INT10<>0 then
            write(' INT10');
          if x and C_FPU_INT75<>0 then
            write(' INT75');
          case x of
            0: write(' ?none?');
            C_FPU_INT02,
            C_FPU_INT10,
            C_FPU_INT75:;
          else
            write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
          end;
          writeln;
          writeln('FPU Status word=',hexstr(fpu_sw,8));
          if (fpu_sw and FPU_ANY_E) = 0 then
            writeln('No status exception bit set');
    
          if (fpu_sw and FPU_SW_ES) = 0 then
            writeln('Status summary exception bit not set');
          if (fpu_sw and FPU_INV_E)<>0 then
            writeln('Invalid Bit Exception set');
          if (fpu_sw and FPU_DEN_E)<>0 then
            writeln('Denormal Bit Exception set');
          if (fpu_sw and FPU_DVZ_E)<>0 then
            writeln('Division by zero Bit Exception set');
          if (fpu_sw and FPU_OVF_E)<>0 then
            writeln('Overflow Bit Exception set');
          if (fpu_sw and FPU_UNF_E)<>0 then
            writeln('Underflow Bit Exception set');
          if (fpu_sw and FPU_PRC_E)<>0 then
            writeln('Precision Bit Exception set');
          if (fpu_sw and FPU_STACK_E)<>0 then
            writeln('FPU stack Bit Exception set');
        end;
    end.
    
    fpuxtest-v2.pp (8,489 bytes)

Activities

Pierre Muller

2017-06-12 09:36

developer   ~0101045

I wrote a change to msdos system,
that will remove the special coprocessor handler of
trap $10, if the tne NE bit ($20) can be reset on CR0 special register,
or if Test8086 returns less than 2.

  The problem here is that it is not easy:
1) to know which real hardware still generate an interrupt x10
(instead of redirecting to interrupt $75).

2) To correctly handle the systems that do not perform this translation.

  Anyhow, the current code was indeed faulty becuase it did not
call previous int $10 handler when no coprocessor error is found.

Pierre

Max Nazhalov

2017-06-12 13:22

reporter   ~0101060

I'm slightly confused..

      if restore_old_int10 then
        asm
          push es
          mov bx,word [SaveInt10]
          mov dx,word [SaveInt10+2]
          mov es,dx
          mov ax, $2506 // didn't You mean $2510 here?
          int $21
          pop es
        end;

Pierre Muller

2017-06-13 22:40

developer   ~0101104

Yes sorry,
I did forget to change the interrupt number...

I will check the correct fix in after testing it...

Pierre

Pierre Muller

2017-06-15 22:32

developer   ~0101169

Should be fixed in revision 36497.

Please check and close if fix works,

Pierre

Max Nazhalov

2017-06-17 09:00

reporter   ~0101200

Last edited: 2017-06-17 17:39

View 3 revisions

New implementation won't work under NTVDM.
E.g., in my tests (Win7) CR0 looks like $80010031, and can not be changed via MOV CR0,EAX, clearing CR0.NE silently ignored, so the the INT 10h handler retains.

Tested Compact mmodel -- the same RTE 207.

UPD: tested Large and Huge mmodels -- they just hang.
Deep tracing reveals they recursing into INT 10h trying to print RTE 207 on the screen over and over again. Simply redirecting stderr (2>file) shows what they finally want to say. Dunno why compact model ever prints something meaningful.
Testing tiny/small/medium models cannot be done at this point -- RTL failed to build for some other unrelated reason (parser.inc(394,1) Error: Local variables size exceeds supported limit).

Max Nazhalov

2017-07-26 15:56

reporter  

take1.diff (8,736 bytes)
Index: prt0comn.asm
===================================================================
--- prt0comn.asm	(revision 36787)
+++ prt0comn.asm	(working copy)
@@ -73,7 +73,6 @@
         extern __SaveInt75
 
         extern __fpu_status
-        extern __fpu_control
 
         extern FPC_HANDLE_I8086_ERROR
 
@@ -283,7 +282,8 @@
 
         global FPC_INT00_HANDLER
 FPC_INT00_HANDLER:
-        sub sp, 4  ; reserve space on the stack for the retf
+        pushf
+        sub sp, 4  ; reserve space on the stack for the iret
 
         push cx
         push ds
@@ -310,7 +310,7 @@
 %ifndef __FAR_CODE__
         ; check whether we're coming from the same code segment
         mov bp, sp
-        mov cx, [bp + 3*2 + 6]  ; get caller segment
+        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
         mov bp, cs
         cmp bp, cx
         jne .call_previous_handler00
@@ -318,12 +318,12 @@
 
         ; Call Fpc_Handle_I8086_Error, with err=0
         mov bp, sp
-        mov cx, [bp + 3*2 + 4]  ; get caller offset
+        mov cx, [bp + 3*2 + 6]  ; get caller offset
 %ifdef __FAR_CODE__
-        mov dx, [bp + 3*2 + 6]  ; get caller segment
+        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
 %endif
         pop bp
-        add sp, 2*2 + 4 + 6
+        add sp, 2*2 + 6 + 6
         xor ax, ax
         push ax
         mov ax, 0
@@ -333,6 +333,7 @@
 %endif
         push cx
         cld
+        sti
 %ifdef __FAR_CODE__
         jmp far FPC_HANDLE_I8086_ERROR
 %else
@@ -348,11 +349,12 @@
         pop bp
         pop ds
         pop cx
-        retf  ; jumps to the previous handler with all registers and stack intact
+        iret  ; jumps to the previous handler with all registers and stack intact
 
         global FPC_INT10_HANDLER
 FPC_INT10_HANDLER:
-        sub sp, 4  ; reserve space on the stack for the retf
+        pushf
+        sub sp, 4  ; reserve space on the stack for the iret
 
         push cx
         push ds
@@ -368,21 +370,8 @@
 %endif
         mov ds, bp
         ; Check that an unmasked exception is indeed set
-        ; First load FPU control register to __fpu_control
-        fnstcw word [__fpu_control]
-        ; Move control register value to bx register
-        mov bx,word [__fpu_control]
-        ; Now load status register to ax
         fnstsw word [__fpu_status]
-        ; Only the exception part is useful, clear other parts
-        and bx,3fh
-        ; at least one same bit must also be set in bx
-        ; in that case with a bit set in status register
-        ; which means an exception has been generated by the FPU
-        ; and the exeception is not masked, as the same
-        ; bit is set in control register
-        mov ax,word [__fpu_status]
-        and ax,bx
+        test byte [__fpu_status], 80h ; really just this bit is enough, see i8087 datasheet
         je  .call_previous_handler10
 
 %ifdef __NEAR_DATA__
@@ -396,7 +385,7 @@
 %ifndef __FAR_CODE__
         ; check whether we're coming from the same code segment
         mov bp, sp
-        mov cx, [bp + 3*2 + 6]  ; get caller segment
+        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
         mov bp, cs
         cmp bp, cx
         jne .call_previous_handler10
@@ -404,12 +393,12 @@
 
         ; Call Fpc_Handle_I8086_Error, with err=$10
         mov bp, sp
-        mov cx, [bp + 3*2 + 4]  ; get caller offset
+        mov cx, [bp + 3*2 + 6]  ; get caller offset
 %ifdef __FAR_CODE__
-        mov dx, [bp + 3*2 + 6]  ; get caller segment
+        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
 %endif
         pop bp
-        add sp, 2*2 + 4 + 6
+        add sp, 2*2 + 6 + 6
         xor ax, ax
         push ax
         mov ax, 10h
@@ -419,6 +408,7 @@
 %endif
         push cx
         cld
+        sti
 %ifdef __FAR_CODE__
         jmp far FPC_HANDLE_I8086_ERROR
 %else
@@ -434,11 +424,12 @@
         pop bp
         pop ds
         pop cx
-        retf  ; jumps to the previous handler with all registers and stack intact
+        iret  ; jumps to the previous handler with all registers and stack intact
 
         global FPC_INT75_HANDLER
 FPC_INT75_HANDLER:
-        sub sp, 4  ; reserve space on the stack for the retf
+        pushf
+        sub sp, 4  ; reserve space on the stack for the iret
 
         push cx
         push ds
@@ -465,7 +456,7 @@
 %ifndef __FAR_CODE__
         ; check whether we're coming from the same code segment
         mov bp, sp
-        mov cx, [bp + 3*2 + 6]  ; get caller segment
+        mov cx, [bp + 3*2 + 6 + 2]  ; get caller segment
         mov bp, cs
         cmp bp, cx
         jne .call_previous_handler75
@@ -473,12 +464,12 @@
 
         ; Call Fpc_Handle_I8086_Error, with err=$75
         mov bp, sp
-        mov cx, [bp + 3*2 + 4]  ; get caller offset
+        mov cx, [bp + 3*2 + 6]  ; get caller offset
 %ifdef __FAR_CODE__
-        mov dx, [bp + 3*2 + 6]  ; get caller segment
+        mov dx, [bp + 3*2 + 6 + 2]  ; get caller segment
 %endif
         pop bp
-        add sp, 2*2 + 4 + 6
+        add sp, 2*2 + 6 + 6
         xor ax, ax
         push ax
         mov ax, 75h
@@ -488,6 +479,14 @@
 %endif
         push cx
         cld
+
+        ; Reset IRQ/#IGNNE latch; signal EOI; enable interrupts
+        mov al,20h
+        out 0F0h,al ; al=any
+        out 0A0h,al
+        out 020h,al
+        sti
+
 %ifdef __FAR_CODE__
         jmp far FPC_HANDLE_I8086_ERROR
 %else
@@ -509,9 +508,9 @@
 
         global FPC_INSTALL_INTERRUPT_HANDLERS
 FPC_INSTALL_INTERRUPT_HANDLERS:
-        push ds
 
 %ifdef __HUGE__
+        push ds
         mov ax, SYSTEM_DATA
         mov ds, ax
 %endif
@@ -525,14 +524,16 @@
 
         ; install the new int 00 handler
 %ifndef __TINY__
+        push ds
         push cs
         pop ds
 %endif
         mov dx, FPC_INT00_HANDLER
-        pop ds
-        push ds
         mov ax, 2500h
         int 21h
+%ifndef __TINY__
+        pop ds
+%endif
 
         ; save old int $10 handler
         mov ax, 3510h
@@ -543,14 +544,16 @@
 
         ; install the new int $10 handler
 %ifndef __TINY__
+        push ds
         push cs
         pop ds
 %endif
         mov dx, FPC_INT10_HANDLER
         mov ax, 2510h
         int 21h
+%ifndef __TINY__
         pop ds
-        push ds
+%endif
 
         ; save old int $75 handler
         mov ax, 3575h
@@ -561,15 +564,21 @@
 
         ; install the new int $75 handler
 %ifndef __TINY__
+        push ds
         push cs
         pop ds
 %endif
         mov dx, FPC_INT75_HANDLER
         mov ax, 2575h
         int 21h
+%ifndef __TINY__
+        pop ds
+%endif
 
 
+%ifdef __HUGE__
         pop ds
+%endif
 %ifdef __FAR_CODE__
         retf
 %else
@@ -579,26 +588,33 @@
 
         global FPC_RESTORE_INTERRUPT_HANDLERS
 FPC_RESTORE_INTERRUPT_HANDLERS:
-        push ds
 
 %ifdef __HUGE__
+        push ds
         mov ax, SYSTEM_DATA
         mov ds, ax
 %endif
-
+        push ds
         mov ax, 2500h
         lds dx, [__SaveInt00]
         int 21h
+        pop ds
 
+        push ds
         mov ax, 2510h
         lds dx, [__SaveInt10]
         int 21h
+        pop ds
 
+        push ds
         mov ax, 2575h
         lds dx, [__SaveInt75]
         int 21h
+        pop ds
 
+%ifdef __HUGE__
         pop ds
+%endif
 %ifdef __FAR_CODE__
         retf
 %else
Index: system.pp
===================================================================
--- system.pp	(revision 36787)
+++ system.pp	(working copy)
@@ -84,7 +84,6 @@
   SaveInt10: FarPointer;public name '__SaveInt10';
   SaveInt75: FarPointer;public name '__SaveInt75';
   fpu_status: word;public name '__fpu_status';
-  fpu_control: word;public name '__fpu_control';
 
 
   AllFilesMask: string [3];
@@ -182,7 +181,7 @@
       fldcw   localfpucw
       fwait
     end;
-    if Test8087 < 2 then
+    if Test8087 < 3 then { i8087/i80287 do not have "native" exception mode (CR0:NE) }
       begin
         restore_old_int10:=true;
       end
@@ -239,13 +238,11 @@
           end;
         { Restore previous interrupt 06 handler }
         asm
-          push es
-          mov bx,word [prevInt06]
-          mov dx,word [prevInt06+2]
-          mov es,dx
+          push ds
           mov ax, $2506
+          lds dx,[prevInt06]
           int $21
-          pop es
+          pop ds
         end;
       end;
       { Special handler of interrupt $10
@@ -254,13 +251,11 @@
       {$ifndef TEST_FPU_INT10}
       if restore_old_int10 then
         asm
-          push es
-          mov bx,word [SaveInt10]
-          mov dx,word [SaveInt10+2]
-          mov es,dx
+          push ds
           mov ax, $2510
+          lds dx,[SaveInt10]
           int $21
-          pop es
+          pop ds
         end;
       {$endif ndef TEST_FPU_INT10}
   end;
take1.diff (8,736 bytes)

Max Nazhalov

2017-07-26 15:57

reporter   ~0101922

Attached patch corrects obvious implementation bugs.
Briefly tested tiny and huge memory models (virtualbox/ntvdm/bochs). More testing TBD.

Some more work to do to detach I10 handler under ntvdm (though seems it no longer interferes).

Pierre Muller

2017-07-26 16:42

developer   ~0101924

Hi Max,

  thank you very much for your patch.

  It seems to be OK, I applied it to fpcWin7-64 machine.
If the testsuite results tomorrow morning show
no problem, I will commit them tomorrow.


Pierre Muller

Max Nazhalov

2017-07-26 16:46

reporter  

fpuxtest.pp (3,839 bytes)
// fpc -Pi8086 -WmTiny fpuxtest.pp

{$mode ObjFPC}
{$asmmode Intel}

const
  C_FPU_INT02 = $01;
  C_FPU_INT10 = $02;
  C_FPU_INT75 = $04;

  CPU_CR0_NE = $0020;
  FPU_SW_ES  = $0080;

function get_MSW(): word; assembler;
asm
  db $0F, $01, $E0 // smsw ax
end;

function probe_FPU_exception_route(): byte; assembler;
// Note: DOSBox currently [0.74] does not emulate FPU exceptions at all
var
  save_i02, save_i75: farpointer;
  fpu_cw, fpu_cw_ex: word;
  pic_m, pic_s: byte;
asm
  mov    byte ptr @@triggered,0
  cli
  fnclex
  // save current INT handlers
  mov    ax,3502h
  int    21h
  mov    word ptr save_i02,bx
  mov    word ptr save_i02[2],es
  mov    ax,3510h
  int    21h
  mov    word ptr @@save_i10,bx
  mov    word ptr @@save_i10[2],es
  mov    ax,3575h
  int    21h
  mov    word ptr save_i75,bx
  mov    word ptr save_i75[2],es
  // setup local INT handlers
  push   ds
  push   cs
  pop    ds
  lea    dx,@@handle_i02
  mov    ax,2502h
  int    21h
  lea    dx,@@handle_i10
  mov    ax,2510h
  int    21h
  lea    dx,@@handle_i75
  mov    ax,2575h
  int    21h
  pop    ds
  // save FPU config word
  fstcw  [fpu_cw]
  mov    dx,[fpu_cw]
  and    dl,0C0h // all exceptions unmasked
  mov    [fpu_cw_ex],dx
  // save PIC IRQ masks
  in     al,021h // master
  mov    [pic_m],al
  in     al,0A1h // slave
  mov    [pic_s],al
  // enable FPU IRQ
  mov    al,[pic_m]
  and    al,0FBh // IRQ2 [cascade]
  out    21h,al
  mov    al,[pic_s]
  and    al,0DFh // IRQ13
  out    0A1h,al
  // reinit FPU unmasking all exception conditions
  finit
  fldcw  [fpu_cw_ex]
  // perform invalid FPU op
  fldz
  fdivp  st,st(0) // 0.0/0.0 & pop
  sti // early STI to avoid NTVDM hang on next FWAIT
  fwait // dispatch INT02/INT10 if any
//sti
  mov    cx,16 // wait for pending INT75 if any
@@wait_exception:
  test   byte ptr @@triggered,C_FPU_INT75
  jnz    @@exception_fired
  loop   @@wait_exception
@@exception_fired:
  // clean-up
  fnclex
  finit
  fldcw  [fpu_cw]
  fwait
  mov    al,[pic_m]
  out    021h,al
  mov    al,[pic_s]
  out    0A1h,al
  push   ds
  mov    ax,2502h
  lds    dx,save_i02
  int    21h
  mov    ax,2510h
  lds    dx,@@save_i10
  int    21h
  mov    ax,2575h
  lds    dx,save_i75
  int    21h
  pop    ds
  mov    al,@@triggered
  jmp    @@done
@@handle_i10: // i80387+ native mode exception
  push   ax
  mov    ah,C_FPU_INT10
  jmp    @@handle_fault
@@handle_i75: // i80287+ IRQ
  push   ax
  mov    ax,C_FPU_INT75 shl 8
  out    0F0h,al // reset IRQ/#IGNNE latch
  mov    al,20h
  out    0A0h,al
  out    020h,al
  jmp    @@handle_fault
@@handle_i02: // i8087 trap
  push   ax
  mov    ah,C_FPU_INT02
@@handle_fault: // common
  pushf
  push   ax
  db     0DFh, 0E0h // fnstsw ax
  test   al,FPU_SW_ES
  pop    ax
  jnz    @@FPU_fault
  // chain to INT10
  test   ah,C_FPU_INT10
  jz     @@ignore
  popf
  pop    ax
  db     0EAh // jmp far X:X
@@save_i10:
  dd     0
@@FPU_fault:
  or     byte ptr @@triggered,ah
  fnclex
@@ignore:
  popf
  pop    ax
  iret
@@triggered:
  db     0
@@done:
end;

var
  x: byte;
begin
  if Test8086>1 then // i386+
    writeln('CR0::NE = ',get_MSW()and CPU_CR0_NE<>0);
  if Test8087<1 then
    begin
      writeln('No FPU detected.');
      exit;
    end;
  write('Probing FPU exception route..');
  x := probe_FPU_exception_route();
  if x and C_FPU_INT02<>0 then
    write(' INT02');
  if x and C_FPU_INT10<>0 then
    write(' INT10');
  if x and C_FPU_INT75<>0 then
    write(' INT75');
  case x of 
    0: write(' ?none?');
    C_FPU_INT02,
    C_FPU_INT10,
    C_FPU_INT75:;
  else
    write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
  end;
  writeln();
end.
fpuxtest.pp (3,839 bytes)

Max Nazhalov

2017-07-26 16:46

reporter   ~0101925

About ntvdm and other weirds: how about approach implemented in fpuxtest.pp?

Pierre Muller

2017-07-29 13:54

developer   ~0101982

Hi all,


I have committed the patch submitted by Max Nazhalov
in rev 36813.

This change fixes tbs/tb0107.pp and tbs/tb0301.pp

See:
https://www.freepascal.org/testsuite/cgi-bin/new-testsuite2.cgi?run1id=335507&run2id=&previousrunid=335206&noskipped=1&failedonly=1&action=Compare_to_previous

Thanks for the fix, Max!
Note that your fpuxtest would still cycle indefinittively with
stock msdos.exe emulator as the exception summary bit
of fpu status register is not set there!


Pierre Muller

Max Nazhalov

2017-07-29 14:42

reporter   ~0101983

Last edited: 2017-07-29 15:12

View 4 revisions

Thanks!

Unfortunately I do not know what is "stock msdos.exe emulator", may be it is also not complete. Just as the DOSBox does not generate FPU excrption at all, so fpuxtest reports "none".
I've tested with BOCHS (it's magick break and instruction trace logging are great), DosBox, VirtualBox, ntvdm, and several modern SBCs.
All (except ntvdm and dosbox) running MSDOS 7.10 (win98 backend) with and without its native EMM386.
All of them (except dosbox) return correct result, even after manually flipping CR0:NE (except ntvdm, where it is not possible by design -- still INT75h, and EMM386, which just halts on exception 10h).
And yes, indeed, i80286 has no CR0:NE (and even CR0 itself, just an MSW), it should be hardwired to generate INT10h via ERROR pin, but I doubt it ever been released in real AT machines -- at least BP7 never tries to intercept INT10h in realmode, so probably real systems just reroute error signal via PIC as it was before.
And note -- BP7 DOES intercept NMI (INT02h) awaiting FPU exception from i8087.
Everything is very confusing here.

Anyway, feel free to return the check of FPU:CW exception mask against FPU:SW pending mask, it does not hurt, and was not principal fix.

Can be closed for now.

Pierre Muller

2017-08-02 09:16

developer   ~0102029

  I tried to modify your test
to get it to work with msdos emulator.

  I will attach my new source version.

  Could you please test it on the different settings
you listed?

  Thanks in advance,

Pierre Muller

Pierre Muller

2017-08-02 09:18

developer  

fpuxtest-v2.pp (8,489 bytes)
// fpc -Pi8086 -WmTiny fpuxtest.pp

{$mode ObjFPC}
{$asmmode Intel}

const
  C_FPU_INT02 = $01;
  C_FPU_INT10 = $02;
  C_FPU_INT75 = $04;

  CPU_CR0_NE  = $0020;
  FPU_INV_E   = $0001;
  FPU_DEN_E   = $0002;
  FPU_DVZ_E   = $0004;
  FPU_OVF_E   = $0008;
  FPU_UNF_E   = $0010;
  FPU_PRC_E   = $0020;
  FPU_STACK_E = $0040;
  FPU_SW_ES   = $0080;
  FPU_ANY_E   = $00FF;

var
  save_i02, save_i10, save_i75: farpointer;
  fpu_cw, fpu_cw_ex, fpu_sw: word;
  pic_m, pic_s: byte;

function get_MSW(): word; assembler;
asm
  db $0F, $01, $E0 // smsw ax
end;

procedure set_MSW(value : word); assembler;
asm
  mov word ptr value,ax
  db $0F, $01, $F0 // lmsw ax
end;

function get_CR0 : dword; assembler;
asm
  db $0f, $20, $c0 // mov eax,cr0
  db $66
  mov dx,ax // mov edx,eax
  mov cl,8
  db $66
  shr dx,cl
  db $66
  shr dx,cl // shr edx,16
end;

procedure set_CR0(value : dword); assembler;
asm
  db $66
  mov word ptr value, ax //mov dword ptr value,eax
  db $0f, $22, $C0 // mov cr0, eax
end;


function probe_FPU_exception_route(): byte; assembler;
// Note: DOSBox currently [0.74] does not emulate FPU exceptions at all
asm
  mov    byte ptr @@triggered,0
  mov    ax,ds
  mov    word ptr @@saved_ds,ax
  cli
  fnclex
  // save current INT handlers
  mov    ax,3502h
  int    21h
  mov    word ptr save_i02,bx
  mov    word ptr save_i02[2],es
  mov    ax,3510h
  int    21h
  mov    word ptr save_i10,bx
  mov    word ptr save_i10[2],es
  mov    word ptr @@save_i10,bx
  mov    word ptr @@save_i10[2],es
  mov    ax,3575h
  int    21h
  mov    word ptr save_i75,bx
  mov    word ptr save_i75[2],es
  // setup local INT handlers
  push   ds
  push   cs
  pop    ds
  lea    dx,@@handle_i02
  mov    ax,2502h
  int    21h
  lea    dx,@@handle_i10
  mov    ax,2510h
  int    21h
  lea    dx,@@handle_i75
  mov    ax,2575h
  int    21h
  pop    ds
  // save FPU config word
  fstcw  [fpu_cw]
  mov    dx,[fpu_cw]
  and    dl,0C0h // all exceptions unmasked
  mov    [fpu_cw_ex],dx
  // save PIC IRQ masks
  in     al,021h // master
  mov    [pic_m],al
  in     al,0A1h // slave
  mov    [pic_s],al
  // enable FPU IRQ
  mov    al,[pic_m]
  and    al,0FBh // IRQ2 [cascade]
  out    21h,al
  mov    al,[pic_s]
  and    al,0DFh // IRQ13
  out    0A1h,al
  // reinit FPU unmasking all exception conditions
  finit
  fldcw  [fpu_cw_ex]
  push   sp
  pop    ax
  mov    word ptr @@saved_sp,ax
  // perform invalid FPU op
  fldz
  fdivp  st,st(0) // 0.0/0.0 & pop
  sti // early STI to avoid NTVDM hang on next FWAIT
  fwait // dispatch INT02/INT10 if any
//sti
  mov    cx,16 // wait for pending INT75 if any
@@wait_exception:
  test   byte ptr @@triggered,C_FPU_INT75
  jnz    @@exception_fired1
  loop   @@wait_exception
@@exception_fired1:
  push   ax
@@exception_fired:
  pop    ax
  // Reload DS
  mov    bx,word ptr @@saved_ds
  mov    ds,bx
  // Reload SP
  mov    bx,word ptr @@saved_sp
  mov    sp,bx
  push   ax
  // clean-up
  fnclex
  finit
  fldcw  [fpu_cw]
  fwait
  mov    al,[pic_m]
  out    021h,al
  mov    al,[pic_s]
  out    0A1h,al
  mov    ax, word ptr @@loc_fpu_sw
  mov    [fpu_sw],ax
  push   ds
  mov    ax,2502h
  lds    dx,save_i02
  int    21h
  mov    ax,2510h
  lds    dx,@@save_i10
  int    21h
  mov    ax,2575h
  lds    dx,save_i75
  int    21h
  pop    ds
  pop    ax
  mov    al,@@triggered
  jmp    @@done

@@handle_i10: // i80387+ native mode exception
  push   ax
  mov    ah,C_FPU_INT10
  jmp    @@handle_fault
@@handle_i75: // i80287+ IRQ
  push   ax
  mov    ax,C_FPU_INT75 shl 8
  out    0F0h,al // reset IRQ/#IGNNE latch
  mov    al,20h
  out    0A0h,al
  out    020h,al
  jmp    @@handle_fault
@@handle_i02: // i8087 trap
  push   ax
  mov    ah,C_FPU_INT02
@@handle_fault: // common
  pushf
  push   ax
  db     0DFh, 0E0h // fnstsw ax
  mov    word ptr @@loc_fpu_sw,ax
  and    ax,FPU_ANY_E
  pop    ax
  jnz    @@FPU_fault1
  // chain to INT10
  test   ah,C_FPU_INT10
  jz     @@ignore
  popf
  pop    ax
  db     0EAh // jmp far X:X
@@save_i10:
dd     0
@@FPU_fault1:
  popf
@@FPU_fault:
  or     byte ptr @@triggered,ah
  fnclex
  jmp    @@exception_fired
@@ignore:
  popf
  pop    ax
  iret
@@triggered:
  db     0
@@saved_ds:
  dw     0
@@saved_sp:
  dw     0
@@loc_fpu_sw:
  dw     0
@@done:
end;

var
  x: byte;
begin
  if Test8086>1 then // i386+
    begin
      writeln('MSW CR0::NE = ',(get_MSW()and CPU_CR0_NE)<>0);
      set_MSW(get_MSW() or CPU_CR0_NE);
      if (get_MSW() and CPU_CR0_NE)=0 then
        writeln('Impossible to set MSW CR0::NE bit')
      else
        writeln('Successfully set MSW CR0::NE bit');
    end;
  if Test8087<1 then
    begin
      writeln('No FPU detected.');
      exit;
    end;
  if Test8087>1 then // i386+
    begin
      try
        writeln('direct CR0::NE = ',(get_CR0()and CPU_CR0_NE)<>0);
        set_CR0(get_CR0() or CPU_CR0_NE);
        if (get_CR0() and CPU_CR0_NE)=0 then
          writeln('Impossible to set CRO::NE bit')
        else
          writeln('Successfully set CR0::NE bit');
      except
        writeln('mov cr0,ax instruction not supported');
      end;
    end;
  write('Probing FPU exception route..');
  x := probe_FPU_exception_route();
  writeln('x=',x,' $',hexstr(x,2));
  if x and C_FPU_INT02<>0 then
    write(' INT02');
  if x and C_FPU_INT10<>0 then
    write(' INT10');
  if x and C_FPU_INT75<>0 then
    write(' INT75');
  case x of
    0: write(' ?none?');
    C_FPU_INT02,
    C_FPU_INT10,
    C_FPU_INT75:;
  else
    write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
  end;
  writeln;
  writeln('FPU Status word=',hexstr(fpu_sw,8));
  if (fpu_sw and FPU_ANY_E) = 0 then
    writeln('No status exception bit set');

  if (fpu_sw and FPU_SW_ES) = 0 then
    writeln('Status summary exception bit not set');
  if (fpu_sw and FPU_INV_E)<>0 then
    writeln('Invalid Bit Exception set');
  if (fpu_sw and FPU_DEN_E)<>0 then
    writeln('Denormal Bit Exception set');
  if (fpu_sw and FPU_DVZ_E)<>0 then
    writeln('Division by zero Bit Exception set');
  if (fpu_sw and FPU_OVF_E)<>0 then
    writeln('Overflow Bit Exception set');
  if (fpu_sw and FPU_UNF_E)<>0 then
    writeln('Underflow Bit Exception set');
  if (fpu_sw and FPU_PRC_E)<>0 then
    writeln('Precision Bit Exception set');
  if (fpu_sw and FPU_STACK_E)<>0 then
    writeln('FPU stack Bit Exception set');

  if Test8087>1 then // i386+
    begin
      try
        writeln('direct CR0::NE = ',(get_CR0() and CPU_CR0_NE)<>0);
        set_CR0(get_CR0() and (not CPU_CR0_NE));
        if (get_CR0() and CPU_CR0_NE)<>0 then
          writeln('Impossible to reset CRO::NE bit')
        else
          writeln('Successfully reset CR0::NE bit');
      except
        writeln('mov cr0,ax instruction not supported');
      end;
      write('Probing FPU exception route..');
      x := probe_FPU_exception_route();
      writeln('x=',x,' $',hexstr(x,2));
      if x and C_FPU_INT02<>0 then
        write(' INT02');
      if x and C_FPU_INT10<>0 then
        write(' INT10');
      if x and C_FPU_INT75<>0 then
        write(' INT75');
      case x of
        0: write(' ?none?');
        C_FPU_INT02,
        C_FPU_INT10,
        C_FPU_INT75:;
      else
        write(' ?more? [',x and not(C_FPU_INT02 or C_FPU_INT10 or C_FPU_INT75),']');
      end;
      writeln;
      writeln('FPU Status word=',hexstr(fpu_sw,8));
      if (fpu_sw and FPU_ANY_E) = 0 then
        writeln('No status exception bit set');

      if (fpu_sw and FPU_SW_ES) = 0 then
        writeln('Status summary exception bit not set');
      if (fpu_sw and FPU_INV_E)<>0 then
        writeln('Invalid Bit Exception set');
      if (fpu_sw and FPU_DEN_E)<>0 then
        writeln('Denormal Bit Exception set');
      if (fpu_sw and FPU_DVZ_E)<>0 then
        writeln('Division by zero Bit Exception set');
      if (fpu_sw and FPU_OVF_E)<>0 then
        writeln('Overflow Bit Exception set');
      if (fpu_sw and FPU_UNF_E)<>0 then
        writeln('Underflow Bit Exception set');
      if (fpu_sw and FPU_PRC_E)<>0 then
        writeln('Precision Bit Exception set');
      if (fpu_sw and FPU_STACK_E)<>0 then
        writeln('FPU stack Bit Exception set');
    end;
end.
fpuxtest-v2.pp (8,489 bytes)

Pierre Muller

2017-08-02 09:19

developer   ~0102030

  New version of test
fpuxtest-v2.pp uploaded,
please test.

  Pierre Muller

Max Nazhalov

2017-08-02 15:20

reporter   ~0102034

Here are preliminary results obtained with patched r36784.
I'll retest tomorrow with real silicon SBC.
Thanks for the test -- some results are rather surprising to me..

// NTVDM [win7-32bit]
// all memory models
MSW CR0::NE = TRUE
Successfully set MSW CR0::NE bit
direct CR0::NE = TRUE
Successfully set CR0::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set
direct CR0::NE = TRUE
Impossible to reset CRO::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set

// BOCHS 2.6.8 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222], bare and with himem
// all memory models except HUGE
// apparently LMSW is unable to change NE ??
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Successfully set CR0::NE bit
Probing FPU exception route..x=2 $02
 INT10
FPU Status word=0000B881
Invalid Bit Exception set
direct CR0::NE = TRUE
Successfully reset CR0::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set

// BOCHS 2.6.8 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222]; bare and with HIMEM
// HUGE: possibly test-v2 is buggy here: reboot/hang/cannot change NE bit (didn't investigate yet; sometimes prints as below)
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Impossible to set CRO::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set
direct CR0::NE = FALSE
Successfully reset CR0::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set

// BOCHS 2.6.8 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222], HIMEM+EMM386
// all memory models except HUGE
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Successfully set CR0::NE bit
Probing FPU exception route..
(EMM386 does not properly reflect exeption 10h into V86, so probing halts/hangs/reboots here; that is expected behavior for NE=1 under EMM386)

// VirtualBox 5.1.22 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222]; bare
// all memory models except HUGE
(same as for BOCHS)

// VirtualBox 5.1.22 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222]; HIMEM
// all memory models except HUGE
// another surprise to me: DIV0 exception is triggered ??
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Successfully set CR0::NE bit
Probing FPU exception route..x=2 $02
 INT10
FPU Status word=00008084
Division by zero Bit Exception set
direct CR0::NE = TRUE
Successfully reset CR0::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=00008084
Division by zero Bit Exception set

// VirtualBox 5.1.22 [win7-32bit]; MS-DOS 7.10 [win98-4.10.2222]; HIMEM+EMM386
(almost the same as for BOCHS: NE=1 is not viable)

// DOSBox 0.74 [win7-32bit]
// all memory models except HUGE
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Successfully set CR0::NE bit
Probing FPU exception route..x=0 $00
 ?none?
FPU Status word=00000000
No status exception bit set
Status summary exception bit not set
direct CR0::NE = TRUE
Successfully reset CR0::NE bit
Probing FPU exception route..x=0 $00
 ?none?
FPU Status word=00000000
No status exception bit set
Status summary exception bit not set

Max Nazhalov

2017-08-02 15:51

reporter   ~0102035

Last edited: 2017-08-02 15:56

View 2 revisions

About LMSW:
current (~P5+) Intel mans say "Only the loworder 4 bits of the source operand (which contains the PE, MP, EM, and TS flags) are loaded into CR0. The PG, CD, NW, AM, WP, NE, and ET flags of CR0 are not affected."
However that was not mentioned in i386/i486 docs.
So this manner to change NE can be considered as unreliable.

Anyway, I'm sure FPC RTL itself must not set/reset NE, it just should adopt the current state smoothly at best, so it is just a test oddity.

Max Nazhalov

2017-08-02 16:11

reporter   ~0102036

Last edited: 2017-08-02 16:57

View 3 revisions

About non-working HUGE mmodel test (did not tried yet to fix and test): possibly missing parts (in interrupt handlers) are

%ifdef __HUGE__
        push ds
        mov ax, SYSTEM_DATA
        mov ds, ax
%endif
...
%ifdef __HUGE__
        pop ds
%endif

I've tried to avoid globals in the original file, but they return. ;-)

Pierre Muller

2017-08-02 17:24

developer   ~0102037

A real test would need to install the interrupt handlers,
generate both FPU and crt calls,
nad check that all works well...

  Concerning huge model,
SYSTEM_DATA is not necessarily the same as
the data segment of the main program ...
But you probably meant the code in the system unit in prt0comm.as file.

 It's true that avoiding global variables would have
simplified the things, but as the debugging is so difficult,
I wanted to be able to print out the values...

  Of course, we could simplify the code, by putting
only the 'special' parts into asm blocks
and do the rest in regular pascal.

  Concerning dosbox, if you use a 32bit version
compiled with
#define C_FPU_X86 1
then the status word reflects the exceptions generated
(but no interrupt is called).

Max Nazhalov

2017-08-02 17:48

reporter   ~0102038

Tomorrow I'll deep-trace huge mmodel with bochs to see where it lands.
And DOSBox is not the main target to test, it's just included for completeness.. (aha, indeeded it's self-built with a few FPU-unrelated modifications a year or two ago, I forgot about this, sorry. I even do not remember the defaults it was built from).

Max Nazhalov

2017-08-03 04:36

reporter   ~0102048

Two small corrections:

set_MSW():
- mov word ptr value,ax
+ mov ax, word ptr value

set_CR0():
- mov word ptr value, ax //mov dword ptr value,eax
+ mov ax, word ptr value //mov eax, dword ptr value

That was the reason of crash in HUGE mmodel (ax hiddenly reloaded with ds on proc entry), and it worked purely by accident in all other mmodels (ax kept intact from the previous call to get_MSW/CR0).

Max Nazhalov

2017-08-03 04:45

reporter   ~0102049

Last edited: 2017-08-03 05:32

View 2 revisions

// SBC with Intel Pentium E2160; MS-DOS 7.10 [win98-4.10.2222]; bare and with HIMEM
MSW CR0::NE = FALSE
Impossible to set MSW CR0::NE bit
direct CR0::NE = FALSE
Successfully set CR0::NE bit
Probing FPU exception route..x=2 $02
 INT10
FPU Status word=0000B881
Invalid Bit Exception set
direct CR0::NE = TRUE
Successfully reset CR0::NE bit
Probing FPU exception route..x=4 $04
 INT75
FPU Status word=0000B881
Invalid Bit Exception set

Issue History

Date Modified Username Field Change
2017-05-25 07:37 Max Nazhalov New Issue
2017-05-25 20:58 Nikolay Nikolov Assigned To => Pierre Muller
2017-05-25 20:58 Nikolay Nikolov Status new => assigned
2017-06-12 09:36 Pierre Muller Fixed in Revision => 36488
2017-06-12 09:36 Pierre Muller Note Added: 0101045
2017-06-12 09:36 Pierre Muller Status assigned => resolved
2017-06-12 09:36 Pierre Muller Fixed in Version => 3.0.4
2017-06-12 09:36 Pierre Muller Resolution open => fixed
2017-06-12 13:22 Max Nazhalov Note Added: 0101060
2017-06-12 13:22 Max Nazhalov Status resolved => feedback
2017-06-12 13:22 Max Nazhalov Resolution fixed => reopened
2017-06-13 22:40 Pierre Muller Note Added: 0101104
2017-06-15 22:32 Pierre Muller Fixed in Revision 36488 => 36497
2017-06-15 22:32 Pierre Muller Note Added: 0101169
2017-06-15 22:32 Pierre Muller Status feedback => resolved
2017-06-15 22:32 Pierre Muller Resolution reopened => fixed
2017-06-17 09:00 Max Nazhalov Note Added: 0101200
2017-06-17 09:00 Max Nazhalov Status resolved => feedback
2017-06-17 09:00 Max Nazhalov Resolution fixed => reopened
2017-06-17 17:20 Max Nazhalov Note Edited: 0101200 View Revisions
2017-06-17 17:39 Max Nazhalov Note Edited: 0101200 View Revisions
2017-07-26 15:56 Max Nazhalov File Added: take1.diff
2017-07-26 15:57 Max Nazhalov Note Added: 0101922
2017-07-26 15:57 Max Nazhalov Status feedback => assigned
2017-07-26 16:42 Pierre Muller Note Added: 0101924
2017-07-26 16:46 Max Nazhalov File Added: fpuxtest.pp
2017-07-26 16:46 Max Nazhalov Note Added: 0101925
2017-07-29 13:54 Pierre Muller Fixed in Revision 36497 => 36813
2017-07-29 13:54 Pierre Muller Note Added: 0101982
2017-07-29 13:54 Pierre Muller Status assigned => resolved
2017-07-29 13:54 Pierre Muller Fixed in Version 3.0.4 => 4.0.0
2017-07-29 13:54 Pierre Muller Resolution reopened => fixed
2017-07-29 13:54 Pierre Muller Target Version => 4.0.0
2017-07-29 14:42 Max Nazhalov Note Added: 0101983
2017-07-29 14:42 Max Nazhalov Status resolved => closed
2017-07-29 14:49 Max Nazhalov Status closed => feedback
2017-07-29 14:49 Max Nazhalov Resolution fixed => reopened
2017-07-29 14:51 Max Nazhalov Note Edited: 0101983 View Revisions
2017-07-29 15:07 Max Nazhalov Note Edited: 0101983 View Revisions
2017-07-29 15:12 Max Nazhalov Note Edited: 0101983 View Revisions
2017-08-02 09:16 Pierre Muller Note Added: 0102029
2017-08-02 09:16 Pierre Muller Status feedback => confirmed
2017-08-02 09:18 Pierre Muller File Added: fpuxtest-v2.pp
2017-08-02 09:19 Pierre Muller Note Added: 0102030
2017-08-02 09:19 Pierre Muller Status confirmed => feedback
2017-08-02 15:20 Max Nazhalov Note Added: 0102034
2017-08-02 15:20 Max Nazhalov Status feedback => assigned
2017-08-02 15:51 Max Nazhalov Note Added: 0102035
2017-08-02 15:56 Max Nazhalov Note Edited: 0102035 View Revisions
2017-08-02 16:11 Max Nazhalov Note Added: 0102036
2017-08-02 16:14 Max Nazhalov Note Edited: 0102036 View Revisions
2017-08-02 16:57 Max Nazhalov Note Edited: 0102036 View Revisions
2017-08-02 17:24 Pierre Muller Note Added: 0102037
2017-08-02 17:48 Max Nazhalov Note Added: 0102038
2017-08-03 04:36 Max Nazhalov Note Added: 0102048
2017-08-03 04:45 Max Nazhalov Note Added: 0102049
2017-08-03 05:32 Max Nazhalov Note Edited: 0102049 View Revisions