-
Notifications
You must be signed in to change notification settings - Fork 11
/
README.mshort
136 lines (114 loc) · 6.58 KB
/
README.mshort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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>