Chapter three
See Chapter 4: Programming with Inline Assembly for more information about using inline assembly with Spontaneous Assembly For C/C++.
In the library names above, m must be replaced with T, S, M, C, or L to denote TINY, SMALL, MEDIUM, COMPACT, and LARGE, and c must be replaced with B, M, T, Z, or N to denote Borland, Microsoft, TopSpeed, Zortech, or Non-emulated floating-point coprocessor instructions.
Most non-TSR applications are linked with both _SAm.LIB and _FPCcm.LIB to ensure that all necessary data conversion functions are linked into the application. However, _FPCcm.LIB is only necessary if floating-point data conversion or scripted I/O functions are used in the application.
If the Spontaneous Assembly heap management functions or floating-point emulation initialization functions are used, Spontaneous Assembly libraries should be specified before the other libraries on the compiler command line. Failure to do this may cause link errors.
The default when using the Scripted I/O and Data Conversion floating point functions requires the _FPCcm to be included. To coalesce a floating point library with a default _SAm library a librarian provided by each compiler may be used to add the two libraries together as follows:
The Spontaneous Assembly startup code is as follows:
Chapter four
"Assembly language serves many purposes, such as improving program speed, reducing memory needs, and controlling hardware."-Microsoft Corporation, Programming Techniques (Microsoft C/C++ 7.0), 1991, pg. 111
You do NOT need to understand inline assembly to use Spontaneous Assembly For C/C++. Inline assembly simply provides an alternate, advantageous method of accessing Spontaneous Assembly functions.
Spontaneous Assembly For C/C++ allows the programmer to choose whether the underlying functions are accessed through the binders or directly using inline assembly. This makes it easy for the programmer to eliminate extra overhead in especially time-critical or size-critical sections of code. Both access methods may be used with the same function in the same module without any degradation of performance. And binders are only linked into the final application on an as-needed basis. This gives the programmer complete freedom to choose the function interface which best suits the needs of the application at any time, without penalty.
The difference between programs written in C using a C interface and programs written with Spontaneous Assembly For C/C++ using inline assembly can be dramatic. On the average, a Spontaneous Assembly inline function is roughly half the size and twice the speed of its counterpart in the standard C library. The library is also highly granular, so program size is kept to an absolute minimum. The net result is that programs written using Spontaneous Assembly For C/C++ withinline assembly can be 50% to 90% smaller than equivalent applications written in C, and they are usually much faster.
{
char str1[] = "hello";
char str2[] = "hola";
_str_cmp(str2,str1);
}
would be translated into the following code (in the SMALL memory model) by the compiler:
{
char str1[] = "hello";
char str2[] = "hola";
lea ax,str1
push ax /* load parameter on stack */
lea ax,str2
push ax /* load parameter on stack */
call _str_cmp /* call function */
pop cx /* clean stack */
pop cx
}
However, if the same function is called with inline assembly, the resulting code is as follows:
{
char str1[] = "hello";
char str2[] = "hola";
lea si,str1 /* load SI register directly */
lea di,str2 /* load DI register directly */
push ss
pop es /* ES = SS */
str_cmp(); /* call function */
}
The complete interface for each inline assembly function is documented in the white Spontaneous Assembly manual which accompanies this package.
These rules must also be followed whenever inline assembly is used. For example, inline assembly code which modifies DS, SI, or DI must restore the appropriate registers when it is finished. The results are undefined if these rules are not followed in inline assembly code.
C Type Register char AL unsigned char AL int AX unsigned int AX long DX;AX unsigned long DX;AX near pointer AX far pointer DX:AXFor example, if the return type is a long, then the return value is returned in DX;AX, so any value in AX or DX would be overwritten by the return value.
1. far pointers should be loaded using the LES or LDS instructions. If LES is used, the segment of the pointer is placed in ES; if LDS is used, the segment is placed in DS.
2. near pointers should be loaded using the MOV instruction. The segment of the pointer is already in DS.
3. Addresses of global or static data should be loaded using MOV with OFFSET. The segment portion of the address is already in DS.
4. Addresses of local data must be loaded using LEA. The segment portion of the address is already in DS in small data models; it is in SS in large data models.
5. The segment portion of an address may be moved from one segment register to another by using PUSH followed by POP.
6. If DS, SI, or DI are modified, they MUST be restored before calling any function or finishing a particular block of inline assembly. This is usually done by using PUSH at the start of the block and POP at the end.
The following code illustrates various methods of declaring variables and the proper method of obtaining the variable address for each type of declaration:
/***** Rule 1 (far pointers) *****/
char far *global_ptr; /* global far pointer */
...
void func (char far *param_ptr) /* parameter far pointer */
{
char far *local_ptr; /* local far pointer */
...
lds si,global_ptr /* DS:SI = global_ptr pointer */
les di,local_ptr /* ES:DI = local_ptr pointer */
...
lds si,param_ptr /* DS:SI = param_ptr pointer */
...
}
/***** Rule 2 (near pointers) *****/
char near *global_ptr; /* global near pointer */
{
char near *local_ptr; /* local near pointer */
...
mov si,local_ptr /* SI = local_ptr pointer */
mov di,global_ptr /* DI = global_ptr pointer */
}
/***** Rule 3 (address of global or static) *****/
char global[10]; /* global array */
...
{
static char buffer[10]; /* static buffer */
...
mov di,offset global /* DI = offset of global */
mov si,offset buffer /* SI = offset of buffer */
...
}
/***** Rules 4 and 5 (address of local) *****/
{
char buffer[10];
...
#if __LARGE_DATA__
push ss
pop es /* ES = SS in large data */
#endif
lea di,buffer /* DI = offset of buffer */
...
}
/***** Rule 6 (preserve registers) *****/
{
char buf1[10];
char buf2[10];
...
push si /* preserve registers */
push di
#if __LARGE_DATA__
push ds
push ss
pop ds /* DS = SS in large data models */
#endif
lea si,buf1 /* DI = offset of buf2 */
lea di,buf2 /* DI = offset of buf2 */
...
#if __LARGE_DATA__
pop ds /* restore registers */
#endif
pop di
pop si
}
When using Microsoft C, the compiler may generate "Illegal size for operand" warnings when it encounters the LEA instruction. These warnings may be suppressed by specifying the size of the operand, as in this example:
lea di,word ptr variable_name
/******************************************************************/
/* STRSORT1.C - Sample C program */
/******************************************************************/
#include
#include
void cmpf(void); /* compare function prototype */
char str1[] = "Here is a string of characters in sorted order...";
main()
{
_put_str(str1); /* print the normal string */
_put_newline();
_quick_sort(str1, sizeof(str1) - 1,1,cmpf); /* sort the string */
_put_str(str1); /* print the sorted string */
}
void cmpf(void) /* compare two chars as defined by _quick_sort */
{
push ax /* preserve AX as required by _quick_sort */
mov al,[si]
cmp al,[di] /* compare the characters */
pop ax
}
STRSORT2 (C Program Using Inline Assembly)The second program (STRSORT2) is written in C, but it uses inline assembly to call the Spontaneous Assembly For C/C++ inline quick_sort function to perform the sorting.
/*******************************************************************************/
/* STRSORT2.C - Sample C program with inline assembly */
/* Uses the Spontaneous Assembly C/C++ inline quick_sort function */
/*******************************************************************************/
#include
#include
void cmpf(void); /* compare function prototype */
char str1[] = "Here is a string of characters in sorted order...";
int len_str1 = sizeof(str1) - 1;
void_main()
{
_put_str(str1); /* print the normal string */
_put_newline();
push di
push ds
pop es /* ES -> DGROUP */
mov di,offset str1 /* ES:DI -> the string to sort */
mov ax,offset cmpf /* AX -> the comparison function to use */
#if __LARGE_CODE__
mov dx,cs /* if large code, load segment address (see Assembly manual) */
#endif
mov cx,1 /* CX = the number of bytes per element */
mov bx,len_str1 /* BX = the number of array elements (characters) */
quick_sort(); /* perform the sort */
pop di
_put_str(str1); /* print the sorted string */
}
void cmpf(void) /* compare two chars as defined by quick_sort */
{
push ax /* preserve AX as required by quick_sort */
mov al,[si]
cmp al,[di] /* compare the characters */
pop ax
}
#include
void _memmov(const char *srcblk, const char far *destblk, unsigned num)
{
#if __SMALL_DATA__
mov si,srcblk /* SI = offset of srcblk */
#else
lds si,srcblk /* DS:SI -> srcblk */
#endif
les di,destblk /* ES:DI -> destblk */
mov cx,num /* CX = num */
push di
push si
mov si,ds
mov di,es
cmp di,si
pop si
pop di /* is DS = ES? */
je _memmov_020 /* y: do overlapping copy */
mem_cpy(); /* n: do regular copy */
ret
_memmov_020:
push cx
push di
push si /* save count and block pointers */
cmp di,si /* is dest addr <= src addr? */
jbe _memmov_030 /* y: copy forward from src to dest */
std /* n: copy backward from src to dest */
add di,cx
dec di /* point to end of dest block */
add si,cx
dec si /* point to end of src block */
_memmov_030:
rep movsb /* copy src to dest */
pop si
pop di
pop cx /* restore count and pointers */
cld /* ensure direction flag is clear (up) */
ret
}
Under copyright law, Spontaneous Assembly source and object code always remain the property of Acclaim Technologies, Inc. This is true even if the source code is edited and reassembled, regardless of the extent of the changes. This means that Spontaneous Assembly source code must always be accompanied by the copyright notice which appears at the top of the original source file, even if it is integrated into the source code of another application. The exact wording of the original copyright notice must be the same.
Although Spontaneous Assembly code may be distributed in EXECUTABLE format royalty-free (see the copyright declaration at the front of this manual for details and restrictions), SOURCE code may only be distributed with the prior written consent of Acclaim Technologies, Inc. Permission is readily granted to commercial developers who need to license or distribute source code with their applications. This permission can only be granted in writing as it requires the signing of an agreement. A nominal one-time fee may be charged. For details, contact PowerQuest/Base Two Development.