Retro video games delivered to your door every month!
Click above to get retro games delivered to your door ever month!
X-Hacker.org- Peter Norton Programmer's Guide - Norton Guide http://www.X-Hacker.org [<<Previous Entry] [^^Up^^] [Next Entry>>] [Menu] [About The Guide]

  A simple rule of thumb can help you determine how a program's memory model
  affects the design of its subroutines: If you have multiple segments, use
  far (intersegment) addressing; if you have a single segment, use near
  (intrasegment) addressing. Let's see how this simple rule can be applied
  in a pair of real subroutines.

  The following variation of our absolute-value subroutine is designed for a
  medium-model C program. A medium-model program has multiple code segments
  but only one data segment. Subroutines in separate segments must be
  accessed through far jumps and far call-return sequences, but the single
  data segment can be accessed with near addresses:

  MEDABS_TEXT     SEGMENT byte public 'CODE'
                  ASSUME  cs:MEDABS_TEXT

                  PUBLIC  _MedAbs
  _MedAbs         PROC    far             ; call with far CALL

                  push    bp
                  mov     bp,sp

                  mov     bx,[bp+6]       ; BX = address of 1st parameter
                  mov     ax,[bx]

                  cwd
                  xor     ax,dx
                  sub     ax,dx

                  mov     [bx],ax         ; leave result at parameter address

                  pop     bp
                  ret                     ; far RETurn

  _MedAbs         ENDP

  MEDABS_TEXT     ENDS

  This medium-model version (MedAbs()), looks very much like SmallAbs(). In
  MedAbs(), the PROC statement declares that the routine is to be called
  with a far CALL and instructs the assembler to generate a far RETurn
  instruction instead of a near RETurn. Because MedAbs() is called with a
  far CALL, the stack contains a segmented return address (4 bytes) as well
  as the saved value of BP (2 bytes), so the subroutine looks for its
  parameter at [BP + 6] instead of [BP + 4].

  A large-model program introduces one more variation in subroutine design.
  Because a large-model program uses multiple data segments, the addresses
  of subroutine parameters are far (segmented) addresses.

  LARGEABS_TEXT   SEGMENT byte public 'CODE'
                  ASSUME  cs:LARGEABS_TEXT

                  PUBLIC  _LargeAbs
  _LargeAbs       PROC    far             ; call with far CALL

                  push    bp
                  mov     bp,sp

                  les     bx,[bp+6]       ; ES:BX = segmented address
                                          ; of first parameter
                  mov     ax,es:[bx]      ; AX = value of first parameter
                  cwd
                  xor     ax,dx
                  sub     ax,dx

                  mov     es:[bx],ax      ; leave result at parameter address

                  pop     bp
                  ret                     ; far RETurn

  _LargeAbs       ENDP

  LARGEABS_TEXT   ENDS

  Because it conforms to a large memory model, LargeAbs() is designed to
  obtain both segment and offset from the stack (LES BX,[BP + 6]). The
  segment part of the parameter's address goes into ES; the offset goes into
  BX. The subroutine uses this register pair to obtain the parameter's value
  (MOV AX,ES:[BX]) and to return a result (MOV ES:[BX],AX).

  If you call LargeAbs() like this:

  LargeAbs( &x );

  a C compiler generates executable code that looks something like this:

  push ds                 ; push the parameter's segment
  mov ax,offset X         ; push the parameter's offset
  push ax
  call _LargeAbs          ; call the subroutine (far call)
  add  sp,4               ; discard the address from the stack

Online resources provided by: http://www.X-Hacker.org --- NG 2 HTML conversion by Dave Pearson