1
0
Files
2022-09-29 17:59:04 +03:00

1262 lines
20 KiB
C

/***********************************************************************\
* File: libc.c *
* *
* PROM C Library *
* *
* I/O is multiplexed to a serial device selected through *
* a pointer in the boot status register. *
* *
\***********************************************************************/
#include <sys/types.h>
#include <sys/cpu.h>
#include "ip27prom.h"
#include "hub.h"
#include "libasm.h"
#include "libc.h"
#include "symbol.h"
#define CTRL(x) ((x) & 0x1f)
#define DEL 0x7f
#define INTR CTRL('C')
#define BEL 0x7
#define MAXINT ((~(unsigned int) 0) >> 1)
#define DEV ((libc_device_t *) get_libc_device())
/*
* libc_device
*
* Select I/O device by supplying a pointer to a libc_device_t structure.
* The libc_device_t structure should be permanent (in the PROM).
*/
libc_device_t *
libc_device(libc_device_t *dev)
{
if (dev)
set_libc_device((__uint64_t) dev);
return (libc_device_t *) get_libc_device();
}
/*
* libc_init
*
* Initialize the selected device
*/
void
libc_init(void *init_data)
{
if (DEV->init)
DEV->init(init_data);
}
/*
* poll
*
* Returns 1 if a character is available on the selected device,
* 0 otherwise.
*/
int
poll(void)
{
return DEV->poll();
}
/*
* readc
*
* Returns a character from the selected device.
* May only be called if poll indicated a character was pending
* on the selected device.
*/
int
readc(void)
{
return DEV->readc();
}
/*
* putc
*
* Writes a single character to the selected device when ready.
* Returns 0 if successful, -1 if error.
*
* Device routines convert \n to \r\n (can add 256 to quote a plain \n).
*/
int
putc(int c)
{
return DEV->putc(c);
}
int
putchar(int c)
{
return DEV->putc(c);
}
/*
* puts
*
* Writes a string to the selected device.
* For some drivers, more efficient than calling putc for each character.
* Returns 0 if successful, -1 if error.
*/
int
puts(char *s)
{
return DEV->puts(s);
}
/*
* flush
*
* Flushes the input buffer.
*/
int
flush(void)
{
return DEV->flush ? DEV->flush() : 0;
}
/*
* kbintr
*
* Returns 1 if control-C has been pressed (and prints ^C).
* Only polls a few times a second since it may be very expensive.
*/
int
kbintr(rtc_time_t *next)
{
rtc_time_t now = rtc_time();
if (now > *next) {
*next = now + 250000;
if (poll() && getc() == 3) {
printf("^C\n");
return -1;
}
}
return 0;
}
/*
* more
*
* Checks to see if the number of lines has been exceeded, and if so,
* prompts for the user to type SPACE for another screenful or ENTER
* for one more line. Returns 0 if the user aborts by pressing q, Q,
* or Control-C; otherwise returns 1. Should be called BEFORE each
* line is printed.
*/
int
more(int *lines, int max)
{
int j, c;
if (++(*lines) == max) {
printf("--More--");
while ((c = getc()) != '\r' && c != '\n' &&
c != 'q' && c != 'Q' && c != ' ' && c != 3)
;
putc('\r');
for (j = 0; j < 10; j++)
putc(32);
putc('\r');
if (c == 'q' || c == 'Q' || c == 3)
return 0;
*lines = (c == '\r' || c == '\n') ? max - 1 : 0;
}
return 1;
}
/*
* device
*
* Returns the device name.
*/
char *
device(void)
{
return DEV->dev_name;
}
/*
* getc_timeout (see also: getc)
*
* Waits for a character on the selected device and returns it.
* Flashes hub LEDs while waiting. The flashing pattern
* indicates which device is accepting input. Returns -1 if
* the user-supplied timeout expires.
*/
#define BLINK_PERIOD 1000000 /* microseconds */
int
getc_timeout(__uint64_t usec)
{
libc_device_t *dev = DEV;
int leds, i;
rtc_time_t now;
rtc_time_t blink_expire;
rtc_time_t user_expire;
flush();
blink_expire = rtc_time();
user_expire = usec ? rtc_time() + usec : ~0ULL;
for (leds = 0x80; ; leds ^= dev->led_pattern) {
hub_led_set(leds);
blink_expire += BLINK_PERIOD;
while ((now = rtc_time()) < blink_expire && now < user_expire)
if ((*dev->poll)()) {
hub_led_set(0xff);
return (*dev->readc)();
}
if (now >= user_expire)
return -1;
}
}
/*
* getc
*
* Like getc_timeout, but without a timeout.
*/
int
getc(void)
{
return getc_timeout(0);
}
/*
* gets_timeout (see also: gets)
*
* Reads an input line from the current device.
* Handles basic line editing.
* If timeout expires, uses 'defl' string (and prints it too).
* If a blank line is entered and 'defl' is non-empty, uses 'defl'.
* Uses empty buffer and returns NULL if user presses ^C.
*/
#ifdef SABLE
#define ECHO(c) /* Sable does the echoing */
#else
#define ECHO(c) putc(c)
#endif
char *
gets_timeout(char *buf, int max_length, __uint64_t usec, char *defl)
{
int c;
char *bufp;
if (defl == 0)
defl = "";
bufp = buf;
for (;;) {
if ((c = getc_timeout(usec)) < 0)
break; /* Timed out */
usec = 0; /* Disable time-out once any key is typed */
switch (c) {
case CTRL('C'):
ECHO('^');
ECHO('C');
ECHO('\n');
buf[0] = 0;
return 0;
case CTRL('H'):
case DEL:
if (bufp > buf) {
bufp--;
ECHO('\b');
ECHO(' ');
ECHO('\b');
} else
ECHO(BEL);
break;
case CTRL('U'):
while (bufp > buf) {
ECHO('\b');
ECHO(' ');
ECHO('\b');
bufp--;
}
break;
case CTRL('R'):
ECHO('\n');
for (c = 0; c < bufp - buf; c++)
ECHO(buf[c]);
break;
case '\t':
do {
if (bufp >= buf + max_length - 1) {
ECHO(BEL);
break;
}
*bufp++ = ' ';
ECHO(' ');
} while ((bufp - buf) & 7);
break;
case '\n':
case '\r':
if (bufp > buf && bufp[-1] == '\\') {
bufp[-1] = '\n';
ECHO('\n');
break;
}
*bufp = 0;
if (buf[0] == 0 && LBYTE(defl) != 0)
goto do_defl;
ECHO('\n');
return buf;
default:
/*
* Make sure there's room for this character plus a
* trailing \n and 0 byte, and that it's printable.
*/
if (bufp >= buf + max_length - 1 || c < 32 || c > 126)
ECHO(BEL);
else {
*bufp++ = c;
ECHO(c);
}
break;
}
}
do_defl:
printf("%s\n", defl);
strcpy(buf, defl);
return buf;
}
/*
* gets
*
* Like gets_timeout, but without a timeout.
*/
char *
gets(char *buf, int max_length)
{
return gets_timeout(buf, max_length, 0, 0);
}
/*
* puthex
*
* Writes a 64-bit value as ASCII hexadecimal.
* Returns 0 if successful, -1 if putc failed.
*/
int
puthex(__uint64_t v)
{
int i, c;
for (i = 0; i < 16; i++, v <<= 4) {
c = (int) (v >> 60);
c += (c < 10) ? '0' : 'a' - 10;
if (putc(c) < 0)
return -1;
}
return 0;
}
/*
* strlen
*/
size_t
strlen(const char *s)
{
size_t len;
for (len = 0; LBYTE(s); s++)
len++;
return len;
}
/*
* strcpy
*/
char *
strcpy(char *dst, const char *src)
{
int i;
for (i = 0; ; i++)
if ((dst[i] = LBYTE(&src[i])) == 0)
break;
return dst;
}
/*
* strncpy
*/
char *
strncpy(char *dst, const char *src, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
if ((dst[i] = LBYTE(&src[i])) == 0)
break;
return dst;
}
/*
* strncmp
*/
int
strncmp(const char *s1, const char *s2, size_t n)
{
int c1, c2;
while (n--) {
c1 = LBYTE(s1);
s1++;
c2 = LBYTE(s2);
s2++;
if (c1 == 0 && c2 == 0)
break;
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
}
return 0;
}
/*
* strcmp
*/
int
strcmp(const char *s1, const char *s2)
{
return strncmp(s1, s2, MAXINT);
}
/*
* strchr
*/
char *
strchr(const char *s, int c)
{
int d;
for (; (d = LBYTE(s)) != 0; s++)
if (d == c)
return (char *) s;
return 0;
}
/*
* strcat
*/
char *strcat(char *dst, const char *src)
{
int i;
for (i = 0; dst[i]; i++)
;
strcpy(dst + i, src);
return dst;
}
/*
* strstr
*/
char *strstr(const char *s, const char *substr)
{
int slen = strlen(substr);
char *se;
for (se = (char *) s + strlen(s) - slen; s <= se; s++)
if (strncmp(s, substr, slen) == 0)
return (char *) s;
return 0;
}
/*
* strrepl
*/
char *strrepl(char *s, int start, int len, const char *repstr)
{
memcpy(s + start + strlen(repstr),
s + start + len,
strlen(s + start + len) + 1);
memcpy(s + start, repstr, strlen(repstr));
return s;
}
/*
* memset
*/
void *
memset(void *base, int value, size_t len)
{
char *dst = base;
if (len >= 8) {
__uint64_t rep;
rep = (__uint64_t) value & 0xff;
rep |= rep << 8;
rep |= rep << 16;
rep |= rep << 32;
while (len > 0 && (__psunsigned_t) dst & 7) {
*dst++ = value;
len--;
}
while (len >= 32) {
((__uint64_t *) dst)[0] = rep;
((__uint64_t *) dst)[1] = rep;
((__uint64_t *) dst)[2] = rep;
((__uint64_t *) dst)[3] = rep;
dst += 32;
len -= 32;
}
while (len >= 8) {
*(__uint64_t *) dst = rep;
dst += 8;
len -= 8;
}
}
while (len--)
*dst++ = value;
return base;
}
/*
* memcmp8
*
* A version of memcmp that requires 8-byte alignment of pointers and size.
*/
int memcmp8(__uint64_t *s1, __uint64_t *s2, size_t n)
{
__uint64_t a1, a2;
while (n > 7) {
a1 = *s1++;
a2 = *s2++;
if (a1 < a2)
return -1;
else if (a1 > a2)
return 1;
n -= 8;
}
return 0;
}
/*
* bzero
*/
void
bzero(void *base, size_t len)
{
memset(base, 0, len);
}
/*
* memcpy
*
* Optimized memcpy that can be used on PROM data.
*/
void *
memcpy(void *dest, const void *source, size_t len)
{
char *dst = dest;
char *src = (char *) source;
if (dst < src || src + len < dst) {
/*
* Forward copy
*/
if ((((__uint64_t) dst ^ (__uint64_t) src) & 7) != 0)
goto residual_1; /* Totally unaligned */
while (len > 0 && ((__uint64_t) dst & 7) != 0) {
*dst++ = LBYTE(src);
src++;
len--;
}
while (len >= 64) {
((__uint64_t *) dst)[0] = ((__uint64_t *) src)[0];
((__uint64_t *) dst)[1] = ((__uint64_t *) src)[1];
((__uint64_t *) dst)[2] = ((__uint64_t *) src)[2];
((__uint64_t *) dst)[3] = ((__uint64_t *) src)[3];
((__uint64_t *) dst)[4] = ((__uint64_t *) src)[4];
((__uint64_t *) dst)[5] = ((__uint64_t *) src)[5];
((__uint64_t *) dst)[6] = ((__uint64_t *) src)[6];
((__uint64_t *) dst)[7] = ((__uint64_t *) src)[7];
dst += 64;
src += 64;
len -= 64;
}
while (len >= 8) {
*(__uint64_t *) dst = *(__uint64_t *) src;
dst += 8;
src += 8;
len -= 8;
}
residual_1:
while (len-- > 0) {
*dst++ = LBYTE(src);
src++;
}
} else {
/*
* Backward copy
*/
dst += len;
src += len;
if ((((__uint64_t) dst ^ (__uint64_t) src) & 7) != 0)
goto residual_2; /* Totally unaligned */
while (len > 0 && ((__uint64_t) dst & 7) != 0) {
--src;
*--dst = LBYTE(src);
len--;
}
while (len >= 64) {
dst -= 64;
src -= 64;
len -= 64;
((__uint64_t *) dst)[7] = ((__uint64_t *) src)[7];
((__uint64_t *) dst)[6] = ((__uint64_t *) src)[6];
((__uint64_t *) dst)[5] = ((__uint64_t *) src)[5];
((__uint64_t *) dst)[4] = ((__uint64_t *) src)[4];
((__uint64_t *) dst)[3] = ((__uint64_t *) src)[3];
((__uint64_t *) dst)[2] = ((__uint64_t *) src)[2];
((__uint64_t *) dst)[1] = ((__uint64_t *) src)[1];
((__uint64_t *) dst)[0] = ((__uint64_t *) src)[0];
}
while (len >= 8) {
dst -= 8;
src -= 8;
len -= 8;
*(__uint64_t *) dst = *(__uint64_t *) src;
}
residual_2:
while (len-- > 0) {
--src;
*--dst = LBYTE(src);
}
}
return dest;
}
/*
* memsum
*
* Efficiently computes the sum of bytes in a memory range.
* Can be used on PROM data.
*/
__uint64_t
memsum(void *base, size_t len)
{
uchar_t *src = base;
__uint64_t sum, part;
int i;
sum = 0;
while (len > 0 && (__psunsigned_t) src & 7) {
sum += LBYTEU(src);
src++;
len--;
}
while (len >= 128 * 8) {
part = qs128((ulong) src);
sum += part & 0xffff;
sum += part >> 16 & 0xffff;
sum += part >> 32 & 0xffff;
sum += part >> 48 & 0xffff;
src += 128 * 8;
len -= 128 * 8;
}
while (len > 0) {
sum += LBYTEU(src);
src++;
len--;
}
return sum;
}
/*
* atoi
*/
int
atoi(const char *cp)
{
int i, c, neg;
if (LBYTE(cp) == '-') {
cp++;
neg = 1;
} else
neg = 0;
/* Calculate in the negative so -MAXINT works */
for (i = 0; isdigit(c = LBYTE(cp)); cp++)
i = i * 10 - (c - '0');
return neg ? i : -i;
}
#define DIGIT(x) (isdigit(x) ? (x) - '0' : \
islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
/*
* htol
*/
__uint64_t
htol(char *cp)
{
__uint64_t i;
/* Ignore leading 0x or 0X */
if (*cp == '0' && (cp[1] == 'x' || cp[1] == 'X'))
cp += 2;
i = 0;
while (isxdigit(*cp)) {
i = i * 16 + DIGIT(*cp);
cp++;
}
return i;
}
/*
* strtoull
*/
__uint64_t
strtoull(const char *str, char **nptr, int base)
{
unsigned long long val;
int c;
int xx;
const char **ptr = (const char **)nptr;
int neg = 0;
if (ptr != (const char **)0)
*ptr = str; /* in case no number is formed */
c = *str;
if (! isalnum(c)) {
while (isspace(c))
c = *++str;
switch (c) {
case '-':
neg++;
/* FALLTHROUGH */
case '+':
c = *++str;
}
}
if (base == 0)
if (c != '0')
base = 10;
else if (str[1] == 'x' || str[1] == 'X')
base = 16;
else if (str[1] == 'b' || str[1] == 'B')
base = 2;
else
base = 8;
/*
* for any base > 10, the digits incrementally following
* 9 are assumed to be "abc...z" or "ABC...Z"
*/
if (!isalnum(c) || (xx = DIGIT(c)) >= base)
return (0); /* no number formed */
if (base == 16 && c == '0' && (str[1] == 'x' || str[1] == 'X') &&
isxdigit(str[2]))
c = *(str += 2); /* skip over leading "0x" or "0X" */
if (base == 2 && c == '0' && (str[1] == 'b' || str[1] == 'B') &&
(str[2] == '0' || str[2] == '1'))
c = *(str += 2); /* skip over leading "0b" or "0B" */
val = (unsigned long long) DIGIT(c);
for (c = *++str; isalnum(c) && (xx = DIGIT(c)) < base; ) {
val *= (unsigned long long) base;
val += (unsigned long long) xx;
c = *++str;
}
if (ptr != (const char **)0)
*ptr = str;
return neg ? ((unsigned long long) -(__int64_t) val) : val;
}
/*
* sprintnu -- output an unsigned number n in base b.
*/
void
sprintnu(char *tgt, __scunsigned_t n, int b, int digits)
{
char prbuf[72];
char *cp;
int d;
cp = prbuf;
do {
d = (int) (n % b);
*cp++ = (d < 10) ? (d + '0') : (d - 10 + 'a');
n /= b;
if (digits)
digits--;
} while (n || digits);
do
*tgt++ = *--cp;
while (cp > prbuf);
*tgt++ = 0;
}
/*
* sprintns -- output a signed number n in base b.
*/
void
sprintns(char *tgt, __scint_t n, int b, int digits)
{
if (n < 0) {
*tgt++ = '-';
n = -n;
}
sprintnu(tgt, (__scunsigned_t) n, b, digits);
}
/*
* sputs -- output a string with fill
*/
static void
sputs(int (*putc)(), __uint64_t putc_data,
char *s, int fw, int len, char fillc)
{
register int i;
if (s == NULL)
s = "(null)";
if (len <= 0)
i = strlen(s);
else
for (i = 0; i < len; i++)
if (LBYTE(&s[i]) == 0)
break;
len = i;
if (fw > 0)
for (i = fw - len; --i >= 0;)
(*putc)(fillc, putc_data);
for (i = len; --i >= 0; s++)
(*putc)(LBYTE(s), putc_data);
if (fw < 0)
for (i = -fw - len; --i >= 0;)
(*putc)(fillc, putc_data);
return;
}
void
prf(int (*putc)(), __uint64_t putc_data, const char *fmt, va_list adx)
{
int c, i;
char *s;
char prbuf[72]; /* large enough for base 2 numbers */
int sgnd, base, fillc, fw, len, ljust;
int altform, islong, isshort;
for (;;) {
while (1) {
c = LBYTE(fmt);
fmt++;
if (c == 0)
return;
if (c == '%')
break;
(*putc)(c, putc_data);
}
fillc = ' ';
fw = 0;
len = 0;
islong = 0;
isshort = 0;
c = LBYTE(fmt);
fmt++;
if (altform = (c == '#')) {
c = LBYTE(fmt);
fmt++;
}
if (ljust = (c == '-')) {
c = LBYTE(fmt);
fmt++;
}
if (c == '0') {
fillc = c;
c = LBYTE(fmt);
fmt++;
}
if (isdigit(c)) {
i = 0;
while (isdigit(c)) {
i = i * 10 + c - '0';
c = LBYTE(fmt);
fmt++;
}
fw = i;
} else if (c == '*') { /* precision from arg */
/*CONSTCOND*/
fw = va_arg(adx, int);
c = LBYTE(fmt);
fmt++;
}
if (c == '.') {
i = 0;
c = LBYTE(fmt);
fmt++;
if (c == '*') { /* precision from arg */
/*CONSTCOND*/
i = va_arg(adx, int);
c = LBYTE(fmt);
fmt++;
} else
while (isdigit(c)) {
i = i * 10 + c - '0';
c = LBYTE(fmt);
fmt++;
}
len = i;
}
if (ljust)
fw = -fw;
if (c == 'l' || c == 'L') {
islong = 1;
c = LBYTE(fmt);
fmt++;
if (c == 'l' || c == 'L') {
c = LBYTE(fmt);
fmt++;
}
} else if (c == 'h') {
isshort = 1;
c = LBYTE(fmt);
fmt++;
}
switch (c) {
case 'y': /* Short for 0x%016llx */
fw = 16;
fillc = '0';
islong = 1;
c = 'x';
altform = 1;
/* fall through */
case 'x':
case 'X':
sgnd = 0;
base = 16;
if (altform) {
(*putc)('0', putc_data);
(*putc)(c, putc_data);
}
goto number;
case 'd':
case 'i':
sgnd = 1;
base = 10;
goto number;
case 'u':
sgnd = 0;
base = 10;
goto number;
case 'o':
sgnd = 0;
base = 8;
if (altform)
(*putc)('0', putc_data);
goto number;
case 'b':
sgnd = 0;
base = 2;
if (altform) {
(*putc)('0', putc_data);
(*putc)(c, putc_data);
}
number:
if (isshort) {
if (sgnd)
sprintns(prbuf, va_arg(adx, short), base, 0);
else
sprintnu(prbuf, va_arg(adx, ushort_t), base, 0);
} else if (islong) {
if (sgnd)
sprintns(prbuf, va_arg(adx, long), base, 0);
else
sprintnu(prbuf, va_arg(adx, ulong), base, 0);
} else {
if (sgnd)
sprintns(prbuf, va_arg(adx, __scint_t), base, 0);
else
sprintnu(prbuf, va_arg(adx, __scunsigned_t), base, 0);
}
sputs(putc, putc_data, prbuf, fw, len, fillc);
break;
case 'c': /* print a character */
/*CONSTCOND*/
prbuf[0] = va_arg(adx, int);
prbuf[1] = 0;
sputs(putc, putc_data, prbuf, fw, len, fillc);
break;
case 's': /* Print a string */
s = va_arg(adx, char *);
sputs(putc, putc_data, s, fw, len, fillc);
break;
case '%':
(*putc)(c, putc_data);
break;
case 'C': /* PROM SPECIAL: CPU letter */
(*putc)('A' + (int) hub_cpu_get(), putc_data);
break;
case 'P': /* PROM SPECIAL: display address as symbol+offset */
symbol_name_off((void *) va_arg(adx, ulong), prbuf);
sputs(putc, putc_data, prbuf, fw, len, fillc);
break;
case 'S': /* PROM SPECIAL: /hw/module/%d/slot/n%d */
strcpy(prbuf, "/hw/module/");
sprintns(prbuf + 11, va_arg(adx, __scint_t), 10, 0);
strcat(prbuf, "/slot/");
if (SN00)
strcat(prbuf, "MotherBoard");
else {
i = strlen(prbuf);
prbuf[i++] = 'n';
sprintns(prbuf + i, va_arg(adx, __scint_t), 10, 0);
}
sputs(putc, putc_data, prbuf, fw, len, fillc);
break;
default:
break;
}
}
}
/*VARARGS1*/
int
printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
prf(putc, 0, fmt, ap);
va_end(ap);
return 0;
}
int
vprintf(const char *fmt, va_list ap)
{
prf(putc, 0, fmt, ap);
return 0;
}
int db_printf(const char *fmt, ...)
{
va_list ap;
if (get_libc_verbosity()) {
va_start(ap, fmt);
prf(putc, 0, fmt, ap);
va_end(ap);
}
return 0;
}
int db_vprintf(const char *fmt, va_list ap)
{
if (get_libc_verbosity())
prf(putc, 0, fmt, ap);
return 0;
}
static int
sprint_putc(int c, __uint64_t data)
{
*(*((char **) data))++ = c;
return 0;
}
/*VARARGS2*/
int
sprintf(char *buf, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
prf(sprint_putc, (__uint64_t) &buf, fmt, ap);
va_end(ap);
*buf = 0;
return 0;
}
int
vsprintf(char *buf, const char *fmt, va_list ap)
{
prf(sprint_putc, (__uint64_t) &buf, fmt, ap);
*buf = '\0';
return 0;
}
#if 0 /* Commented out to save room */
/*VARARGS1*/
static int
length_putc(int c, __uint64_t data)
{
return (*(int *) data)++;
}
int
sprintf_length(const char *fmt, ...)
{
va_list ap;
int length = 1;
va_start(ap, fmt);
prf(length_putc, (__uint64_t) &length, fmt, ap);
va_end(ap);
return length;
}
#endif
int majority(int n, int *votes)
{
int i, ayes = 0, nos = 0;
for (i = 0; i < n; i++)
if (votes[i])
ayes++;
else
nos++;
if (ayes > nos)
return 1;
else
return 0;
}