Measurement Computing Personal488 rev.3.0 For DOS & Windows 3.Xi User Manual
Page 114
II. SOFTWARE GUIDES - 8. Driver488/DRV
8K. Other Languages
Personal488 User’s Manual, Rev. 3.0
II-99
#if defined(M_I86SM) || defined(M_I86MM)
int segment(ptr)
void near *ptr;
{ static struct SREGS segs = { 0, 0, 0, 0, };
if (segs.ds==0) segread(&segs);
return segs.ds;
}
#define offset(ptr) (int)ptr
#else
#define offset(ptr) ((unsigned)(fp))
#define segment(ptr) ((unsigned)((unsigned long)(fp) > 16))
#endif
The
#IF
statement checks if we are using the Small (
M_I86SM
) or Medium (
M_I86MM
) models
(Microsoft C does not support the Tiny model) and, if we are, defines a function segment that returns
the (constant) segment value that can be used for all data elements. The
segment
function takes a
pointer (
ptr
) as an argument, but does not use it. Instead, the first time it is called, it calls the
segread
library function to read the
ds
segment register and returns that value. Each subsequent time
segment
is called, it returns the saved
ds
register value. In these small-data models, the pointer to a
data object is just the
offset
address of that object, and so we define
offset(ptr)
as
(int)ptr
,
which is just an integer with the same value as the pointer.
If we are using a large-data model, data pointers are 32 bits, and the
segment
and
offset
must be
extracted from the 32-bit far pointer (
fp
). The
offset
, which is the least-significant word of the
pointer, is extracted by converting the pointer to
unsigned
. This discards the upper 16-bit of the
pointer, leaving the segment value. The
segment
is extracted by interpreting (casting) the pointer as
an
unsigned long
(32-bit) integer, shifting the result right 16 places, and then taking the least
significant word of the result. In this way, the
offset
and
segment
are extracted from the 32-bit
far pointer.
Calling Protocols
If the programming language we are using does not provide functions that return the
segment
or
offset
of an object, we must write our own. The data object whose address we desire is passed as an
argument to this function. The calling protocols for that language specify just how information about
the arguments is passed to the function.
There are two popular methods of passing arguments: call-by-value and call-by-reference. Other
methods, such as call-by-name which is used in Algol 60, are possible, but are not used in common
microcomputer languages.
Call-By-Value
In call-by-value, which is the only method used by C, the actual arguments are copied, and these copies
are passed to the subprogram. If the address of a variable is needed by the subprogram then that
address must be explicitly passed to the subprogram. For example:
int i; Declare and integer variable i.
i=5; Set i to 5
fun1(i); Call fun1 with an argument of 5.
fun2(&i); Call fun2 with an argument equal to the address of i.
Notice that if
fun1
tries to change the value of
i
, it only changes its own copy of
i
, not the calling
program’s variable. In contrast,
fun2
has the address of
i
and can get access to the calling program’s
variable and change its value. In call-by-value, the entire data element must be duplicated and passed
to the subprogram. This is not a problem for simple variables, but can be quite awkward if a large
array must be copied to be passed to the subprogram. Large data structures are rarely passed using
call-by-value. Instead, the address of the data structure is passed in what is, in effect, call-by-reference.
Call-By-Reference
Call-by-reference is the most common form of argument passing in languages other than C. In call-by-
reference, the address of the argument is passed to the subprogram. The size of this address may be 16
or 32 bits depending on the language and its memory model.