Inter-Segment calls in assembler

-----------
> I wish to load an external piece of code from a file, and then execute
> it from within my program, but to do this, I have to update both CS and
> IP at the same time. I have read the explanations for JMP and CALL
> several times, but there is no description of HOW to perform a 'long'
> jmp (or call).
[...]
> I know how to make a ea. JMP 0000:0001, but it's a pain to modify the
> code in memory during run-time. btw this is in REAL mode.
[...]
> Thanks for one of the most interresting and useful pages on the net.
> RayMan DK
-----------

It is not as hard as you might think. As you stated above, when you use the CALL instruction, DOS puts the offset of the next instruction on the stack. Then when your code uses the (near) RET, the RET instruction POP's this offset off the stack and places it in IP. The Code Segment register (CS) is untouched.

Now for inter-segment calls. All you have to do is the same thing plus the code segment. Using the CALLF (Call Far) instruction pushes the offset of the next instruction on the stack just like before (call near), but it also pushes CS on to the stack. Now your 'overlay' code must use the RETF (RET Far) instruction which pops both IP and CS.

This technique works well on simple code routines. If you get more technical and start using 32 bit to 16 bit inter-segment calls or protected 32 bit calls, you might have to use the call gates of the CPU.

But for this demonstration, we will just do a simple call.
Please note that I use NBASM from here.
However, with minor modifications, this code should work with MASM, A86, and other assemblers.

;Assemble with NBASM

; we will allocate some memory, load some executable code
; in to it and then call it as a procedure

.model tiny
.code
.186

; We need to resize the memory block DOS allocated for this
;  program so that we can allocate our own memory below.
;  We could do this manually, but the NBASM directive .START
;  uses below does this for us.
           .start
           mov  ah,48h
           mov  bx,16                   ; 256 bytes should be plenty
           int  21h                     ;  adjust to your needs
           push ax                      ; save segment
           push ds
           push ax
           mov  ax,3D00h                ; open file for read
           mov  dx,offset File          ;
           int  21h                     ;
           mov  bx,ax                   ; bx = handle
           mov  ah,3Fh                  ; read from file
           mov  cx,25                   ; size of file (adjust to your needs)
           xor  dx,dx                   ;
           pop  ds                      ; put it at ds:0000h
           int  21h                     ;
           pop  ds                      ;
           mov  ah,3Eh                  ; close the file
           int  21h                     ;
           pop  ax                      ; restore seg
           mov  di,offset TheSeg        ; put the seg:offset in to our var
           mov  [di+2],ax               ; remember offset goes first
           xor  ax,ax                   ; 0000:seg
           mov  [di],ax                 ;
           callf [di]                   ; call far using the address at TheSeg

           mov  ah,4Ch                  ; exit to DOS
           int  21h                     ; (and free the mem for us)

File       db  's2.bin',0
TheSeg     dw  00h           ; offset  (0000h)
           dw  00h           ; segment
.end

Now for the called code. We must assemble this with the IP locator starting at offset 00h. Notice the ORG 00h.

;Assemble with NBASM

.model tiny
.code
.8086
           org  00h             ; must be 00h for offsets used

           push ds              ; we must set DS to our new CS
           push cs
           pop  ds
           mov  dx,offset Msg
           mov  ah,09
           int  21h
           pop  ds
           retf                 ; must use RET FAR

Msg        db 'Temp Message$'
.end

As stated above, you could create your own Overlays with this technique. Once you load the code and run it, you could
free the memory used and be able to use it for other resources. If your program needs to be small so that it has a lot of free
memory, this technique will help you out.