Assembler with LCC-Win32

How to call another subroutine from a function by push-ing arguments onto the stack before making the call. 

The routine copies a substring into the main string if there is enough room before the null byte of the main string and then returns zero, otherwise it aborts the function and returns 1.

Before calling a routine from within a function one pushes a required value onto the stack (if it needs one). This is used by the called routine - is this case the address of the string. By pushing a value onto the stack one has moved all previous stack values down one. So after returning from the subroutine one would normally pop to re-establish the previous stack state. However by using RET num in the called subroutine the pop-ing is done automatically. One can pop a maximum of 16 bytes using this method. As we only push one long in this routine we use _asm("ret $4"); to pop 4 bytes.

When calling an asm routine from C do not use RET num unless you have declared the asm routine __stdcall. This tells the compiler that the function will clean up the stack. Without _stdcall the compiler will generate code to clean up the stack too, so it will be cleaned twice and will result in a crash.

Example.

size_t __declspec(naked) __stdcall str_len(char * s)

StrInsert

#include <stdio.h>

size_t __declspec(naked) str_len(char * s)
{
    _asm("movl 4(%esp),%ecx");   // first arg is at 4(%esp)
    _asm("jecxz exit1");         // check for '0' NULL in ecx
    _asm("movl $-1,%eax");       // -1 to start
_asm("L1:");
    _asm("inc %eax");
    _asm("cmpb $0,(%ecx,%eax)"); // compares '0' with the byte pointed to
                                 // by ecx with an offfset of eax.
    _asm("jne L1");              // not finished yet?
_asm("exit1:");
    _asm("ret $4");              // ret in eax (pop 4 bytes before returning)
}

int __declspec(naked) str_insert(char * str, char * substr, int pos)
{
    _asm ("pushl %esi");
    _asm ("pushl %edi");
    _asm ("movl 12(%esp), %edi"); // address of str
    _asm ("movl 16(%esp), %esi"); // address of substr
    _asm ("movl 20(%esp), %edx"); // start pos

    _asm("pushl %edi");           // push address of main string
    _asm("call _str_len");        // find length of string, result will be in eax
    _asm("movl %eax,%edx");       // save length of main string
    _asm("pushl %esi");           // push address of sub string
    _asm("call _str_len");        // find length of substring, result will be in eax
    _asm("movl %eax,%ecx");       // save length of substring
    _asm("addl 20(%esp),%eax");   // position + length of substring
    _asm("cmpl %edx,%eax");       // is pos + sublen to big?
    _asm("jg error");

    _asm("movl %ecx,%edx");       // save substring length again
    _asm("addl 20(%esp),%edi");   // start address + pos
    _asm ("cld");                 // inc for string instructions (edi)
    _asm ("shr $2, %ecx");        // divide sublen for dwords
    _asm ("rep");
    _asm ("movsl");               // copy dwords

    _asm ("movl %edx, %ecx");     // size of sub string in ECX
    _asm ("andl $3, %ecx");       // divide for bytes
    _asm ("rep");
    _asm ("movsb");               // copy bytes
    _asm("jmp noerr");

_asm("error:");
    _asm("movl $1,%eax");         // return 1
    _asm("jmp exit");

_asm("noerr:");
    _asm("xorl %eax,%eax");       // return 0

_asm("exit:");
    _asm ("popl %edi");
    _asm ("popl %esi");
    _asm("ret");                  // result is in eax
}

int main(void)
{
    char s[] = "The way was tough on all but the most hardy";
    char s1[] = "tardy";
    printf(" %s\n", s);
    int ret = str_insert(s, s1, 12);
    printf("%s: %s\n", ret ? "Failed" : "Success",s);
    return 0;
}

Back to main page