mshort

The version 0.55 will probably be the last MiNTLib that contains libraries compiled with -mshort, i.e. with libraries where an int is only 16 Bits wide.
There is a simple reason for that: It is a very time-consuming and frustrating job to design the library in a way that allows it to be used both with 16 bit and 32 bit ints and I neither have the time nor the interest to do that. The 16 bit libraries in this release are merely for information, they are known to be buggy and besides it is impossible to build a 16 bit library in a compatible way. For example read() and write() are supposed to return the number of bytes written as int but you probably don't want a library that can read/write at most 32767 bytes at once.

If somebody volunteers to maintain the 16 bit support for the library, go ahead. But don't expect that job to be done by the current maintainer who has only limited time and no ambitions to waste time for supporting a ridiculous feature.

Unfortunately, our OS dates from a time where it was already short-sighted but not really ridiculous to use an int type with 16 bits. The libc with 16 bit ints was mostly used in cases where an interface with either the OS or code that was generated by historic compilers was required. It is not always sufficient to replace all occurencies of "int" or "unsigned" with "long int" or "unsigned long int" in these cases.

For example there is software that can be extended by plug-ins, modules, divers, or overerlays, whatever it is called. The problems begin when this software defines an API and this API uses pointers to functions. Exchanging data in structures is no problem. If one piece of software exchanges data in a structure which looks approximately like this:

    struct zodiac {
        int magic;  /* Magic number.  */
        void* data; /* Pointer to data.  */
    };

If you know that the creator of that software understood "int" as a 16 bit type, you simply write in your own code:

    struct zodiac {
        short int magic;
        void* data;
    };

and your module and the other software understand each other perfectly alright.

But what if that software (I will call that the server now) performs tasks for your module (from now on the client) by means of a function that you can get the address of:

    int initialize_server (int magic, void* data);

If you know that the author of the software presumed a compiler that uses 16 bit ints you have problems now. In a naive approach you could prototype that interface in your own code as:

    short int initialize_server (short int magic, void* data);

By convention all parameters are passed on the stack and the return value of a function is returned in register D0. However, if you look at the assembler output of your compiler you will notice a difference, depending on whether int has 16 or 32 bits. ANSI C requires (I was told so and I believe it) that all integral types that are narrower than int are automatically promoted to int resp. unsigned int when passed as function arguments. Anyway, whether ANSI C requires that or not, gcc does it exactly that way. It will push both parameters MAGIC and DATA as 32 bit types on the stack. The flag -mshort changes that behavior: In that case for parameter MAGIC only 16 bits will be pushed.

Back in the old days when there was a 16 bit int libc and you still compiled with -mshort you simply coded:

    short int my_magic = 0xbeef;
    void* my_data = pointer_to_my_data;
    extern short int initialize_server (short int magic, void* data);
    short int retval = initialize_server (my_magic, my_data);

But this lousy MiNTLib maintainer is too lazy to keep up support for a libc with 16 bit ints. So what are we going to do now? There is a little trick that will do that job:

    struct init_args {
        short int magic;
        void* data;
    } my_args = { 0xbeef, pointer_to_my_data };
    extern short int initialize_server (struct init_args);
    short int retval = initialize_server (my_args);

We hide the arguments in a C structure. If a function takes a C structure (NOT a pointer to a structure!) as an argument, the contents of this structure will be pushed "as is" on the stack. This is at least valid for gcc and probably also the case for future C compilers and it is very unlikely that this will ever change. By simply rewriting the function prototype you can therefore gain full control of the stack layout when the external function is called. This is a very simple way to emulate the calling conventions of a 16 bit compiler.

The only thing that you have to bear in mind is that a 16 bit compiler will promote arguments of type "char" or "unsigned char" to "short int" or "unsigned short int" respectively. You don't have to care about arguments that are larger than a short int (for example float, double, long double, long long, structures, pointers, ...). You only have to hide "char" and "short" in the structure. You will encounter no problems with the return value (if you do, type-cast it).

Caveat: The "struct trick" won't work with variadic functions but I doubt that this will ever lead to problems.

What about performance? By looking at the code in C one might think that the above described kludge would result in a considerable performance penalty. In fact that effect is neglectible respectively arbitrary. In some cases the new code will be slightly slower, in other cases it will be slightly faster. In any case: The MiNTLib is of course optimized for 32 bit ints and that makes sense because 99 % of the software that is linked against the MiNTLib uses 32 bit ints. It is very unlikely that a performance penalty caused by a possible overhead in the interface to external software will not be compensated by a performance gain in the libc itself. In the end I would guess that the effect is not measurable.

One effect is however measurable: Rewriting old code that was traditionally compiled and linked with -mshort will take some time. But ... this has only to be done once. On the other hand, a lot of code in the MiNTLib origins from external sources and the authors of that external code would most probably show me the finger if I asked them to keep their code 16 bit clean (in the year 2000 it is much more an issue to make code 64 bit clean). Each time this external code gets updated I have to waste my time skimming through the code and eliminating all code that makes the (perfectly legal) assumption that an int can hold at least 32 bits. This useless job is a major nuisance and I'm not willing to do that any longer. If you think you cannot live without a libc with 16 bit ints feel free to complain but don't forget to name the person who will take care of the problems.

Guido Flohr guido@freemint.de


Table of contents