ABOUT US  |  PRODUCTS  |  LIBRARY  |  PARTNERS  |  SUPPORT  |  NEWS  |  DOWNLOADS  |  ORDER  |  CONTACT
 
  You are here: Home > Library > White Papers
   
Using 32-bit Registers in 16-bit Modes


Summary

This paper discusses use of 32-bit registers in real mode and 16-bit protected mode in an smx system running on a 386 processor or higher. It is necessary to use a compiler switch and enable a setting in smx before using 32-bit registers. The advantages and disadvantages of using this option are discussed, as well as how to enable it. Once enabled, the compiler will generate code that uses 32-bit registers when beneficial, and 32-bit registers can be used from assembly code. This option requires smx source code.

Motivations for Using 32-bit Registers

  1. Efficient 32-bit calculations and other operations
  2. Reduced code size
  3. Data segments larger than 64K can be used in 16-bit protected mode (from assembly code).

Disadvantages of using 32-bit registers

  1. Impacts performance of all isr's when running on a processor that has a 16-bit data bus, such as the 386EX or NS486SXF.

32-bit Operations

Using /G3 (Microsoft) or -3 (Borland, Paradigm) tells the compiler that the target is a 386 or higher. Since the 386 offers 32-bit registers, the compiler generates more efficient code for 32-bit operations, such as 32-bit integer operations. Otherwise, arithmetic on 32-bit integers is done using two 16-bit registers (ax and dx), which takes more time and code.

Note: Paradigm C++ is based on Borland C++ so the notes here for Borland C++ apply to it as well. However, starting with compiler v5.40, Paradigm changed the prolog/epilog for isr's to be more efficient, like Microsoft.

Reduced Code Size

Use of 32-bit registers often means fewer instructions, and it can result in significantly smaller code. This can be a lifesaver if you are already pushing the limit on ROM space and you need to add new features. This was the case for one of our real mode customers who saved a significant amount of code space by enabling use of 32-bit registers.

With the /G3 or -3 switch, the compiler may also take advantage of other new instructions to reduce code. In addition, the Microsoft compiler generates more efficient jump sequences with /G3 than /G1 or /G2, which is strange since this could be done for /G1 and /G2 also (and Borland does generate the more efficient sequence with its -1 and -2 switches). Apparently, Microsoft learned a few tricks but left the older /G1 and /G2 switches to operate as they had before.

64K Data Segments in 16-bit PM

In 16-bit protected mode, it is the 16-bit C/C++ compiler that prevents you from having segments larger than 64K. In assembly language, though, it is possible to create 32-bit segments and access them with 32-bit offsets. Use the DPMI functions to allocate a descriptor and set the segment type to a use32 segment. Set the segment limit as desired. (Note that this requires a minor change to the pmEasy DPMI server to remove the limit restriction for PME16 in setLimit. As shipped, the code only permits segment limits < 1MB to conform to the DPMI specification.)

Impact on isr's

isr's written in C use the 'interrupt' keyword, which causes all registers to be saved and restored. When use of 32-bit registers is enabled, the compiler preserves registers: eax, ebx, ecx, edx, esi, and edi instead of ax, bx, cx, dx, si, and di. For processors that have only a 16-bit data bus, saving eax rather than ax requires an additional memory access. (eax is saved in 2 parts.) This means a total of 12 extra memory accesses. The next section shows the prolog and epilog for isr's when using /G2 vs. /G3 and -2 vs. -3. Lines that differ are marked. If your target has a 16-bit data bus, you should consider the performance impact of using 32-bit register saving. The default operation of real mode and 16-bit protected mode smx is to preserve only 16-bit registers. This is the way the pre-built smx libraries and all makefiles are set to operate. If you want to enable 32-bit register saving, you must have smx source code so you can rebuild the smx library with the proper settings. Otherwise, the stack will not be as the scheduler expects and execution will fail.

isr Prologs and Epilogs for 16-bit Compilers

Microsoft:

             /G3:                    /G1 or /G2: 

        pushad                      pusha                  ;* 
        push    ds                  push    ds 
        push    es                  push    es 
        mov     bp,sp               mov     bp,sp 
        push    ds                  push    ds 
        mov     ax,DGROUP           mov     ax,DGROUP 
        mov     ds,ax               mov     ds,ax 
        cld                         cld 
        --------------------------------------------- 
        mov     sp,bp               mov     sp,bp 
        pop     es                  pop     es 
        pop     ds                  pop     ds 
        popad                       popa                   ;* 
        iret                        iret 

 

Borland:
             -3:                    -1 or -2:

        push    eax                 push    ax             ;* 
        push    ebx                 push    bx             ;* 
        push    ecx                 push    cx             ;* 
        push    edx                 push    dx             ;* 
        push    es                  push    es 
        push    ds                  push    ds 
        push    esi                 push    si             ;* 
        push    edi                 push    di             ;* 
        push    bp                  push    bp 
        mov     bp,DGROUP           mov     bp,DGROUP 
        mov     ds,bp               mov     ds,bp 
        mov     bp,sp               mov     bp,sp 
        sub     sp,6                sub     sp,6 
        cld                         cld 
        --------------------------------------------- 
        leave                       leave 
        pop     edi                 pop     di             ;* 
        pop     esi                 pop     si             ;* 
        pop     ds                  pop     ds 
        pop     es                  pop     es 
        pop     edx                 pop     dx             ;* 
        pop     ecx                 pop     cx             ;* 
        pop     ebx                 pop     bx             ;* 
        pop     eax                 pop     ax             ;* 
        iret                        iret 
 Register Usage with 32-bit Registers Enabled

The following is a summary of the set of 32-bit registers that are used in the code generated by the Microsoft and Borland 16- bit compilers when /G3 or -3 is used. This table is based on our examination of a modest amount of code generated by each compiler. There is the possibility that other registers are used in special cases, but it seems unlikely to us.

                       MC   BC 
                  eax   y    y 
                  ebx   -    y 
                  ecx   y    - 
                  edx   y    y 
                  esi   -    - 
                  edi   -    - 
                  ebp   -    - 

From our study of generated code, the usual rule that di, si, and bp are preserved across C calls still applies for Microsoft and Borland when using /G3 or -3. Interestingly, neither of these compilers seems to use edi, esi, or ebp, so these are essentially preserved as well. In assembly code, one could probably safely expect these extended registers to be preserved across a C call. However, this is not true if that C call is an smx API call, because smx calls can result in a task switch. Another task could use assembly code to alter one of these extended registers, causing trouble for the first task that expected its value to be retained. Also, keep in mind that if you call a non-smx C function, it (or a function it calls) might make an smx call. We could alter the suspend-on-call path through the scheduler to save these extended registers on a task switch, due to an smx call, but for machines with a 16-bit data bus, saving 32-bit registers entails extra overhead and we felt it was best to avoid this, since it is not difficult for you to save any of these registers before making an smx call, if you depend on the value being preserved. Most users will write in C or C++ and very little assembly code will be present, so we do not want to penalize all task switches with this extra overhead.

Directions for Enabling Use of 32-bit Registers

  1. XDEF.INC, XDEF.H: Set REGSAVE32 to 1. This enables the proper versions of some smx macros used by the smx scheduler and in assembly isr's.
  2. Change all makefiles to use /G3 (Microsoft) or -3 (Borland).
    This switch must be used for ALL code, including the smx library and all of your application code. (Actually, it must be done only for modules that have isr's, but it is safest to do it for all modules, to prevent problems if an isr is added to a module in the future.)
    It is necessary to be consistent in using /G3 or -3 for all modules so that the compiler generates the same prolog and epilog for all isr's. These must match what is expected in the macros in xmac.inc, which are used by the scheduler and isr's. All isr's must push and pop the same registers.

    1. All users: Change mak.bat's to set p=3 and change the message to say "MAKING ... FOR PROCESSOR 3". Then change the makefiles:

    2. P1 users:
          • Library makefiles: Should be ok.
          • Protosystem makefile: MC.P1 and PC.P1 should be ok. For BC.P1, the pr macro definition section should look like this       (notice the 4th line):
                !if($(p) == 0)
                pr = 1-
                !else
                pr = $(p)
                !endi
           • PC.P1 users If using the IDE, change the processor setting in the project: Options | Style Sheets, select one of the SMX          style sheets and press Edit. In the left pane, select 16-bit compiler, then Processor. In the right pane, select 80386. Do again          for the other SMX style sheet. If using the makefile to build the Protosystem, change the CPU macro in PARADIGM.OPT to 3          and edit PARADIGM.MKF, where it uses CPU, to add -3 to the CFLAGS macro if $(CPU) == 3.

    3. P2 users:
          1. Library makefiles: Change !if "$(p)" == "2" To !if ("$(p)" == "2" || "$(p)" == "3")
          2. Protosystem makefile: Change /G2 to /G3 or -2 to -3.
  3. PRO.MAK: In all macros that specify paths to libraries, change $(p) to 1 (real mode users) or 2 (16-bit pm users), so that the paths to the libraries are correct.
  4. Add USE16 to all segment definitions in .asm modules if not already there. Example: _TEXT SEGMENT BYTE PUBLIC USE16 'CODE'
  5. Rebuild everything -- all libraries and your application.
  6. Test

 

Please contact me with any comments or questions about this article.

David Moore
Director of Development
Micro Digital Inc
1-800-366-2491

back to White Papers page

 
HOME  |  SITEMAP  |  CONTACT