Archive

Posts Tagged ‘ASM’

Mixing it up with C and assembler with METALC..

July 27, 2013 4 comments

A few months ago I posted this sample C code to reverse a character string:

// Reverse a string

#include <iostream>
#include <string.h>

using namespace std;

char * reverse( char * p) {
    char * e;
    char * s;                // Copy ptr because I am going to mess with it
    int l = strlen(p);
    s=p;                     // first char ptr
    e = p+l-1;               // last char ptr

    char * m;                // mid point of the string
    m=s+l/2;                 // set it (int arith drops remainder if odd length

    for (;s<m;s++) {         // just need to traverse half the string
        s[0]=s[0]^e[0];      // swap curr leading and current last byte
        e[0]=e[0]^s[0];      // using xor
        s[0]=s[0]^e[0];
        e--;                 // back up 1 from end
        }
    return p;                // return same string back to caller

    }

int main()
{
   char c[21]="12345";             // String to reverse
   cout << reverse(c)<<'\n';       // reverse and print result
   cout << c;                      // orig string also reversed
   reverse(c);                     // reverse in place
   cout <<'\n'<<c;                 // show it is reversed

   return 0;
}

Pretty standard loop driven stuff and ‘adequate’ shall we say.

One of my ‘to do’ items this year is to go through the POP (principles of operation) manual and update my knowledge of a lot of the new instructions in there as it’s been forever since I last did that. Anyway, one of the instructions I came across was MVCIN or Move Inverse. This is a bit like MVC (Move Characters) except that it reverses the string as it moves it. Cool, except that we are in C and that is an assembler instruction and it’s a lot of extra work to write a sub routine you could call from a C program to do that.

But wait, there’s more (otherwise this would be a pointless post!)…

On the z/OS mainframe, the run time environment for C (and other programming languages like Cobol and PL/I) is normally provided by Language Environment (LE). BUT! If you use the C compiler option METALC, you can compile C code that has NO LE dependency. It is completely stand alone. Not only that but you can embed small snippets of ASSEMBLER code directly into your C source code and it compiles just like part of the regular program.

What this means is that I can replace that ugly for loop in the code above with a SINGLE instruction that will do the same thing, and do it far more efficiently than that loop will.

Now bear in mind I have not tried this code yet so it may need some tweaking but this is what I have so far:

// Reverse a string

#include <iostream>
#include <string.h>

using namespace std;

char * reverse( char * in) {

    int l = strlen(in);
    char * work;
    work = new char[l];         // get a temp work area 

    __asm (     
      "    MVCIN %0(%1),0(%2)      \n"  
      :"=m"(work) : r(l), "m"(in[l-1])                         
    );   

    memcpy(in,work,l);       // copy reversed string in work to orig input area
    delete [] work;          // release work area

    return in;               // return same string back to caller

}                                        

    }

int main()
{
   char c[21]="12345";             // String to reverse
   cout << reverse(c)<<'\n';       // reverse and print result
   cout << c;                      // orig string also reversed
   reverse(c);                     // reverse in place
   cout <<'\n'<<c;                 // show it is reversed

   return 0;
}

I have to allocate a work area to receive the reversed string since you cannot overlay the source with the output so the question becomes, is the cost (in CPU time) of allocating that work area and releasing it greater than the saving incurred by switching to a hardware instruction to do the reverse. You could however get around that to some extent by requiring the caller to provide the work area.

The only other downside is that this instruction is limited to 256 character strings so for anything longer you would have to implement some sort of loop but even so, the fact that you could reverse the string in 256 byte chunks must have some performance improvement implications.

In case you are wondering, the %0, %1 and %2 represent the positional parameters below the instruction. The fields after the first colon are output fields and the fields after the second colon are input parameters.

The parameter (“=m”(work) after the first colon tells the compiler this is an output field, it will be modified by the code and that it is a memory reference.

The two parameters (r(1) and “m”(in[l-1]) are input parameters. r(1) means this should be a register and the second memory reference (the source string) is a memory reference. It has to be the address of the LAST character of the source string, not the first, hence the [l-1] after the field name.

As soon as I get a chance I’ll try compiling this and seeing it it actually works. Look for another update soon!

Advertisements
Categories: Coding, Mainframe Tags: , ,