source: trunk/src/os2ahci/libc.c@ 112

Last change on this file since 112 was 112, checked in by Markus Thielen, 14 years ago
  • removed RAS calls (tracing to OS/2 kernel trace buffer was unreliable)
  • added private trace ring buffer implementation
  • support read from OS2AHCI$ character device
  • contents of trace ring buffer are accesible via OS2AHCI$ character device
  • updated WATCOM makefile; WATCOM build still produces a non-working driver
  • code cleanup (unused variables etc.)
File size: 24.2 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28
29/* -------------------------- macros and constants ------------------------- */
30
31#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
32 * for printing custom messages via
33 * DevHelp_Save_Message() */
34
35/* heap management constants */
36#define HEAP_SIZE 8192
37#define HEAP_UNIT 128
38#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
39
40/* ------------------------ typedefs and structures ------------------------ */
41
42/* mdelay() calibration status */
43typedef enum {
44 MD_NOT_CALIBRATED, /* delay loop not calibrated */
45 MD_CALIBRATION_START, /* calibration run started */
46 MD_CALIBRATION_END, /* calibration run ended */
47 MD_CALIBRATION_DONE /* calibration complete */
48} MDCAL;
49
50/* -------------------------- function prototypes -------------------------- */
51
52static void long_to_asc (long val,
53 char _far *buf,
54 int base,
55 int zero, int flen);
56static void _cdecl _far mdelay_timer_callback (ULONG timer_handle,
57 ULONG parm1,
58 ULONG parm2);
59static int mdelay_cal_end (void);
60
61/* ------------------------ global/static variables ------------------------ */
62
63/* debug COM port base address */
64u16 com_base = 0x03f8;
65
66static char hex_digits[] = "0123456789abcdef";
67static ULONG mem_lock;
68ULONG com_lock;
69
70/* message table for DosHelp_Save_Message() which prints the first string */
71static MSGTABLE init_msgtbl = {
72 MSG_REPLACEMENT_STRING,
73 1,
74 0
75};
76
77/* COM port initialization sequence */
78static struct {
79 int reg;
80 u8 data;
81} com_init_sequence[] = {
82 3, 0x80, /* overlay divisor latch register at 0x3f8 and 0x3f9 */
83 0, 0x01, /* set low byte of divisor to 1 (115200 baud) */
84 1, 0x00, /* set high byte of divisor to 0 */
85 3, 0x03, /* reset divisor latch register overlay and set 8,n,1 */
86 1, 0x00, /* disable interrupts */
87 4, 0x0f, /* modem control register */
88 -1, 0x00
89};
90
91/* delay loop calibration data */
92volatile MDCAL mdelay_cal_status = 0; /* delay loop calibration status */
93volatile u32 mdelay_loops_per_ms = 0; /* delay loop counter */
94
95/* very small heap for dynamic memory management */
96static u8 heap_buf[HEAP_SIZE];
97static u8 heap_units[HEAP_UNIT_CNT];
98static ULONG heap_phys_addr;
99
100/* global info segment */
101volatile PGINFOSEG gis;
102
103
104/* ----------------------------- start of code ----------------------------- */
105
106/******************************************************************************
107 * Initialize libc components
108 */
109void init_libc(void)
110{
111 PSEL p;
112
113 DevHelp_CreateSpinLock(&mem_lock);
114 DevHelp_CreateSpinLock(&com_lock);
115
116 DevHelp_VirtToPhys(heap_buf, &heap_phys_addr);
117
118 /* get global info segment */
119 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p) == 0) {
120 gis = (PGINFOSEG) ((u32) *p << 16);
121 }
122
123}
124
125/******************************************************************************
126 * Initialize COM port to 115200,n,8,1
127 *
128 * NOTE: Something is wrong with this code, or the init sequence, but we never
129 * got around to fixing it because it works fine on Virtualbox, and on
130 * physical machines we tend to have the kernel debugger running on the
131 * same port, thus KDB will set port parameters for us. This is going
132 * to be fixed eventually...
133 */
134void init_com(void)
135{
136 int i;
137
138 spin_lock(com_lock);
139
140 for (i = 0; com_init_sequence[i].reg != -1; i++) {
141 u16 port = com_base + com_init_sequence[i].reg;
142 u8 data = com_init_sequence[i].data;
143 _asm {
144 mov dx, port;
145 mov al, data;
146 out dx, al;
147 }
148 }
149
150 spin_unlock(com_lock);
151}
152
153/******************************************************************************
154 * Print a formatted message into a string buffer. This is very basic,
155 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
156 * and hex (x)). Formatting length modifiers are only supported with a single
157 * digit -- 32-bit numbers don't need more than 9 characters -- and an
158 * optional '0' in front.
159 */
160int vsprintf(char _far *buf, const char *fmt, va_list va)
161{
162 char _far *orig = buf;
163 char _far *s;
164 int lmod;
165 int fptr;
166 int zero;
167 int flen;
168
169 for (; *fmt != '\0'; fmt++) {
170 switch (*fmt) {
171
172 case '%':
173 fmt++;
174 zero = flen = 0;
175 if (*fmt >= '0' && *fmt <= '9') {
176 /* formatting length modifiers */
177 zero = (*fmt == '0') ? 1 : 0;
178 fmt += zero;
179 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
180 fmt++;
181 }
182 }
183
184 /* data type modifiers */
185 lmod = (*fmt == 'l') ? 1 : 0;
186 fptr = (*fmt == 'F') ? 1 : 0;
187 fmt += lmod + fptr;
188
189 switch (*fmt) {
190
191 case 's':
192 if (fptr) {
193 char _far *p = va_arg(va, char _far *);
194 s = (p == 0) ? "[null]" : p;
195 } else {
196 char *p = va_arg(va, char *);
197 s = (p == 0) ? "[null]" : p;
198 }
199 while ((*buf = *(s++)) != '\0')
200 buf++;
201 break;
202
203 case 'c':
204 *(buf++) = (char) va_arg(va, int);
205 break;
206
207 case 'd':
208 long_to_asc((lmod) ? va_arg(va, long)
209 : va_arg(va, int), buf, 10, zero, flen);
210 buf += strlen(buf);
211 break;
212
213 case 'x':
214 long_to_asc((lmod) ? va_arg(va, u32)
215 : va_arg(va, u16), buf, 16, zero, flen);
216 buf += strlen(buf);
217 break;
218
219 case 'p':
220 if (fptr || lmod) {
221 u16 off = va_arg(va, u16);
222 u16 seg = va_arg(va, u16);
223 long_to_asc(seg, buf, 16, 1, 4);
224 buf += strlen(buf);
225 *(buf++) = ':';
226 long_to_asc(off, buf, 16, 1, 4);
227 buf += strlen(buf);
228 } else {
229 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
230 buf += strlen(buf);
231 }
232 break;
233
234 default:
235 *(buf++) = *fmt;
236 break;
237 }
238 break;
239
240 case '\n':
241 *(buf++) = '\r';
242 *(buf++) = '\n';
243 break;
244
245 default:
246 *(buf++) = *fmt;
247 break;
248
249 }
250 }
251
252 *buf = '\0';
253 return((int) (buf - orig));
254}
255
256/*******************************************************************************
257 * Print a formatted message into a string buffer. Relies on vsprintf()
258 */
259int sprintf(char _far *buf, const char *fmt, ...)
260{
261 va_list va;
262
263 va_start(va, fmt);
264 return(vsprintf(buf, fmt, va));
265}
266
267/******************************************************************************
268 * Print messages to serial port
269 *
270 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
271 * messages should never exceed 1024 bytes.
272 */
273void vprintf(const char *fmt, va_list va)
274{
275 static char buf[1024];
276 char *s;
277 int len;
278
279 spin_lock(com_lock);
280
281 len = vsprintf(buf, fmt, va);
282
283 if (com_base == 0) {
284 /* write debug message to trace buffer, not COM port */
285 trace_write(buf, len);
286 spin_unlock(com_lock);
287 return;
288 }
289
290 /* write debug message to serial port */
291 for (s = buf; *s != '\0'; s++) {
292
293 /* inp() and outp() are redefined by the DDK in an incompatible
294 * way (only words). Instead of messing around with those
295 * definitions, it's safer and easier to put the whole thing
296 * into an _asm block.
297 *
298 * The C equivalent would look like this:
299 *
300 * while (!(inp(com_base + 5) & 0x20));
301 * outp(com_base, *s);
302 */
303
304 _asm {
305 /* wait until COM transmitter is idle */
306 mov dx, com_base;
307 add dx, 5;
308 transmitter_not_idle:
309 in al, dx;
310 and al, 0x20;
311 jz transmitter_not_idle;
312
313 /* output character to be sent */
314 mov dx, com_base;
315 mov bx, s;
316 mov al, [bx];
317 out dx, al;
318 };
319 }
320
321 spin_unlock(com_lock);
322}
323
324/******************************************************************************
325 * Print messages to COM port
326 */
327void printf(const char *fmt, ...)
328{
329 va_list va;
330
331 va_start(va, fmt);
332 vprintf(fmt, va);
333}
334
335/******************************************************************************
336 * Print a message to the system console. This works only during device driver
337 * initialization.
338 *
339 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
340 * messages should never exceed 1024 bytes...
341 */
342void cprintf(const char *fmt, ...)
343{
344 static char buf[1024];
345 va_list va;
346 size_t len;
347
348 va_start(va, fmt);
349 vsprintf(buf, fmt, va);
350
351 if (debug && com_base != 0) {
352 /* print the same message to COM1 as well */
353 printf("%s", buf);
354 }
355
356 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
357 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
358 buf[len-2] = '\0';
359 }
360
361 init_msgtbl.MsgStrings[0] = buf;
362 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
363}
364
365/******************************************************************************
366 * Print hex buffer to COM port.
367 */
368void phex(const void _far *p, int len, const char *fmt, ...)
369{
370 va_list va;
371 const unsigned char _far *buf = p;
372 int i;
373
374 if (!debug) {
375 return;
376 }
377
378 /* print header */
379 va_start(va, fmt);
380 vprintf(fmt, va);
381
382 /* print hex block */
383 while (len > 0) {
384 printf("%Fp ", buf);
385
386 /* print hex block */
387 for (i = 0; i < 16; i++) {
388 if (i < len) {
389 printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
390 } else {
391 printf(" ");
392 }
393 }
394
395 /* print ASCII block */
396 printf(" ");
397 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
398 printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
399 }
400 printf("\n");
401
402 buf += 16;
403 len -= 16;
404 }
405}
406
407/******************************************************************************
408 * Return length of zero-terminated string
409 */
410size_t strlen(const char _far *s)
411{
412 int len = 0;
413
414 while (*(s++) != '\0') {
415 len++;
416 }
417 return(len);
418}
419
420/******************************************************************************
421 * Copy zero-terminated string
422 */
423char _far *strcpy(char _far *dst, const char _far *src)
424{
425 char _far *orig = dst;
426
427 while ((*(dst++) = *(src++)) != '\0');
428 return(orig);
429}
430
431/******************************************************************************
432 * Compare blocks of memory
433 */
434int memcmp(void _far *p1, void _far *p2, size_t len)
435{
436 register char _far *s1 = p1;
437 register char _far *s2 = p2;
438 int n = 0;
439
440 while (len > 0) {
441 if ((n = *(s1++) - *(s2++)) != 0) {
442 return(n);
443 }
444 len--;
445 }
446 return(0);
447}
448
449/******************************************************************************
450 * Copy block from S/G list to virtual address or vice versa.
451 */
452void sg_memcpy(SCATGATENTRY _far *sg_list, USHORT sg_cnt, ULONG sg_off,
453 void _far *buf, USHORT len, SG_MEMCPY_DIRECTION dir)
454{
455 USHORT mode_flag;
456 USHORT i;
457 USHORT l;
458 ULONG phys_addr;
459 ULONG pos = 0;
460 char _far *p;
461
462 /* walk through S/G list to find the elements involved in the operation */
463 for (i = 0; i < sg_cnt && len > 0; i++) {
464 if (pos <= sg_off && pos + sg_list[i].XferBufLen > sg_off) {
465
466 /* this S/G element intersects with the block to be copied */
467 phys_addr = sg_list[i].ppXferBuf + (sg_off - pos);
468 if ((l = sg_list[i].XferBufLen - (sg_off - pos)) > len) {
469 l = len;
470 }
471
472 if (DevHelp_PhysToVirt(phys_addr, l, (PVOID) &p, &mode_flag)) {
473 panic("sg_memcpy(): DevHelp_PhysToVirt() failed");
474 }
475 if (dir == SG_TO_BUF) {
476 memcpy(buf, p, l);
477 } else {
478 memcpy(p, buf, l);
479 }
480 sg_off += l;
481 buf = (char _far *) buf + l;
482 len -= l;
483 }
484
485 pos += sg_list[i].XferBufLen;
486 }
487}
488
489/******************************************************************************
490 * Convert a string to a long value using the specified base
491 */
492long strtol(const char _far *buf, const char _far * _far *ep, int base)
493{
494 register const char _far *s = buf;
495 long val = 0;
496 int negative = 0;
497
498 /* skip leading whitespace */
499 while (*s == ' ' || *s == '\t') {
500 s++;
501 }
502
503 /* positive or negative */
504 if (*s == '-') {
505 negative = 1;
506 s++;
507 } else if (*s == '+') {
508 s++;
509 }
510
511 /* convert string to long integer */
512 for (;; s++) {
513 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
514 if (digit < 0 || digit >= base) {
515 break;
516 }
517 val *= base;
518 val += digit;
519 }
520
521 if (ep != NULL) {
522 *ep = s;
523 }
524 if (negative) {
525 val = -val;
526 }
527 return(val);
528}
529
530/******************************************************************************
531 * Extremely simple and stupid implementation of malloc(). The heap is very
532 * small, only 8K at the moment, and the memory blocks are managed using a
533 * simple array of "number of heap units allocated", zero meaning this unit is
534 * available. Each heap unit is currently 128 bytes.
535 *
536 * Dynamic memory is primarily used for things like ATA identify, ATAPI
537 * sense buffers, etc. and should be freed as soon as possible, otherwise
538 * we'll quickly run out of memory.
539 */
540void *malloc(size_t len)
541{
542 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
543 u16 i;
544 u16 n;
545
546 spin_lock(mem_lock);
547
548 /* find a sequence of free heap units big enough for the requested length */
549 for (i = 0; i < HEAP_UNIT_CNT; i++) {
550 if (heap_units[i] == 0) {
551 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
552 if (heap_units[n] != 0) {
553 break;
554 }
555 }
556 if (n == i + units) {
557 /* found a chunk large enough; update 'heap_units[]' */
558 for (; i < n; i++) {
559 heap_units[i] = (u8) (n - i);
560 }
561 spin_unlock(mem_lock);
562 return(heap_buf + (n - units) * HEAP_UNIT);
563 }
564
565 /* keep searching... */
566 i = n;
567 } else {
568 /* skip occupied heap units */
569 i += heap_units[i] - 1;
570 }
571 }
572
573 /* out of memory */
574 spin_unlock(mem_lock);
575 dprintf("malloc(%d): out of memory\n", len);
576 return(NULL);
577}
578
579/******************************************************************************
580 * Free block of memory allocted by malloc().
581 *
582 * NOTE: This function is not reentrant, thus must be called with the driver-
583 * level spinlock held. The main reason for this design is that most
584 * functions that need dynamic memory are already holding the spinlock.
585 */
586void free(void *ptr)
587{
588 u8 *p = (u8 *) ptr;
589 u16 first_unit;
590 u16 units;
591 u16 i;
592
593 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
594 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
595 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
596 (u16) p, (u16) heap_buf);
597 return;
598 }
599
600 /* clear unit allocation counters in heap_units[] */
601 spin_lock(mem_lock);
602
603 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
604 units = heap_units[first_unit];
605 for (i = first_unit; i < first_unit + units; i++) {
606 heap_units[i] = 0;
607 }
608
609 spin_unlock(mem_lock);
610}
611
612/******************************************************************************
613 * Return the physical address of a pointer inside the heap buffer. This is
614 * necessary because DevHelp_VirtToPhys() can't be called at interrupt time
615 * and we need physical addresses for heap objects when requeueing unaligned
616 * IORBs inside ahci_intr -> trigger_engine.
617 *
618 * If the pointer is not a heap pointer, this function falls back to calling
619 * DevHelp_VirtToPhys with all consequences (i.e. a trap when this is done
620 * at interrupt time).
621 */
622ULONG virt_to_phys(void _far *ptr)
623{
624 if (ptr < heap_buf || ptr > heap_buf + sizeof(heap_buf)) {
625 ULONG addr;
626
627 if (DevHelp_VirtToPhys(ptr, &addr) != 0) {
628 panic("virt_to_phys(): invalid pointer or execution mode");
629 }
630 return(addr);
631 }
632
633 return(heap_phys_addr + ((char _far *) ptr - (char _far *) heap_buf));
634}
635
636/******************************************************************************
637 * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
638 * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
639 * calls mdelay() with a large milliseond value as initial delay loop counter.
640 * When the timer triggers, 'mdelay()' will stop and update the delay loop
641 * counter.
642 *
643 * This function needs to be called at device driver init time. Since it uses
644 * ADD timers, it must be called with interrupts enabled. All this is not very
645 * precise (we should wait for a clock tick before starting, ...) but we don't
646 * really need precise timers.
647 */
648void mdelay_cal(void)
649{
650 ULONG timer_handle;
651
652 dprintf("calibrating delay loop... ");
653
654 mdelay_loops_per_ms = 100000;
655 mdelay_cal_status = MD_CALIBRATION_START;
656
657 ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
658 mdelay(999999999);
659 ADD_CancelTimer(timer_handle);
660
661 dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
662}
663
664/******************************************************************************
665 * Wait specified number of milliseconds. This is implemented using a busy
666 * loop and is only good for delays in the millisecond range but never for more
667 * than a few milliseconds and only in situations where a proper timer won't do.
668 * As a rule of thumb, don't call this function and use ADD timers, instead.
669 *
670 * NOTES:
671 *
672 * - Timers are problematic on x86 platforms because there's no reliable
673 * hardware timer on all architectures and the CPU clock speed may change
674 * while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
675 * calibration routines won't really be sufficient. But this usually only
676 * extends the delay and we don't really need a high precision timer. The
677 * exception are things like notebooks that are clocked slower when on
678 * battery and which got booted while on battery. Should still be OK,
679 * though, because our requirements are not that strict.
680 *
681 * - The code in this function is inefficient by design to make sure it
682 * will work with future CPUs which might otherwise be too fast for
683 * our loop counters. Part of this design is using volatile variables to
684 * force memory operations.
685 *
686 * - Before using this function, call mdelay_calibrate() to determine the
687 * number of inner loops required per millisecond.
688 */
689void mdelay(u32 millies)
690{
691 volatile u32 i;
692 volatile u32 n;
693
694 for (i = 0; i < millies; i++) {
695 for (n = 0; n < mdelay_loops_per_ms; n++) {
696 if (mdelay_cal_end()) {
697 /* this is a calibration run that just ended */
698 goto complete_calibration;
699 }
700 }
701 }
702 return;
703
704complete_calibration:
705 /* complete calibration cycle */
706 if (i < 1000) {
707 /* Initial value for delay loop was too high; interpolate results for
708 * an assumed initial delay loop divided by 1000.
709 */
710 i = i * 1000 + mdelay_loops_per_ms % 1000;
711 mdelay_loops_per_ms /= 1000;
712 }
713 mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
714 mdelay_cal_status = MD_CALIBRATION_DONE;
715}
716
717/******************************************************************************
718 * Sleep specified number of milliseonds. This is implemented by yielding the
719 * CPU until the system timer value indicates we're done. This function can
720 * only be called at task time, or from a context hook.
721 *
722 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
723 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
724 */
725void msleep(u32 millies)
726{
727 ULONG start;
728 ULONG end;
729
730 if (gis == NULL) {
731 /* no global info segment; use mdelay() */
732 mdelay(millies);
733 return;
734 }
735
736 start = gis->msecs;
737 end = start + millies;
738
739 if (end < start) {
740 /* wrap-around; wait until 'msecs' has wrapped, too */
741 while (gis->msecs >= start) {
742 DevHelp_Yield();
743 }
744 }
745
746 while (gis->msecs <= end) {
747 DevHelp_Yield();
748 }
749}
750
751/******************************************************************************
752 * Halt processing by submitting an internal error. This is a last resort and
753 * should only be called when the system state is corrupt.
754 */
755void panic(char *msg)
756{
757 DevHelp_InternalError(msg, strlen(msg));
758}
759
760/******************************************************************************
761 * Disable interrupts. The reason for using a separate function for this is
762 * that the presence of _asm statements will disable compiler optimizations.
763 * In order to support nested calls, this function will return 0 if the
764 * interrupts were already disabled or != 0, if not.
765 *
766 * NOTE: SMP systems should use spinlocks.
767 */
768int disable(void)
769{
770 int rc = 0;
771
772 _asm {
773 pushf
774 pop ax
775 and ax, 0x0200; /* "interrupts enabled" bit */
776 mov rc, ax;
777 cli
778 }
779
780 return(rc);
781}
782
783/******************************************************************************
784 * Enable interrupts. The reason for using a separate function for this is
785 * that the presence of _asm statements will disable compiler optimizations.
786 *
787 * NOTE: SMP systems should use spinlocks.
788 */
789void enable(void)
790{
791 _asm sti;
792}
793
794/******************************************************************************
795 * Convert 'long' to ASCII with the specified base
796 */
797static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
798{
799 register unsigned long abs_val;
800 char tmp[80];
801 char _far *ptmp = tmp;
802 char _far *s;
803
804 if (base > 16) {
805 sprintf(buf, "[EVAL]");
806 return;
807 }
808
809 abs_val = (unsigned long) ((val < 0 && base <= 10) ? -val : val);
810 tmp[sizeof(tmp) - 1] = '\0';
811
812 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
813 *s = hex_digits[abs_val % base];
814 flen--;
815 if ((abs_val /= base) == 0) {
816 break;
817 }
818 }
819
820 /* left-pad the resulting number with zeros or spaces up to 'flen' */
821 while (flen > 0) {
822 *(--s) = (zero) ? '0' : ' ';
823 flen--;
824 }
825
826 /* prepend minus sign if val was negative and base is decimal or less */
827 if (val < 0 && base <= 0) {
828 *(--s) = '-';
829 flen--;
830 }
831
832 strcpy(buf, s);
833}
834
835/******************************************************************************
836 * Timer callback handler for 'mdelay_calibrate()'
837 */
838static void _cdecl _far mdelay_timer_callback(ULONG timer_handle,
839 ULONG parm1,
840 ULONG parm2)
841{
842 mdelay_cal_status = MD_CALIBRATION_END;
843}
844
845/******************************************************************************
846 * Determine whether an mdelay calibration run has just ended. This is in a
847 * function to prevent overzealous optimizers from removing the whole delay
848 * loop in mdelay().
849 */
850static int mdelay_cal_end(void)
851{
852 return(mdelay_cal_status == MD_CALIBRATION_END);
853}
854
Note: See TracBrowser for help on using the repository browser.