The macros for dual code segment support contain two different macros for differently handling inter-segment calls.
The easier to use is the
If calling within the same segment it simply expands
to a near immediate call (opcode
Otherwise it calls a helper function, with a target offset to call
stored in the code behind the helper function call opcode.
This helper thunk will rearrange the stack so that
it branches far to the desired target,
with two return addresses on the stack.
First is a near return address.
This will return to a small helper in the same code segment
as the target.
This helper will use the second return address, which is far,
to return to the original caller in the other segment.
(The second return address is actually "far-like". Details to follow
in the lDebugX dual call description.)
nearcall mechanism only works if the called target function does
not access parameters on its stack.
If such access is desired a
dualcall must be used instead.
The following first describes 86-Mode-only dual calls, which
cannot be used by DPMI-capable (lDebugX) builds.
dualcall macro will expand to a
push cs then
call near rel16
sequence if calling into the same code segment.
This sequence constructs a stack frame matching that of a far call.
When calling into the other code segment,
the macro expands into a
call far imm16:imm16 instruction,
with zero used as the segment.
The segment word of this instruction is entered into
a relocation patch table,
which is used by init to fill in the appropriate target segment.
We use our own patch table rather than emitting DOS MZ .EXE relocations for three reasons:
In the DPMI-capable lDebugX, the
dualcall macro will instead always call
a helper function. Each code segment has two such helpers:
One to call a function in the same segment, and one to do an actual inter-segment function call.
For both, the target offset is stored behind the (near) call instruction
in the caller's code.
The helper will construct a stack frame so that it can branch to the
desired target function, either near or far.
For true inter-segment calls this far branch always uses an actual
segment or selector, chosen by the helper thunk to match the current mode.
The target function will receive a stack frame consisting of a far-like return address,
as well as the stack as set up by the original caller.
A dual function must return with a call to the
dualreturn helper, then
retf imm16 instruction.
Further, dual functions have to use far call stack frames.
To simplify this, the macros define
far if dual code segments are in use and
near if not.
Dual functions generally should use
to set up their stack frames.
Then any number of
lpar and a use of
lpar_return are permitted.
The dual function can directly access parameters on its stack then.
The nature of far-like return addresses is that they take up 32 bits, just like actual far return addresses. However, the segment/selector value in the high word is instead used to store an index. The dual return helper examines the index and replaces it by the desired segment or selector to which to return. This complication allows any function to switch modes (from Real/Virtual 86 Mode to Protected Mode, or vice versa). The possible need for this was determined important enough to support such uses, although no current users are known. The index supports both modes. An actual segment or selector value is inserted to replace the index only in the dual return helper, which must be immediately followed by the far return instruction that will use this far return address.
are used to tell the macros which section a target function belongs to.
section_of for a specific symbol currently must be used
dualcall macro that uses that symbol.
The section to note down is the current active section as selected
usesection macros (from
section_of_function macro is used at a function's definition
to verify that the correct section was specified.
section_of may be used even when the specified function
does not exist, for example when it is not included
due to the current build options.
A label specified with the
however, must correspond to an earlier use of
This example is based on
the initial revision
that introduced the dual code sections support.
This is what the
bu_relocated function would look like
as a normal, near-callable function
within the single
bu_relocated: lframe near lpar word, sign lenter mov ax, word [bp + ?sign] mov di, msg.bu_relocated.sign call hexword mov dx, msg.bu_relocated call putsz lleave lret
Note that this hardcodes a near stack frame. And this is how it would be called:
mov ax, 2642h push ax call bu_relocated
Next, here's how to change it to a dual-callable function
which goes into the
if that is used:
%if _DUALCODE usesection lDEBUG_CODE2 %endif section_of bu_relocated dualfunction bu_relocated: lframe dualdistance lpar word, sign lenter mov ax, word [bp + ?sign] mov di, msg.bu_relocated.sign nearcall hexword mov dx, msg.bu_relocated nearcall putsz lleave dualreturn lret
And this is a caller:
mov ax, 2642h push ax dualcall bu_relocated
Note the uses of
When this source is compiled with dual code segments enabled,
the distance of the stack frame will be far instead.
If the build is DPMI-capable then
dualcall helper function
will push a far-like return address,
dualreturn helper will then
convert into a segmented far return address.
lret will expand to a
retf 2 in this case.
Converting function calls to use the
only involves replacing plain
nearcall macro invocation,
as well as specifying the correct sections
As mentioned, a function must not use
on its stack in order to call it using