blob: 3c7117c4f7213ece58523bdfd306b5a023f565e7 [file] [log] [blame]
Kevin O'Connor9521e262008-07-04 13:04:29 -04001// Misc utility functions.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05005// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor9521e262008-07-04 13:04:29 -04006
Kevin O'Connor273e8ae2009-01-19 19:56:07 -05007#include "util.h" // call16
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "bregs.h" // struct bregs
Kevin O'Connor35ae7262009-01-19 15:44:44 -05009#include "farptr.h" // GET_FLATPTR
Kevin O'Connor7f343092009-01-01 18:31:11 -050010#include "biosvar.h" // get_ebda_seg
Kevin O'Connor9521e262008-07-04 13:04:29 -040011
12// Call a function with a specified register state. Note that on
13// return, the interrupt enable/disable flag may be altered.
14inline void
15call16(struct bregs *callregs)
16{
17 asm volatile(
Kevin O'Connor7ab798f2008-07-13 11:08:36 -040018#if MODE16 == 1
Kevin O'Connor9521e262008-07-04 13:04:29 -040019 "calll __call16\n"
Kevin O'Connorf094ba82009-04-13 19:30:27 -040020 "cli\n"
21 "cld\n"
Kevin O'Connor9521e262008-07-04 13:04:29 -040022#else
23 "calll __call16_from32\n"
24#endif
25 : "+a" (callregs), "+m" (*callregs)
26 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050027 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor9521e262008-07-04 13:04:29 -040028}
29
30inline void
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050031call16big(struct bregs *callregs)
32{
Kevin O'Connor7f343092009-01-01 18:31:11 -050033 extern void __force_link_error__call16big_only_in_32bit_mode();
34 if (MODE16)
35 __force_link_error__call16big_only_in_32bit_mode();
36
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050037 asm volatile(
38 "calll __call16big_from32\n"
39 : "+a" (callregs), "+m" (*callregs)
40 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050041 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050042}
43
44inline void
Kevin O'Connor9521e262008-07-04 13:04:29 -040045__call16_int(struct bregs *callregs, u16 offset)
46{
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050047 if (MODE16)
48 callregs->cs = GET_SEG(CS);
49 else
50 callregs->cs = SEG_BIOS;
Kevin O'Connor9521e262008-07-04 13:04:29 -040051 callregs->ip = offset;
52 call16(callregs);
53}
Kevin O'Connora4d35762008-03-08 15:43:03 -050054
Kevin O'Connora83ff552009-01-01 21:00:59 -050055inline void
56call16_simpint(int nr, u32 *eax, u32 *flags)
57{
58 extern void __force_link_error__call16_simpint_only_in_16bit_mode();
59 if (!MODE16)
60 __force_link_error__call16_simpint_only_in_16bit_mode();
61
62 asm volatile(
63 "stc\n"
64 "int %2\n"
65 "pushfl\n"
66 "popl %1\n"
Kevin O'Connora83ff552009-01-01 21:00:59 -050067 "cli\n"
Kevin O'Connor4ebc0b72009-03-01 12:31:57 -050068 "cld\n"
Kevin O'Connora83ff552009-01-01 21:00:59 -050069 : "+a"(*eax), "=r"(*flags)
70 : "i"(nr)
71 : "cc", "memory");
72}
73
Kevin O'Connor7f343092009-01-01 18:31:11 -050074// Switch to the extra stack in ebda and call a function.
75inline u32
76stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
77{
78 extern void __force_link_error__stack_hop_only_in_16bit_mode();
79 if (!MODE16)
80 __force_link_error__stack_hop_only_in_16bit_mode();
81
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050082 u16 ebda_seg = get_ebda_seg(), bkup_ss;
83 u32 bkup_esp;
Kevin O'Connor7f343092009-01-01 18:31:11 -050084 asm volatile(
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050085 // Backup current %ss/%esp values.
86 "movw %%ss, %w3\n"
87 "movl %%esp, %4\n"
88 // Copy ebda seg to %ds/%ss and set %esp
89 "movw %w6, %%ds\n"
90 "movw %w6, %%ss\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050091 "movl %5, %%esp\n"
92 // Call func
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050093 "calll %7\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050094 // Restore segments and stack
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050095 "movw %w3, %%ds\n"
96 "movw %w3, %%ss\n"
97 "movl %4, %%esp\n"
98 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
99 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
Kevin O'Connor7f343092009-01-01 18:31:11 -0500100 : "cc", "memory");
101 return eax;
102}
103
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400104// Sum the bytes in the specified area.
105u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500106checksum_far(u16 buf_seg, void *buf_far, u32 len)
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400107{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500108 SET_SEG(ES, buf_seg);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400109 u32 i;
110 u8 sum = 0;
111 for (i=0; i<len; i++)
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500112 sum += GET_VAR(ES, ((u8*)buf_far)[i]);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400113 return sum;
114}
115
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500116u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500117checksum(void *buf, u32 len)
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500118{
119 return checksum_far(GET_SEG(SS), buf, len);
120}
121
Kevin O'Connor67823442009-04-13 14:14:51 -0400122size_t
123strlen(const char *s)
124{
125 if (__builtin_constant_p(s))
126 return __builtin_strlen(s);
127 const char *p = s;
128 while (*p)
129 p++;
130 return p-s;
131}
132
133// Compare two areas of memory.
134int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400135memcmp(const void *s1, const void *s2, size_t n)
Kevin O'Connor67823442009-04-13 14:14:51 -0400136{
137 while (n) {
138 if (*(u8*)s1 != *(u8*)s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400139 return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
Kevin O'Connor67823442009-04-13 14:14:51 -0400140 s1++;
141 s2++;
142 n--;
143 }
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400144 return 0;
Kevin O'Connor67823442009-04-13 14:14:51 -0400145}
146
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400147// Compare two strings.
148int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400149strcmp(const char *s1, const char *s2)
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400150{
151 for (;;) {
152 if (*s1 != *s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400153 return *s1 < *s2 ? -1 : 1;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400154 if (! *s1)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400155 return 0;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400156 s1++;
157 s2++;
158 }
159}
160
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400161void *
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400162memset(void *s, int c, size_t n)
163{
164 while (n)
165 ((char *)s)[--n] = c;
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400166 return s;
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400167}
168
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500169inline void
170memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400171{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500172 SET_SEG(ES, d_seg);
173 u16 bkup_ds;
174 asm volatile(
175 "movw %%ds, %w0\n"
176 "movw %w4, %%ds\n"
177 "rep movsb (%%si),%%es:(%%di)\n"
178 "movw %w0, %%ds\n"
179 : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
180 : "r"(s_seg)
181 : "cc", "memory");
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400182}
183
Kevin O'Connord9441142009-04-19 23:18:54 -0400184// Memcpy that uses 4-byte accesses
185void
186memcpy4(void *d1, const void *s1, size_t len)
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400187{
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400188 len /= 4;
189 asm volatile(
190 "rep movsl (%%esi),%%es:(%%edi)\n"
Kevin O'Connord9441142009-04-19 23:18:54 -0400191 : "+c"(len), "+S"(s1), "+D"(d1)
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400192 : : "cc", "memory");
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400193}
194
195void *
Kevin O'Connorc7812932008-06-08 23:08:12 -0400196memmove(void *d, const void *s, size_t len)
197{
198 if (s >= d)
199 return memcpy(d, s, len);
200
201 d += len-1;
202 s += len-1;
203 while (len--) {
204 *(char*)d = *(char*)s;
205 d--;
206 s--;
207 }
208
209 return d;
210}
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500211
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500212// Copy a string - truncating it if necessary.
213char *
214strtcpy(char *dest, const char *src, size_t len)
215{
216 char *d = dest;
217 while (len-- && *src != '\0')
218 *d++ = *src++;
219 *d = '\0';
220 return dest;
221}
222
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500223// Wait for 'usec' microseconds with irqs enabled.
Kevin O'Connor5ca4b952009-02-15 13:45:48 -0500224void
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500225usleep(u32 usec)
226{
227 struct bregs br;
228 memset(&br, 0, sizeof(br));
229 br.ah = 0x86;
230 br.cx = usec >> 16;
231 br.dx = usec;
232 call16_int(0x15, &br);
233}
234
235// See if a keystroke is pending in the keyboard buffer.
236static int
237check_for_keystroke()
238{
239 struct bregs br;
240 memset(&br, 0, sizeof(br));
241 br.ah = 1;
242 call16_int(0x16, &br);
243 return !(br.flags & F_ZF);
244}
245
246// Return a keystroke - waiting forever if necessary.
247static int
248get_raw_keystroke()
249{
250 struct bregs br;
251 memset(&br, 0, sizeof(br));
252 call16_int(0x16, &br);
253 return br.ah;
254}
255
256// Read a keystroke - waiting up to 'msec' milliseconds.
257int
258get_keystroke(int msec)
259{
260 for (;;) {
261 if (check_for_keystroke())
262 return get_raw_keystroke();
263 if (msec <= 0)
264 return -1;
265 usleep(50*1000);
266 msec -= 50;
267 }
268}