Преглед изворни кода

+ generate proper win16 prologue/epilogue for exported routines (we don't yet
actually export them from the .exe or .dll, but they can now be used as
callbacks)
+ support win16 smart callbacks via the $K switch

git-svn-id: trunk@31835 -

nickysn пре 10 година
родитељ
комит
b26e10d53c
5 измењених фајлова са 102 додато и 10 уклоњено
  1. 2 1
      compiler/globtype.pas
  2. 13 3
      compiler/i8086/cgcpu.pas
  3. 2 2
      compiler/i8086/symcpu.pas
  4. 8 0
      compiler/switches.pas
  5. 77 4
      compiler/x86/cgx86.pas

+ 2 - 1
compiler/globtype.pas

@@ -169,7 +169,8 @@ interface
          { target specific }
          cs_executable_stack,
          { i8086 specific }
-         cs_huge_code
+         cs_huge_code,
+         cs_win16_smartcallbacks
        );
        tmoduleswitches = set of tmoduleswitch;
 

+ 13 - 3
compiler/i8086/cgcpu.pas

@@ -1762,8 +1762,16 @@ unit cgcpu;
         { remove stackframe }
         if not nostackframe then
           begin
-            if (current_settings.x86memorymodel=mm_huge) and
-                not (po_interrupt in current_procinfo.procdef.procoptions) then
+            if (po_exports in current_procinfo.procdef.procoptions) and
+               (target_info.system=system_i8086_win16) then
+              begin
+                list.concat(Taicpu.Op_reg(A_POP,S_W,NR_DI));
+                list.concat(Taicpu.Op_reg(A_POP,S_W,NR_SI));
+              end;
+            if ((current_settings.x86memorymodel=mm_huge) and
+                not (po_interrupt in current_procinfo.procdef.procoptions)) or
+               ((po_exports in current_procinfo.procdef.procoptions) and
+                (target_info.system=system_i8086_win16)) then
               list.concat(Taicpu.Op_reg(A_POP,S_W,NR_DS));
             if (current_procinfo.framepointer=NR_STACK_POINTER_REG) then
               begin
@@ -1781,7 +1789,9 @@ unit cgcpu;
             else
               begin
                 generate_leave(list);
-                if (ts_x86_far_procs_push_odd_bp in current_settings.targetswitches) and
+                if ((ts_x86_far_procs_push_odd_bp in current_settings.targetswitches) or
+                    ((po_exports in current_procinfo.procdef.procoptions) and
+                     (target_info.system=system_i8086_win16))) and
                     is_proc_far(current_procinfo.procdef) then
                   cg.a_op_const_reg(list,OP_SUB,OS_ADDR,1,current_procinfo.framepointer);
               end;

+ 2 - 2
compiler/i8086/symcpu.pas

@@ -387,8 +387,8 @@ implementation
 
   function tcpuprocdef.is_far: boolean;
     begin
-      result:=(current_settings.x86memorymodel in x86_far_code_models) and
-        ((po_far in procoptions) or default_far);
+      result:=(po_exports in procoptions) or
+              ((current_settings.x86memorymodel in x86_far_code_models) and ((po_far in procoptions) or default_far));
     end;
 
 {****************************************************************************

+ 8 - 0
compiler/switches.pas

@@ -73,7 +73,11 @@ const
    {H} (typesw:localsw; setsw:ord(cs_refcountedstrings)),
    {I} (typesw:localsw; setsw:ord(cs_check_io)),
    {J} (typesw:localsw; setsw:ord(cs_typed_const_writable)),
+{$ifdef i8086}
+   {K} (typesw:modulesw; setsw:ord(cs_win16_smartcallbacks)),
+{$else i8086}
    {K} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
+{$endif i8086}
    {L} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
    {M} (typesw:localsw; setsw:ord(cs_generate_rtti)),
    {N} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
@@ -111,7 +115,11 @@ const
    {H} (typesw:localsw; setsw:ord(cs_refcountedstrings)),
    {I} (typesw:localsw; setsw:ord(cs_check_io)),
    {J} (typesw:localsw; setsw:ord(cs_external_var)),
+{$ifdef i8086}
+   {K} (typesw:modulesw; setsw:ord(cs_win16_smartcallbacks)),
+{$else i8086}
    {K} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
+{$endif i8086}
    {L} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
    {M} (typesw:localsw; setsw:ord(cs_generate_rtti)),
    {N} (typesw:unsupportedsw; setsw:ord(cs_localnone)),

+ 77 - 4
compiler/x86/cgx86.pas

@@ -2859,8 +2859,61 @@ unit cgx86;
 
       begin
 {$ifdef i8086}
+        { Win16 callback/exported proc prologue support.
+          Since callbacks can be called from different modules, DS on entry may be
+          initialized with the data segment of a different module, so we need to
+          get ours. But we can't do
+
+                      push ds
+                      mov ax, dgroup
+                      mov ds, ax
+
+          because code segments are shared between different instances of the same
+          module (which have different instances of the current program's data segment),
+          so the same 'mov ax, dgroup' instruction will be used for all instances
+          of the program and it will load the same segment into ax.
+
+          So, the standard win16 prologue looks like this:
+
+                      mov ax, ds
+                      nop
+                      inc bp
+                      push bp
+                      mov bp, sp
+                      push ds
+                      mov ds, ax
+
+          By default, this does nothing, except wasting a few extra machine cycles and
+          destroying ax in the process. However, Windows checks the first three bytes
+          of every exported function and if they are 'mov ax,ds/nop', they are replaced
+          with nop/nop/nop. Then the MakeProcInstance api call should be used to create
+          a thunk that loads ds for the current program instance in ax before calling
+          the routine.
+
+          And now the fun part comes: somebody (Michael Geary) figured out that all this
+          crap was unnecessary, because in Win16 exe modules, we always have DS=SS, so we
+          can simply initialize DS from SS :) And then calling MakeProcInstance becomes
+          unnecessary. This is what "smart callbacks" (cs_win16_smartcallbacks) do. However,
+          this only works for exe files, not for dlls, because dlls run with DS<>SS. There's
+          another solution for dlls - since win16 dlls only have a single instance of their
+          data segment, we can initialize ds from dgroup. However, there's not a single
+          solution for both exe and dlls, so we don't know what to use e.g. in a unit. So,
+          that's why there's still an option to turn smart callbacks off and go the
+          MakeProcInstance way.
+
+          Additional details here: http://www.geary.com/fixds.html }
+        if (current_settings.x86memorymodel<>mm_huge) and
+           (po_exports in current_procinfo.procdef.procoptions) and
+           (target_info.system=system_i8086_win16) then
+          begin
+            if cs_win16_smartcallbacks in current_settings.moduleswitches then
+              list.concat(Taicpu.Op_reg_reg(A_MOV,S_W,NR_SS,NR_AX))
+            else
+              list.concat(Taicpu.Op_reg_reg(A_MOV,S_W,NR_DS,NR_AX));
+            list.concat(Taicpu.op_none(A_NOP));
+          end
         { interrupt support for i8086 }
-        if po_interrupt in current_procinfo.procdef.procoptions then
+        else if po_interrupt in current_procinfo.procdef.procoptions then
           begin
             list.concat(Taicpu.Op_reg(A_PUSH,S_W,NR_AX));
             list.concat(Taicpu.Op_reg(A_PUSH,S_W,NR_BX));
@@ -2941,7 +2994,9 @@ unit cgx86;
             else
               begin
 {$ifdef i8086}
-                if (ts_x86_far_procs_push_odd_bp in current_settings.targetswitches) and
+                if ((ts_x86_far_procs_push_odd_bp in current_settings.targetswitches) or
+                    ((po_exports in current_procinfo.procdef.procoptions) and
+                     (target_info.system=system_i8086_win16))) and
                     is_proc_far(current_procinfo.procdef) then
                   cg.a_op_const_reg(list,OP_ADD,OS_ADDR,1,current_procinfo.framepointer);
 {$endif i8086}
@@ -2983,8 +3038,16 @@ unit cgx86;
               end;
 
 {$ifdef i8086}
-              if (current_settings.x86memorymodel=mm_huge) and
-                 not (po_interrupt in current_procinfo.procdef.procoptions) then
+              { win16 exported proc prologue follow-up (see the huge comment above for details) }
+              if (current_settings.x86memorymodel<>mm_huge) and
+                 (po_exports in current_procinfo.procdef.procoptions) and
+                 (target_info.system=system_i8086_win16) then
+                begin
+                  list.concat(Taicpu.op_reg(A_PUSH,S_W,NR_DS));
+                  list.concat(Taicpu.Op_reg_reg(A_MOV,S_W,NR_AX,NR_DS));
+                end
+              else if (current_settings.x86memorymodel=mm_huge) and
+                      not (po_interrupt in current_procinfo.procdef.procoptions) then
                 begin
                   list.concat(Taicpu.op_reg(A_PUSH,S_W,NR_DS));
                   reference_reset(fardataseg,0);
@@ -2992,6 +3055,16 @@ unit cgx86;
                   list.concat(Taicpu.Op_ref_reg(A_MOV,S_W,fardataseg,NR_AX));
                   list.concat(Taicpu.Op_reg_reg(A_MOV,S_W,NR_AX,NR_DS));
                 end;
+            { SI and DI are volatile in the BP7 and FPC's pascal calling convention,
+              but must be preserved in Microsoft C's pascal calling convention, and
+              since Windows is compiled with Microsoft compilers, these registers
+              must be saved for exported procedures (BP7 for Win16 also does this). }
+            if (po_exports in current_procinfo.procdef.procoptions) and
+               (target_info.system=system_i8086_win16) then
+              begin
+                list.concat(Taicpu.Op_reg(A_PUSH,S_W,NR_SI));
+                list.concat(Taicpu.Op_reg(A_PUSH,S_W,NR_DI));
+              end;
 {$endif i8086}
 
 {$ifdef i386}