Add support for gcc v3.x compilers.
[seabios.git] / src / util.c
1 // Misc utility functions.
2 //
3 // Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "util.h" // call16
8 #include "bregs.h" // struct bregs
9 #include "farptr.h" // GET_FLATPTR
10 #include "biosvar.h" // get_ebda_seg
11
12 // Call a function with a specified register state.  Note that on
13 // return, the interrupt enable/disable flag may be altered.
14 inline void
15 call16(struct bregs *callregs)
16 {
17     asm volatile(
18 #if MODE16 == 1
19         "calll __call16\n"
20         "cli\n"
21         "cld"
22 #else
23         "calll __call16_from32"
24 #endif
25         : "+a" (callregs), "+m" (*callregs)
26         :
27         : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
28 }
29
30 inline void
31 call16big(struct bregs *callregs)
32 {
33     extern void __force_link_error__call16big_only_in_32bit_mode();
34     if (MODE16)
35         __force_link_error__call16big_only_in_32bit_mode();
36
37     asm volatile(
38         "calll __call16big_from32"
39         : "+a" (callregs), "+m" (*callregs)
40         :
41         : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
42 }
43
44 inline void
45 __call16_int(struct bregs *callregs, u16 offset)
46 {
47     if (MODE16)
48         callregs->cs = GET_SEG(CS);
49     else
50         callregs->cs = SEG_BIOS;
51     callregs->ip = offset;
52     call16(callregs);
53 }
54
55 // Switch to the extra stack in ebda and call a function.
56 inline u32
57 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
58 {
59     extern void __force_link_error__stack_hop_only_in_16bit_mode();
60     if (!MODE16)
61         __force_link_error__stack_hop_only_in_16bit_mode();
62
63     u16 ebda_seg = get_ebda_seg(), bkup_ss;
64     u32 bkup_esp;
65     asm volatile(
66         // Backup current %ss/%esp values.
67         "movw %%ss, %w3\n"
68         "movl %%esp, %4\n"
69         // Copy ebda seg to %ds/%ss and set %esp
70         "movw %w6, %%ds\n"
71         "movw %w6, %%ss\n"
72         "movl %5, %%esp\n"
73         // Call func
74         "calll %7\n"
75         // Restore segments and stack
76         "movw %w3, %%ds\n"
77         "movw %w3, %%ss\n"
78         "movl %4, %%esp"
79         : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
80         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
81         : "cc", "memory");
82     return eax;
83 }
84
85 // Sum the bytes in the specified area.
86 u8
87 checksum_far(u16 buf_seg, void *buf_far, u32 len)
88 {
89     SET_SEG(ES, buf_seg);
90     u32 i;
91     u8 sum = 0;
92     for (i=0; i<len; i++)
93         sum += GET_VAR(ES, ((u8*)buf_far)[i]);
94     return sum;
95 }
96
97 u8
98 checksum(void *buf, u32 len)
99 {
100     return checksum_far(GET_SEG(SS), buf, len);
101 }
102
103 size_t
104 strlen(const char *s)
105 {
106     if (__builtin_constant_p(s))
107         return __builtin_strlen(s);
108     const char *p = s;
109     while (*p)
110         p++;
111     return p-s;
112 }
113
114 // Compare two areas of memory.
115 int
116 memcmp(const void *s1, const void *s2, size_t n)
117 {
118     while (n) {
119         if (*(u8*)s1 != *(u8*)s2)
120             return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
121         s1++;
122         s2++;
123         n--;
124     }
125     return 0;
126 }
127
128 // Compare two strings.
129 int
130 strcmp(const char *s1, const char *s2)
131 {
132     for (;;) {
133         if (*s1 != *s2)
134             return *s1 < *s2 ? -1 : 1;
135         if (! *s1)
136             return 0;
137         s1++;
138         s2++;
139     }
140 }
141
142 inline void
143 memset_far(u16 d_seg, void *d_far, u8 c, size_t len)
144 {
145     SET_SEG(ES, d_seg);
146     asm volatile(
147         "rep stosb %%es:(%%di)"
148         : "+c"(len), "+D"(d_far)
149         : "a"(c)
150         : "cc", "memory");
151 }
152
153 inline void
154 memset16_far(u16 d_seg, void *d_far, u16 c, size_t len)
155 {
156     len /= 2;
157     SET_SEG(ES, d_seg);
158     asm volatile(
159         "rep stosw %%es:(%%di)"
160         : "+c"(len), "+D"(d_far)
161         : "a"(c)
162         : "cc", "memory");
163 }
164
165 void *
166 memset(void *s, int c, size_t n)
167 {
168     while (n)
169         ((char *)s)[--n] = c;
170     return s;
171 }
172
173 inline void
174 memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
175 {
176     SET_SEG(ES, d_seg);
177     u16 bkup_ds;
178     asm volatile(
179         "movw %%ds, %w0\n"
180         "movw %w4, %%ds\n"
181         "rep movsb (%%si),%%es:(%%di)\n"
182         "movw %w0, %%ds"
183         : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
184         : "r"(s_seg)
185         : "cc", "memory");
186 }
187
188 void *
189 #undef memcpy
190 memcpy(void *d1, const void *s1, size_t len)
191 #define memcpy __builtin_memcpy
192 {
193     void *d = d1;
194     if (((u32)d1 | (u32)s1 | len) & 3) {
195         // non-aligned memcpy
196         asm volatile(
197             "rep movsb (%%esi),%%es:(%%edi)"
198             : "+c"(len), "+S"(s1), "+D"(d)
199             : : "cc", "memory");
200         return d1;
201     }
202     // Common case - use 4-byte copy
203     len /= 4;
204     asm volatile(
205         "rep movsl (%%esi),%%es:(%%edi)"
206         : "+c"(len), "+S"(s1), "+D"(d)
207         : : "cc", "memory");
208     return d1;
209 }
210
211 void *
212 memmove(void *d, const void *s, size_t len)
213 {
214     if (s >= d)
215         return memcpy(d, s, len);
216
217     d += len-1;
218     s += len-1;
219     while (len--) {
220         *(char*)d = *(char*)s;
221         d--;
222         s--;
223     }
224
225     return d;
226 }
227
228 // Copy a string - truncating it if necessary.
229 char *
230 strtcpy(char *dest, const char *src, size_t len)
231 {
232     char *d = dest;
233     while (len-- && *src != '\0')
234         *d++ = *src++;
235     *d = '\0';
236     return dest;
237 }
238
239 // Wait for 'usec' microseconds with irqs enabled.
240 void
241 usleep(u32 usec)
242 {
243     struct bregs br;
244     memset(&br, 0, sizeof(br));
245     br.ah = 0x86;
246     br.cx = usec >> 16;
247     br.dx = usec;
248     call16_int(0x15, &br);
249 }
250
251 // See if a keystroke is pending in the keyboard buffer.
252 static int
253 check_for_keystroke()
254 {
255     struct bregs br;
256     memset(&br, 0, sizeof(br));
257     br.ah = 1;
258     call16_int(0x16, &br);
259     return !(br.flags & F_ZF);
260 }
261
262 // Return a keystroke - waiting forever if necessary.
263 static int
264 get_raw_keystroke()
265 {
266     struct bregs br;
267     memset(&br, 0, sizeof(br));
268     call16_int(0x16, &br);
269     return br.ah;
270 }
271
272 // Read a keystroke - waiting up to 'msec' milliseconds.
273 int
274 get_keystroke(int msec)
275 {
276     for (;;) {
277         if (check_for_keystroke())
278             return get_raw_keystroke();
279         if (msec <= 0)
280             return -1;
281         usleep(50*1000);
282         msec -= 50;
283     }
284 }