Welcome, Guest. Please login or register.

Author Topic: Writing a shared library - ASM Help  (Read 2979 times)

Description:

0 Members and 1 Guest are viewing this topic.

Offline nyteschaydeTopic starter

  • VIP / Donor - Lifetime Member
  • Hero Member
  • *****
  • Join Date: Mar 2002
  • Posts: 643
    • Show only replies by nyteschayde
    • http://www.nyteshade.com
Writing a shared library - ASM Help
« on: January 31, 2017, 01:33:33 AM »
So I have been following this guide to create a shared library in C for the Amiga. So far the default example works fine. The questions I have, as I want to adopt my code for the library, are these:

  • How do the registers work? What's the difference between A0/A1/D0 etc...?
  • What happens if I have more than one argument? Which registers do I use?
  • What about var args?
  • Does anybody know how this would differ if done in GCC? Can I, do I have to, link the SAS/C libinit.o/libinitr.o files?


I want to not only post the library I am building, but I want to be able to quickly reuse it for my own applications as I write them. Anybody else do this? Any fun stuff to share?
Senior MTS Software Engineer with PayPal
Amigas: A1200T 060/603e PPC • A1200T 060 • A4000D 040 • A3000 (x2) • A2000 Vamp/V2 • A1200 (x4) • A1000 (x3) • A600 Vamp/V1 • A500
 

Offline nyteschaydeTopic starter

  • VIP / Donor - Lifetime Member
  • Hero Member
  • *****
  • Join Date: Mar 2002
  • Posts: 643
    • Show only replies by nyteschayde
    • http://www.nyteshade.com
Re: Writing a shared library - ASM Help
« Reply #1 on: January 31, 2017, 08:38:26 AM »
So it seems that A[0-7] is for addresses and D[0-7?] is for data? I am still not quite sure which parameters to associate with which registers. I am guessing that anything that is a not a pointer would get D register and all pointers an A register? However, that is purely a guess.
Senior MTS Software Engineer with PayPal
Amigas: A1200T 060/603e PPC • A1200T 060 • A4000D 040 • A3000 (x2) • A2000 Vamp/V2 • A1200 (x4) • A1000 (x3) • A600 Vamp/V1 • A500
 

Offline nyteschaydeTopic starter

  • VIP / Donor - Lifetime Member
  • Hero Member
  • *****
  • Join Date: Mar 2002
  • Posts: 643
    • Show only replies by nyteschayde
    • http://www.nyteshade.com
Re: Writing a shared library - ASM Help
« Reply #2 on: February 16, 2017, 06:09:54 AM »
Answering my previous question, any argument that is a pointer to something goes in an __a register and anything that is a direct value goes in a __d register. It appears there are roughly 7 usable pointer registers and perhaps 7-8 direct value registers per function definition.

Code: [Select]
void __saveds __asm someFunction(
  register __a0 APTR ptr1,
  register __a0 APTR ptr1,
  register __a1 APTR ptr2,
  register __a2 APTR ptr3,
  register __a3 APTR ptr4,
  register __a4 APTR ptr5,
  register __a5 APTR ptr6,
  register __a6 APTR ptr7,
/*register __a7 APTR ptr8,      Seems this one is special */
  register __d0 BPTR val1,
  register __d1 BPTR val2,
  register __d2 BPTR val3,
  register __d3 BPTR val4,
  register __d4 BPTR val5,
  register __d5 BPTR val6,
  register __d6 BPTR val7,
  register __d7 BPTR val8
);

Alright folks, I have this all working fairly well. I have now run into another issue that I am not sure how to fix and I cannot, as of yet, find any docs around. When making the shared library you typically have to tag and define the functions that the library will use with register designations. But I am not sure what to do for varargs style functions

Code: [Select]
/* Typical Example */

struct Person {
  STRPTR name;
  SHORT age;
};

int __saveds __asm getAge(register __a0 struct Person *p) {
  return p->age;
}

/** Varargs?! */

int * __saveds __asm getAges(register __a0 struct Person *first, ...) {
  /* Loop through and collect the ages and return as int array */
}


Does the ... need to be tagged with a register?
Senior MTS Software Engineer with PayPal
Amigas: A1200T 060/603e PPC • A1200T 060 • A4000D 040 • A3000 (x2) • A2000 Vamp/V2 • A1200 (x4) • A1000 (x3) • A600 Vamp/V1 • A500
 

Offline Thomas

Re: Writing a shared library - ASM Help
« Reply #3 on: February 16, 2017, 08:25:23 AM »
You cannot have varargs functions in a shared library.

What you do is to define a function which takes a pointer to a tag list and provide a varargs stub in a static link library which calls the shared library function.

in your shared library:

Code: [Select]
ULONG myFunctionTagList (__A0 APTR some_argument, __A1 struct TagItem *arg_taglist)

{
struct TagItem *temp_taglist = arg_taglist;
struct TagItem *ti;

while (ti = NextTagItem (&temp_taglist))
   {
   ...
   }

return (result);
}



in your static link library (or in the user's program):

Code: [Select]
void myFunctionTags (APTR some_argument, Tag tag1, ...)
{
return (myFunctionTagList (some_argument, (struct TagItem *)&tag1));
}



If you carefully check the include files for OS libraries, you'll see that they work the same: the library itself only contains TagList functions while all the varargs functions are in amiga.lib.

Offline Thomas

Re: Writing a shared library - ASM Help
« Reply #4 on: February 16, 2017, 02:20:25 PM »
Regarding registers:

A7 is the stack pointer (also referred to as SP), it cannot be used for anything else.
A6 is your library base pointer. You may use/change it in your routine if you don't need the library base, but it cannot be used for arguments.
A5 is used by the compiler as base for the local variables. You might get the compiler into deep trouble if you use it for function arguments. At least it adds some overhead.
A4 is used by the compiler as base for the global variables when the small data model is used. Same trouble as above. No issue with large data model.

D0, D1, A0 and A1 are scratch-registers. You may use/change them in your routine and don't need to preserve their values. With the exception of D0 which is used as return value. If your routine shall return something, move it into D0.

All other registers  must be preserved. This does not mean you may not use them, you only have to restore their values before you leave your routine.

Finally a word about the "register" attribute. The way you use it creates unnecessary overhead.

The register attribute tells the compiler to reserve a register for that variable. In case of a function argument which already comes in a register, this means the compiler reserves an additional register for the variable and moves the argument register into the reserved register.

For example if you declare a function argument as register __D0, the compiler will reserve a free register, for example D2 and move D0 into it.

To avoid this overhead (and excessive register usage) I suggest to omit the word register and only use __Dx or __Ax.

guest11527

  • Guest
Re: Writing a shared library - ASM Help
« Reply #5 on: February 17, 2017, 10:16:22 PM »
Quote from: Thomas;822263
You cannot have varargs functions in a shared library.

Well, yes, but you can provide an .fd file which will for all practical means works like a varargig function (is this a proper word?)

As Thomas already said (the other Thomas, not me), you write a function which accepts as last argument a tag list. Then, for the library, you create an ".fd" file which describes all your call-ins (LVOs, to be precise). The particular function name should have a "TagList" as last part of the function, e.g.

myFuncTagList(args,tags)(a0,a1)

Then, to create prototypes and pragmas for your compiler, get "FD2Pragma" from aminet. This is a tiny code generator which creates both prototypes and pragmas for a given fd file (see the guide for how to run it).

In particular, for calls like the above, it will create *two* pragmas and *two* prototypes: One for a function taking a pointer to a tag list as second argument, and another vararg function which takes a list of tags as last arguments.

For the SAS/C, this is even more convenient as the compiler takes care of converting the argument list to a pointer during the function call, so the overhead is minimal (or even zero, depending on your point of view).

Essentially, SAS/C knows two types of library function calls:
#pragma libcall
which is the classical "put arguments in registers then call the library" and
#pragma tagcall
which is a "put all initial arguments in registers, but put all last arguments on the stack and then call the function with the argument of the list on the stack".

The nice part is that it does not take any sort of "stub" function, i.e. there is no need to write a function like the one Thomas suggested because, essentially, the compiler does it for you, and even inlines it. (With a grain of salt).

I do not know how other compilers handle this, though, and whether they offer a similar mechanism.