Coherent4.2.10/tboot/cbootlib.c
/* cbootlib.c -- C routines for use by boot programs.
*
* La Monte H. Yarroll <piggy@mwc.com>, September 1991
*/
#include <string.h>
#include <ctype.h>
#include "tboot.h"
/* puts() -- put a NUL terminated string.
* Takes one argument--a pointer to a NUL terminated character string.
* Does no error checking. Calls the assembly language routine putc().
*/
void
puts(s)
register char *s;
{
while (*s != '\0') {
putchar(*s++);
}
} /* puts() */
#define BS '\010'
#define DEL '\0' /* This is really what getchar() returns! */
#define NAK '\025'
/* gets() -- Read string from keyboard.
* Takes one argument--a pointer to a buffer big enough for the
* expected response.
* It stops reading as soon as it detects a carriage return. The CR
* is replaced with a NUL.
*/
char *
gets(s)
char *s;
{
register char *t;
t = s;
while ('\r' != (*t = getchar())) {
if ((BS == *t) || (DEL == *t)) {
/* Process back space. */
if (t > s) {
t--;
puts("\010 \010"); /* Erase the last character. */
}
} else if (NAK == *t) {
/* Kill line. */
while (--t >= s) {
puts("\010 \010"); /* Erase the last character. */
}
t = s;
} else {
/* Echo the character; prepare for another. */
putchar(*t);
t++;
}
}
*t = '\0';
return(s);
} /* gets() */
/* Reverse string s in place.
* Straight from K&R.
*/
void
reverse(s)
char s[];
{
int c, i, j;
for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
} /* reverse() */
#define BASE10 10 /* itoa() generates base 10 numbers. */
/* Convert n to decimal characters in s.
* Straight from K&R (with minor sylistic changes.)
*/
void
itoa(n, s)
char s[];
int n;
{
int i, sign;
if ((sign = n) < 0) { /* Record sign. */
n = -n; /* Make n positive. */
}
i = 0;
do { /* Generate digits in reverse order. */
s[i++] = n % BASE10 + '0'; /* Get next digit. */
} while ((n /= BASE10) > 0); /* Delete it. */
if (sign < 0) {
s[i++] = '-';
}
s[i] = '\0';
reverse(s);
} /* itoa() */
#define BASE16 16
/* Convert n to digits in s, base base.
* Works for any base from 2 to 36.
* Modified itoa() from K&R.
*/
void
itobase(n, s, base)
uint16 n;
char s[];
int base;
{
uint16 i;
i = 0;
do { /* Generate digits in reverse order. */
s[i] = n % base + '0'; /* Get next digit. */
/* Adjust for the gap between '9' and 'A'. */
if (s[i] > '9') {
s[i] += ('A' - '9') - 1;
}
++i;
} while ((n /= base) > 0); /* Delete it. */
s[i] = '\0';
reverse(s);
} /* itobase() */
/* basetoi(char *s, int base)
* Convert a string base "base" to an integer.
* Good through base 36.
* Loosely based on K&R's atoi().
*/
uint16
basetoi(s, base)
char *s;
int base;
{
int i, n;
static char convert[]="0123456789abcdefghijklmnopqrstuvwxyz";
/* Lowercase the entire string. */
for(i = 0; '\0' != s[i]; ++i) {
if (isupper(s[i])) {
s[i] = tolower(s[i]);
}
}
/* Actually do the conversion. */
n = 0;
for (i = 0; '\0' != s[i] && NULL != strchr(convert, s[i]); ++i) {
n = (base * n) + (strchr(convert, s[i]) - convert);
}
return(n);
} /* basetoi() */
/* seginc(uint16 *offset,
* uint16 *segment,
* uint16 increment)
* Add an offset to a segment. We may adjust the segment base
* to make everything fit.
*/
#define MAXSEG (int32)0xffff /* Top address in a segment. */
#define PPSIZE 16 /* Size of a paragraph--
* segments are PP aligned.
*/
void
seginc(offset, segment, increment)
uint16 *offset;
uint16 *segment;
uint16 increment;
{
/* If we won't spill over a segment boundary, just add increment
* to *offset.
*/
if ((int32) (*offset) + (int32) increment < MAXSEG) {
*offset += increment;
} else {
/* Otherwise, we have to adjust the segment. */
*segment += (increment / PPSIZE);
/* If offset is within PPSIZE of the end of a segment,
* we have to bump segment up to the next paragraph.
*/
if ((int32) (*offset) + (int32) (increment % PPSIZE) < MAXSEG) {
*offset += (increment % PPSIZE);
} else {
*segment += 1;
*offset =
(int) (((int32) (*offset) +
(int32) (increment % PPSIZE))
- MAXSEG);
}
}
} /* seginc() */
/* Pad a string s on the left with character c, to length n.
* The old contents of s are replaced by the padded version.
*/
char *
lpad(s, c, n)
char *s;
char c;
int n;
{
static char localbuf[LINESIZE];
register int len_s; /* length of s. */
register int i;
len_s = strlen(s);
/* We only have something to do if the string is too short. */
if (len_s < n) {
/* Is n small enough to fit in locabuf? */
if (n < (LINESIZE - 1)) {
/* Fill the padding into the local buffer. */
for (i = 0; i < (n - len_s); ++i) {
localbuf[i] = c;
}
localbuf[i] = '\0';
/* Append the string to be padded. */
strcat(localbuf, s);
/* Copy the padded string back where it came from. */
strcpy(s, localbuf);
} else {
/* Too big! Do something more complicated. */
strcpy(s, "lpad: n is too big!");
}
}
return(s);
} /* lpad() */
#define BITS_PER_INT16 16 /* Number of bits in an int16. */
#define DIGITS_PER_INT16 4 /* Maximum hex digits in a 16 bit number. */
#define DIGITS_PER_INT8 2 /* Maximum hex digits in an 8 bit number. */
/*
* Print a 32 bit integer in hexadecimal.
*/
void
print32(my_int)
uint32 my_int;
{
uint16 half;
char buffer[sizeof("ffff")];
/* Convert and print the upper half. */
half = (uint16) ((my_int) >> BITS_PER_INT16);
itobase(half, buffer, BASE16);
lpad(buffer, '0', DIGITS_PER_INT16);
puts(buffer);
/* Convert and print the lower half. */
half = (uint16) ((my_int << BITS_PER_INT16) >> BITS_PER_INT16);
itobase(half, buffer, BASE16);
lpad(buffer, '0', DIGITS_PER_INT16);
puts(buffer);
}
/*
* Print a 16 bit integer in hexadecimal.
*/
void
print16(my_int)
uint16 my_int;
{
char buffer[sizeof("ffff")];
itobase(my_int, buffer, BASE16);
lpad(buffer, '0', DIGITS_PER_INT16);
puts(buffer);
}
/*
* Print an 8 bit integer in hexadecimal.
*/
void
print8(my_int)
uint8 my_int;
{
char buffer[sizeof("ff")];
itobase((uint16) my_int, buffer, BASE16);
lpad(buffer, '0', DIGITS_PER_INT8);
puts(buffer);
}
/*
* Wrapper for far-far copy. Changes the segment so that the requested
* length does not wrap past the end of the segment.
*
* For Intel 8086 Real Mode.
*/
void
ffcopy(to_offset, to_seg, from_offset, from_seg, length)
uint16 to_offset;
uint16 to_seg;
uint16 from_offset;
uint16 from_seg;
uint16 length;
{
uint16 to_move; /* Amount to move at a time. */
/* Algorithm:
* Align both segments so each offset is within a paragraph
* of the beginning of the segment.
* Move up to 1/2 a segment.
* Decrement length.
* Interate.
*/
while (length != 0) {
/* Align both segments. */
seg_align(&to_offset, &to_seg);
seg_align(&from_offset, &from_seg);
/* Move up to 1/2 a segment. */
to_move = LESSER(length, MAXUINT16/2);
_ffcopy(from_offset, from_seg, to_offset, to_seg, to_move);
/* Decrement length. */
length -= to_move;
}
} /* ffcopy() */
/*
* Align a far address so that its offset is within a paragraph of
* the start of the segment.
*
* Note that we ignore overflow in the segment, since this is exactly
* what happens when you offset past the end of the highest segment.
*
* WARNING: This routine is destructive to its arguments.
*
* For Intel 8086 Real Mode.
*/
void
seg_align(offset, segment)
uint16 *offset;
uint16 *segment;
{
#define BYTE_PER_PP 16 /* Number of bytes in a paragraph. */
uint16 new_offset,
new_segment;
new_segment = *segment + (*offset/BYTE_PER_PP);
new_offset = *offset % BYTE_PER_PP;
*segment = new_segment;
*offset = new_offset;
} /* seg_align() */
/*
* wait_for_keystroke() -- wait for a specific keystroke.
*/
/* Location of BIOS-run timer. */
#define TIMER_SEG 0x0040
#define TIMER_OFF 0x006c
#define MIDNIGHT (((uint32) 24) << 16)
/*
* Waits delay ticks for the requested keystroke. Returns TRUE if
* keystroke came, FALSE if delay runs out.
* If key == -1, accept ANY keystroke.
*/
int
wait_for_keystroke(delay, key)
int delay;
int key;
{
extern uint16 myds; /* My Data Segment, defined in Statup.s. */
uint32 end_time; /* Return when time reaches this. */
uint32 current_time; /* Current value of timer list. */
int my_key_found;
while (iskey()) {
getchar(); /* Eat all pending characters. */
}
/* Calculate the terminating time. */
ffcopy(&end_time, myds, TIMER_OFF, TIMER_SEG, sizeof(int32));
end_time += (int32) delay;
/* Adjust for timer reset at midnight. */
if (end_time > MIDNIGHT) {
/* These messages are meaningless. */
puts("KABOOM!\r\n");
puts("I'm tired. Please leave me alone.\r\n");
end_time -= MIDNIGHT;
}
/* Busy wait keystrokes and time delay. */
my_key_found = FALSE;
do {
ffcopy(¤t_time, myds, TIMER_OFF, TIMER_SEG,sizeof(int32));
if (iskey()) {
/* The order of evaluation here is important.
* getchar() MUST be called to clean out the
* pending character.
*/
if (((int) getchar() == key) || (-1 == key)) {
my_key_found = TRUE;
}
}
} while (!my_key_found && (current_time < end_time));
return(my_key_found);
} /* wait_for_keystrok() */