631 lines
16 KiB
C
631 lines
16 KiB
C
|
/*! \file libnachos.c
|
||
|
* \brief Functions of our library, for user programs.
|
||
|
*
|
||
|
* This library only provides some usefull functions for
|
||
|
* programming.
|
||
|
*
|
||
|
* -----------------------------------------------------
|
||
|
* This file is part of the Nachos-RiscV distribution
|
||
|
* Copyright (c) 2022 University of Rennes 1.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, version 3.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details
|
||
|
* (see see <http://www.gnu.org/licenses/>).
|
||
|
* -----------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
#include "libnachos.h"
|
||
|
#include <stdarg.h>
|
||
|
#include <stdint.h>
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// threadStart()
|
||
|
/*! Makes a thread execute a function or program. This function
|
||
|
// is static, it is called internally by library function threadCreate
|
||
|
// and should not be called directly by user programs. The interest
|
||
|
// of this function is to be able to terminate threads correctly,
|
||
|
// even when the thread to be terminated does not explicitly call
|
||
|
// Exit. threadStart provides the mechanism by which Exit
|
||
|
// is called automatically
|
||
|
//
|
||
|
// \param func is the identificator of the function to execute.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
|
||
|
static void threadStart(uint64_t func)
|
||
|
{
|
||
|
VoidNoArgFunctionPtr func2;
|
||
|
func2=(VoidNoArgFunctionPtr)func;
|
||
|
// Call the function that actually contains the thread code
|
||
|
(*func2)();
|
||
|
// Call exit, such that there is no return using an empty stack
|
||
|
Exit(0);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// threadCreate()
|
||
|
/*! Creates a thread and makes it execute a function.
|
||
|
//
|
||
|
// NB : instead of directly executing the required function,
|
||
|
// function threadStart is called so as to ensure
|
||
|
// that the thread will properly exit
|
||
|
// This function must be called instead of calling directly
|
||
|
// the system call newThread
|
||
|
//
|
||
|
// \param name the name of the thread (for debugging purpose)
|
||
|
// \param func is the address of the function to execute.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
ThreadId threadCreate(char *debug_name, VoidNoArgFunctionPtr func)
|
||
|
{
|
||
|
return newThread(debug_name, (uint64_t)threadStart,(uint64_t)func);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_strcmp()
|
||
|
/*! String comparison
|
||
|
//
|
||
|
// \param s1 is the first string,
|
||
|
// \param s2 is the second one.
|
||
|
// \return an integer greater than, equal to, or less than 0,
|
||
|
// if the first string is greater than, equal to, or less than
|
||
|
// the the second string.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_strcmp(const char *s1, const char *s2)
|
||
|
{
|
||
|
int comparaison;
|
||
|
int fini=0;
|
||
|
int i=0;
|
||
|
while(!fini) {
|
||
|
if ((s1[i]==0)&&(s2[i]==0)) {
|
||
|
fini=1;
|
||
|
comparaison=0;
|
||
|
}
|
||
|
if (s1[i]<s2[i]) {
|
||
|
fini=1;
|
||
|
comparaison=-1;
|
||
|
}
|
||
|
if(s1[i]>s2[i]) {
|
||
|
fini=1;
|
||
|
comparaison=1;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
return comparaison;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_strcpy()
|
||
|
/*! String copy
|
||
|
//
|
||
|
// \param dst is where the string is to be copied,
|
||
|
// \param src is where the string to copy is.
|
||
|
// \return dst, if the copy successes, 0 otherwise
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
char *n_strcpy(char *dst, const char *src)
|
||
|
{
|
||
|
int i=0;
|
||
|
int fini=0;
|
||
|
if ((dst!=0)&&(src!=0)) {
|
||
|
while(fini==0) {
|
||
|
if(src[i]=='\0') fini=1;
|
||
|
dst[i]=src[i];
|
||
|
i++;
|
||
|
}
|
||
|
return dst;
|
||
|
}
|
||
|
else return 0;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_strlen()
|
||
|
/*! Gives the number of bytes in a string, not including the
|
||
|
// terminating null character.
|
||
|
//
|
||
|
// \param c is a pointer onto a string.
|
||
|
// \return the length of the string.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
size_t n_strlen(const char *s)
|
||
|
{
|
||
|
size_t i=0;
|
||
|
while (s[i] != 0) i++;
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_strcat()
|
||
|
/*! Appends a copy of a string, including null character, to the end
|
||
|
// of another string. Enough memory has to be available in the
|
||
|
// destination string.
|
||
|
//
|
||
|
// \param dst is a pointer onto the string where the other string
|
||
|
// will be appended.
|
||
|
// \param src is the string to append.
|
||
|
// \return the pointer string dst.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
char *n_strcat(char *dst, const char *src)
|
||
|
{
|
||
|
int i,j,k;
|
||
|
i=(int)n_strlen(dst);
|
||
|
j=(int)n_strlen(src);
|
||
|
for(k=i;k<=j+i;k++) {
|
||
|
dst[k]=src[k-i];
|
||
|
}
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_toupper()
|
||
|
/*! Gives the upper-case letter corresponding to the lower-case
|
||
|
// letter passed as parameter.
|
||
|
//
|
||
|
// \param c is the ASCII code of the letter to transform.
|
||
|
// \return the corresponding upper-case letter
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_toupper(int c)
|
||
|
{
|
||
|
if((c>='a')&&(c<='z'))
|
||
|
return c+('A'-'a');
|
||
|
else return c;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_tolower()
|
||
|
/*! Gives the lower-case letter corresponding to the upper-case
|
||
|
// letter passed as parameter
|
||
|
//
|
||
|
// \param c is the ASCII code of the letter to transform.
|
||
|
// \return the corresponding lower-case letter
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_tolower(int c)
|
||
|
{
|
||
|
if((c<='Z')&&(c>='A'))
|
||
|
return c+('a'-'A');
|
||
|
else return c;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_atoi()
|
||
|
/*! String to integer conversion.
|
||
|
//
|
||
|
// \param c is a pointer onto a string.
|
||
|
// \return the corresponding value
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_atoi(const char *str)
|
||
|
{
|
||
|
int i=0;
|
||
|
int fini=0;
|
||
|
int val=0;
|
||
|
int negative = 0;
|
||
|
if (str[i] == '-') {
|
||
|
negative = 1; i=1;
|
||
|
}
|
||
|
while(!fini)
|
||
|
{
|
||
|
if(str[i]==0 || str[i]<'0' || str[i]>'9')
|
||
|
fini=1;
|
||
|
else
|
||
|
{
|
||
|
val*=10;
|
||
|
val+=str[i]-'0';
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
if (negative) return(-val); else return val;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_memcmp()
|
||
|
/*! Memory comparison.
|
||
|
//
|
||
|
// \param s1 is the first memory area,
|
||
|
// \param s2 is the second memory area.
|
||
|
// \param n size in bytes of the area to be compared.
|
||
|
// \return an integer less than, equal to, or greater than 0,
|
||
|
// according as s1 is lexicographically less than, equal to,
|
||
|
// or greater than s2 when taken to be unsigned characters.
|
||
|
//
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_memcmp(const void *s1, const void *s2, size_t n)
|
||
|
{
|
||
|
unsigned char* c1=(unsigned char*)s1;
|
||
|
unsigned char* c2=(unsigned char*)s2;
|
||
|
|
||
|
int comparaison=0;
|
||
|
int fini=0;
|
||
|
int i=0;
|
||
|
while ((!fini)&&(i<n)) {
|
||
|
if (c1[i]<c2[i]) {
|
||
|
fini=1;
|
||
|
comparaison=-1;
|
||
|
}
|
||
|
if (c1[i]>c2[i]) {
|
||
|
fini=1;
|
||
|
comparaison=1;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
return comparaison;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_memcpy()
|
||
|
/*! Memory copy.
|
||
|
//
|
||
|
// \param s1 is where the elements are to be copied,
|
||
|
// \param s2 is the memory area to copy.
|
||
|
// \param n size in bytes of the area to be copied.
|
||
|
// \return the memory area where the copy has been done.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
void *n_memcpy(void *s1, const void *s2, size_t n)
|
||
|
{
|
||
|
|
||
|
unsigned char* c1=(unsigned char*)s1;
|
||
|
unsigned char* c2=(unsigned char*)s2;
|
||
|
|
||
|
int i=0;
|
||
|
if ((c1!=0)&&(c2!=0)) {
|
||
|
while(i<n) {
|
||
|
c1[i]=c2[i];
|
||
|
i++;
|
||
|
}
|
||
|
return (void *)c1;
|
||
|
}
|
||
|
else return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_memset()
|
||
|
/*! Sets the first n bytes of a memory area to a value (converted to
|
||
|
// an unsigned char).
|
||
|
//
|
||
|
// \param s is the memory area to transform,
|
||
|
// \param c is the value wanted,
|
||
|
// \param n is the number of bytes to put at c.
|
||
|
// \return s.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
void *n_memset(void *s, int c, size_t n)
|
||
|
{
|
||
|
unsigned char* c1=(unsigned char*)s;
|
||
|
int i;
|
||
|
for (i=0;i<n;i++) {
|
||
|
c1[i]=c;
|
||
|
}
|
||
|
return (void *)c1;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_dumpmem()
|
||
|
/*! Dumps on the string the n first bytes of a memory area
|
||
|
// (used for debugging)
|
||
|
//
|
||
|
// \param addr address of the memory area
|
||
|
// \param len number of bytes to be dumped
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
void n_dumpmem(char *addr, int len)
|
||
|
{
|
||
|
#define TOHEX(x) \
|
||
|
({ char __x = (x); if(__x < 10) __x+='0'; else __x='a'+(__x-10) ; __x; })
|
||
|
|
||
|
int i;
|
||
|
for (i = 0 ; i < len ; i++) {
|
||
|
char s[3];
|
||
|
if ((i%16) == 0)
|
||
|
n_printf("%x\t", (unsigned long)&addr[i]);
|
||
|
else if ((i%8) == 0)
|
||
|
n_printf(" ");
|
||
|
s[0] = TOHEX((addr[i] >> 4) & 0xf);
|
||
|
s[1] = TOHEX(addr[i] & 0xf);
|
||
|
s[2] = '\0';
|
||
|
n_printf("%s ", s);
|
||
|
if ((((i+1)%16) == 0) || (i == len-1))
|
||
|
n_printf("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define PUTCHAR(carac) \
|
||
|
do { \
|
||
|
if (result < len-1) *buff++ = carac;\
|
||
|
result++; \
|
||
|
} while (0)
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_vsnprintf()
|
||
|
/*! Build a string according to a specified format (internal function)
|
||
|
//
|
||
|
// Nachos vsnprintf accepts:
|
||
|
// %c to print a character,
|
||
|
// %s, to print a string,
|
||
|
// %d, to print an integer,
|
||
|
// %x, to print an integer in hexa
|
||
|
// %lx %ld same for 64-bit values
|
||
|
// %f, to print a floating point value
|
||
|
//
|
||
|
// \param buff the destination buffer to generate the string to
|
||
|
// \param len the size of buff, determines the number max of
|
||
|
// characters copied to buff (taking the final \0 into account)
|
||
|
// \param format the string to parse
|
||
|
// \param ap parameters to print
|
||
|
//
|
||
|
// \return the number of characters formatted (NOT including \0),
|
||
|
// that is, the number of characters that would have been written
|
||
|
// to the buffer if it were large enough. -1 on error.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
static int n_vsnprintf(char *buff, int len, const char *format, va_list ap)
|
||
|
{
|
||
|
int i, result;
|
||
|
|
||
|
if (!buff || !format || (len < 0)) {
|
||
|
return -1;
|
||
|
}
|
||
|
result = 0;
|
||
|
|
||
|
for (i=0 ; format[i] != '\0' ; i++) {
|
||
|
switch (format[i]) {
|
||
|
case '%':
|
||
|
i++;
|
||
|
switch(format[i]) {
|
||
|
case '%': {
|
||
|
PUTCHAR('%');
|
||
|
break;
|
||
|
}
|
||
|
case 'i':
|
||
|
case'd': {
|
||
|
int integer = (int) va_arg(ap,int);
|
||
|
int cpt2 = 0;
|
||
|
char buff_int[11];
|
||
|
if (integer<0) {PUTCHAR('-');
|
||
|
}
|
||
|
do {
|
||
|
int m10 = integer%10;
|
||
|
m10 = (m10 < 0)? -m10:m10;
|
||
|
buff_int[cpt2++]=(char)('0'+ m10);
|
||
|
integer=integer/10;
|
||
|
} while(integer!=0);
|
||
|
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||
|
PUTCHAR(buff_int[cpt2]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case 'l': {
|
||
|
i++;
|
||
|
switch(format[i]) {
|
||
|
case 'd': {
|
||
|
long longer = va_arg(ap,long);
|
||
|
int cpt2 = 0;
|
||
|
char buff_long[20];
|
||
|
if (longer<0) {
|
||
|
PUTCHAR('-');
|
||
|
}
|
||
|
do {
|
||
|
int m10 = longer%10;
|
||
|
m10 = (m10 < 0)? -m10:m10;
|
||
|
buff_long[cpt2++]=(char)('0'+ m10);
|
||
|
longer=longer/10;
|
||
|
} while(longer!=0);
|
||
|
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||
|
PUTCHAR(buff_long[cpt2]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case 'x': {
|
||
|
uint64_t hexa = va_arg(ap,long);
|
||
|
uint64_t nb;
|
||
|
uint32_t i, had_nonzero = 0;
|
||
|
for (i=0 ; i < 16 ; i++) {
|
||
|
nb = (hexa << (i*4));
|
||
|
nb = (nb >> 60);
|
||
|
nb = nb & 0x000000000000000f;
|
||
|
// Skip the leading zeros
|
||
|
if (nb == 0) {
|
||
|
if (had_nonzero) {
|
||
|
PUTCHAR((uint8_t)'0');
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
had_nonzero = 1;
|
||
|
if (nb < 10)
|
||
|
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||
|
else
|
||
|
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||
|
}
|
||
|
}
|
||
|
if (! had_nonzero)
|
||
|
PUTCHAR((uint8_t)'0');
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
PUTCHAR('%');
|
||
|
PUTCHAR('l');
|
||
|
PUTCHAR(format[i]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case 'c': {
|
||
|
int value = va_arg(ap,int);
|
||
|
PUTCHAR((char)value);
|
||
|
break;
|
||
|
}
|
||
|
case 's': {
|
||
|
char *string = va_arg(ap,char *);
|
||
|
if (! string)
|
||
|
string = "(null)";
|
||
|
for( ; *string != '\0' ; string++)
|
||
|
PUTCHAR(*string);
|
||
|
break;
|
||
|
}
|
||
|
case 'x': {
|
||
|
uint32_t hexa = va_arg(ap,int);
|
||
|
uint32_t nb;
|
||
|
uint32_t i, had_nonzero = 0;
|
||
|
for (i=0 ; i < 8 ; i++) {
|
||
|
nb = (hexa << (i*4));
|
||
|
nb = (nb >> 28);
|
||
|
nb = nb & 0x0000000f;
|
||
|
// Skip the leading zeros
|
||
|
if (nb == 0) {
|
||
|
if (had_nonzero)
|
||
|
PUTCHAR((uint8_t)'0');
|
||
|
}
|
||
|
else {
|
||
|
had_nonzero = 1;
|
||
|
if (nb < 10)
|
||
|
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||
|
else
|
||
|
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||
|
}
|
||
|
}
|
||
|
if (! had_nonzero)
|
||
|
PUTCHAR((uint8_t)'0');
|
||
|
break;
|
||
|
}
|
||
|
/*case 'f': {
|
||
|
// Very simple routine to print floats as xxxx.yyyyy
|
||
|
// Not very good (unable to print large numbers)
|
||
|
// If anyone wants to re-write it, feel free ...
|
||
|
double f = (double) va_arg(ap,double);
|
||
|
int cpt2, j;
|
||
|
char buff_float[200];
|
||
|
long ient,idec;
|
||
|
if (f<0) {
|
||
|
PUTCHAR('-');
|
||
|
f = -f;
|
||
|
}
|
||
|
ient = (int)f;
|
||
|
// 100000 = print 5 digits max
|
||
|
idec = (int)((f - ((double)ient))*100000);
|
||
|
// Round up
|
||
|
if ( f - ((double)ient) - ((double)idec)/100000.0 >= 0.5E-5)
|
||
|
idec ++;
|
||
|
cpt2 = 0;
|
||
|
// Print digits after the '.'
|
||
|
for (j=0 ; j<5 ; j++) {
|
||
|
buff_float[cpt2++]=(char)('0'+(idec%10));
|
||
|
idec=idec/10;
|
||
|
}
|
||
|
buff_float[cpt2++] = '.';
|
||
|
// Print digits before the '.'
|
||
|
do {
|
||
|
buff_float[cpt2++]=(char)('0'+ (ient%10));
|
||
|
ient=ient/10;
|
||
|
} while (ient!=0);
|
||
|
for(j = cpt2 - 1 ; j >= 0 ; j--)
|
||
|
PUTCHAR(buff_float[j]);
|
||
|
break;
|
||
|
}
|
||
|
*/
|
||
|
default:
|
||
|
PUTCHAR('%');
|
||
|
PUTCHAR(format[i]);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
PUTCHAR(format[i]);
|
||
|
}
|
||
|
}
|
||
|
*buff = '\0';
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_snprintf()
|
||
|
/*! Build a string according to a specified format
|
||
|
//
|
||
|
// Nachos snprintf accepts:
|
||
|
// %c to print a character,
|
||
|
// %s, to print a string,
|
||
|
// %d, to print an integer,
|
||
|
// %x, to print a string in hexa
|
||
|
// %f, to print a floating point value
|
||
|
//
|
||
|
// \param buff the destination buffer to generate the string to
|
||
|
// \param len the size of buff, determines the number max of
|
||
|
// characters copied to buff (taking the final \0 into account)
|
||
|
// \param format the string to parse
|
||
|
// \param ... the (variable number of) arguments
|
||
|
//
|
||
|
// \return the number of characters formatted (NOT including \0),
|
||
|
// that is, the number of characters that would have been written
|
||
|
// to the buffer if it were large enough. -1 on error.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_snprintf(char * buff, int len, const char *format, ...){
|
||
|
va_list ap;
|
||
|
va_start(ap, format);
|
||
|
len = n_vsnprintf(buff, len, format, ap);
|
||
|
va_end(ap);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_printf()
|
||
|
/*! Print to the standard output parameters.
|
||
|
//
|
||
|
// Nachos printf accepts:
|
||
|
// %c to print a character,
|
||
|
// %s, to print a string,
|
||
|
// %d, to print an integer,
|
||
|
// %x, to print a string in hexa
|
||
|
// %ld, %lx, same for 64-bit values
|
||
|
// %f, to print a floating point value
|
||
|
//
|
||
|
// \param parameters to print,
|
||
|
// \param type of print.
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
void n_printf(const char *format, ...){
|
||
|
|
||
|
va_list ap;
|
||
|
char buff[200];
|
||
|
int len;
|
||
|
|
||
|
va_start(ap, format);
|
||
|
len = n_vsnprintf(buff, sizeof(buff), format, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
if (len >= sizeof(buff)) {
|
||
|
len = sizeof(buff) - 1;
|
||
|
}
|
||
|
if (len > 0) {
|
||
|
Write(buff,len,CONSOLE_OUTPUT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
// n_read_int()
|
||
|
/*!
|
||
|
// Very basic minimalist read integer function, no error
|
||
|
// checking...
|
||
|
*/
|
||
|
//----------------------------------------------------------------------
|
||
|
int n_read_int(void) {
|
||
|
char buff[200];
|
||
|
Read(buff,200,CONSOLE_INPUT);
|
||
|
return n_atoi(buff);
|
||
|
}
|