diff --git a/test_programs/.gitignore b/test_programs/.gitignore
index 0a0178a..f7e49da 100644
--- a/test_programs/.gitignore
+++ b/test_programs/.gitignore
@@ -1,4 +1,4 @@
# Ignoring dump files
*.dump
*.o
-./target
\ No newline at end of file
+target
\ No newline at end of file
diff --git a/test_programs/Makefile b/test_programs/Makefile
index 662a888..ad2c039 100644
--- a/test_programs/Makefile
+++ b/test_programs/Makefile
@@ -6,8 +6,16 @@ include $(TOPDIR)/Makefile.config
#
dumps:
$(MAKE) dumps -C riscv_instructions/
- mkdir -p ${TOPDIR}/target
- find . -name '*.dump' -exec mv {} ${TOPDIR}/target \;
+ mkdir -p ${TOPDIR}/target/dumps/
+ find . -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \;
+
+user_lib:
+ $(MAKE) -C userlib/
+
+tests: user_lib
+ $(MAKE) tests -C riscv_instructions/
+ mkdir -p ${TOPDIR}/target/guac/
+ find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ \;
clean:
rm -rf $(TOPDIR)/target
\ No newline at end of file
diff --git a/test_programs/Makefile.tests b/test_programs/Makefile.tests
new file mode 100644
index 0000000..681d31f
--- /dev/null
+++ b/test_programs/Makefile.tests
@@ -0,0 +1,38 @@
+include $(TOPDIR)/Makefile.config
+USERLIB = $(TOPDIR)/userlib
+INCPATH += -I$(TOPDIR) -I$(USERLIB)
+LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
+ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
+CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
+
+# Rules
+%.o: %.s
+ $(RISCV_AS) $(ASFLAGS) -c $<
+
+%.o: %.c
+ $(RISCV_GCC) $(CFLAGS) -c $<
+
+%.dump: %.o
+ $(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
+
+%.guac: %.o
+ $(RISCV_LD) $(LDFLAGS) $+ -o $@
+
+# Dependencies
+.%.d: %.s
+ @echo Generating dependencies for $<
+ @$(SHELL) -ec '$(GCC) -x assembler-with-cpp -M $(ASFLAGS) $< \
+ | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
+ [ -s $@ ] || rm -f $@'
+
+.%.d: %.c
+ @echo Generating dependencies for $<
+ @$(SHELL) -ec '$(GCC) -M $(CFLAGS) $< \
+ | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
+ [ -s $@ ] || rm -f $@'
+
+# Targets
+#clean:
+# rm -rf *.o 2> /dev/null
+# rm -rf *.dump 2> /dev/null
+# rm -rf *.guac 2> /dev/null
\ No newline at end of file
diff --git a/test_programs/riscv_instructions/Makefile b/test_programs/riscv_instructions/Makefile
index c134d5f..22a1303 100644
--- a/test_programs/riscv_instructions/Makefile
+++ b/test_programs/riscv_instructions/Makefile
@@ -1,4 +1,9 @@
dumps:
make dumps -C boolean_logic/
make dumps -C jump_instructions/
- make dumps -C simple_arithmetics/
\ No newline at end of file
+ make dumps -C simple_arithmetics/
+
+tests:
+ make tests -C boolean_logic/
+ make tests -C jump_instructions/
+ make tests -C simple_arithmetics/
\ No newline at end of file
diff --git a/test_programs/riscv_instructions/boolean_logic/Makefile b/test_programs/riscv_instructions/boolean_logic/Makefile
index 2150f14..5f738e9 100644
--- a/test_programs/riscv_instructions/boolean_logic/Makefile
+++ b/test_programs/riscv_instructions/boolean_logic/Makefile
@@ -1,4 +1,9 @@
TOPDIR = ../..
-include $(TOPDIR)/Makefile.dumps
+include $(TOPDIR)/Makefile.tests
-dumps: comparisons.dump if.dump switch.dump
\ No newline at end of file
+dumps: comparisons.dump if.dump switch.dump
+
+tests: comparisons.guac if.guac switch.guac
+
+# Dependances
+$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
\ No newline at end of file
diff --git a/test_programs/riscv_instructions/jump_instructions/Makefile b/test_programs/riscv_instructions/jump_instructions/Makefile
index 75b887b..ce69447 100644
--- a/test_programs/riscv_instructions/jump_instructions/Makefile
+++ b/test_programs/riscv_instructions/jump_instructions/Makefile
@@ -1,4 +1,6 @@
TOPDIR = ../..
-include $(TOPDIR)/Makefile.dumps
+include $(TOPDIR)/Makefile.tests
-dumps: jump.dump ret.dump
\ No newline at end of file
+dumps: jump.dump ret.dump
+
+tests: jump.guac ret.guac
\ No newline at end of file
diff --git a/test_programs/riscv_instructions/simple_arithmetics/Makefile b/test_programs/riscv_instructions/simple_arithmetics/Makefile
index d775b97..623b297 100644
--- a/test_programs/riscv_instructions/simple_arithmetics/Makefile
+++ b/test_programs/riscv_instructions/simple_arithmetics/Makefile
@@ -1,4 +1,6 @@
TOPDIR = ../..
-include $(TOPDIR)/Makefile.dumps
+include $(TOPDIR)/Makefile.tests
-dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
\ No newline at end of file
+dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
+
+tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
\ No newline at end of file
diff --git a/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c b/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c
index 05a7829..f1c005a 100644
--- a/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c
+++ b/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c
@@ -1,3 +1,6 @@
+#include "userlib/syscall.h"
+#include "userlib/libnachos.h"
+
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1
int main() {
unsigned int x = 0;
diff --git a/test_programs/userlib/Makefile b/test_programs/userlib/Makefile
new file mode 100644
index 0000000..903f3b5
--- /dev/null
+++ b/test_programs/userlib/Makefile
@@ -0,0 +1,4 @@
+TOPDIR = ../
+include $(TOPDIR)/Makefile.tests
+
+default: sys.o libnachos.o
\ No newline at end of file
diff --git a/test_programs/userlib/ldscript.lds b/test_programs/userlib/ldscript.lds
new file mode 100644
index 0000000..7bc9ebf
--- /dev/null
+++ b/test_programs/userlib/ldscript.lds
@@ -0,0 +1,61 @@
+/*
+ ldscript for running user programs under Nachos
+
+ Sections should be aligned on page boundaries. Here an alignement of
+ at least 0x2000 is selected, thus supporting pages up to 8KB
+ large. See addrspace.cc for details.
+*/
+
+ENTRY(__start)
+SECTIONS
+{
+
+ /* Skip an area of about 8k, so that NULL pointer dereferences can
+ be detected */
+ . += 0x2000;
+
+ .sys ALIGN(0x4000) : {
+ *(.init)
+ *(.sys)
+ }
+
+ /* Code is aligned on a 16k boundary
+ Due to the size of the .sys section, the code start address will
+ presumably be at address 0x4000 */
+ .text ALIGN(0x400000) : {
+ _ftext = .;
+ eprol = .;
+ *(.text)
+ *(.fini)
+ }
+ etext = .;
+ _etext = .;
+
+ /* Initialized data is aligned on a 16k boundary */
+ .data ALIGN(0x4000) : {
+ _fdata = .;
+ *(.data)
+ *(.sdata)
+ }
+ .rodata ALIGN(0x4000) :
+ {
+ *(.rdata)
+ *(.srodata)
+ *(.rodata)
+ }
+ edata = .;
+ _edata = .;
+
+ /* Non-initialized data is aligned on a 16k boundary */
+ /* Bss = Block Started by Symbol */
+ .bss ALIGN(0x4000) : {
+ *(.bss)
+ *(.sbss)
+ *(.scommon)
+ *(COMMON)
+ }
+
+ end = .;
+ _end = .;
+
+}
diff --git a/test_programs/userlib/libnachos.c b/test_programs/userlib/libnachos.c
new file mode 100644
index 0000000..32bb273
--- /dev/null
+++ b/test_programs/userlib/libnachos.c
@@ -0,0 +1,630 @@
+/*! \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 ).
+ * -----------------------------------------------------
+ */
+
+#include "libnachos.h"
+#include
+#include
+
+//----------------------------------------------------------------------
+// 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;
+ }
+ 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)&&(ic2[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> 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);
+}
diff --git a/test_programs/userlib/libnachos.h b/test_programs/userlib/libnachos.h
new file mode 100644
index 0000000..f033ed5
--- /dev/null
+++ b/test_programs/userlib/libnachos.h
@@ -0,0 +1,87 @@
+/*! \file libnachos.h
+ \brief Function structures for programs
+
+ Libnachos proposes several 'libc-like' functions
+ for:
+ Input-Output operations,
+ String operations,
+ Memory operations,
+ System calls are defined in kernel/syscalls.h
+
+ Nachos-libc functions are prefixed by 'n' to avoid
+ any confusion with standard libc functions.
+
+ * -----------------------------------------------------
+ * 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 ).
+ * -----------------------------------------------------
+*/
+
+#include "userlib/syscall.h"
+
+typedef void (*VoidNoArgFunctionPtr)();
+typedef unsigned int size_t;
+
+// Thread management
+// ----------------------------
+ThreadId threadCreate(char * debug_name, VoidNoArgFunctionPtr func);
+
+// Input/Output operations :
+// ------------------------------------
+
+// Print on the standard output specified parameters.
+void n_printf(const char *format, ...);
+
+// Format (of max length ) according to the format
+int n_snprintf(char * buff, int len, const char *format, ...);
+
+// Read an integer on the standard input
+int n_read_int(void);
+
+// String operations :
+// -------------------
+
+// Compare two strings byte by byte.
+int n_strcmp(const char *s1, const char *s2);
+
+// Copy a string.
+char* n_strcpy(char *dst, const char *src);
+
+// Return the number of bytes in a string.
+size_t n_strlen(const char *s);
+
+// appends a copy of a string, to the end of another string.
+char* n_strcat(char *dst, const char *src);
+
+// Return a upper-case letter,
+// equivalent to the lower-case letter given.
+int n_toupper(int c);
+
+// Return a lower-case letter,
+// equivalent to the upper-case letter given.
+int n_tolower(int c);
+
+// Convert a string in integer.
+int n_atoi(const char *str);
+
+// Concerning memory area operations :
+// -----------------------------------
+
+// Compare two memory area, looking at the first n bytes .
+int n_memcmp(const void *s1, const void *s2, size_t n);
+
+// Copy n byte from an memory area to another.
+void* n_memcpy(void *s1, const void *s2, size_t n);
+
+// Set the first n bytes in a memory area to a specified value.
+void* n_memset(void *s, int c, size_t n);
diff --git a/test_programs/userlib/sys.s b/test_programs/userlib/sys.s
new file mode 100644
index 0000000..64c3069
--- /dev/null
+++ b/test_programs/userlib/sys.s
@@ -0,0 +1,343 @@
+/* Start.s
+ * Assembly language assist for user programs running on top of Nachos.
+ *
+ * Since we don't want to pull in the entire C library, we define
+ * what we need for a user program here, namely Start and the system
+ * calls.
+ *
+ * -----------------------------------------------------
+ * This file is part of the BurritOS 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 ).
+ * -----------------------------------------------------
+ */
+
+#define IN_ASM
+#include "userlib/syscall.h"
+
+ // Equivalent to ".text", but with a different name, in order
+ // to be correctly handled by the ldscript
+ .section .sys,"ax",@progbits
+
+ .align 2
+
+/* -------------------------------------------------------------
+ * __start
+ * Initialize running a C program, by calling "main".
+ *
+ * NOTE: This has to be first, so that it gets loaded at location 0.
+ * The Nachos kernel always starts a program by jumping to location 0.
+ * -------------------------------------------------------------
+ */
+
+ .globl __start
+ .type __start, @function
+__start:
+
+/* Call the program entry point */
+ call main
+ li a0, 0
+ call Exit
+ jr ra /* if we return from main, exit(0) */
+
+
+/* -------------------------------------------------------------
+ * System call stubs:
+ * Assembly language assist to make system calls to the Nachos kernel.
+ * There is one stub per system call, that places the code for the
+ * system call into register r10, and leaves the arguments to the
+ * system call alone (in other words, arg1 is in r12, arg2 is
+ * in r13, arg3 is in r14, arg4 is in r15)
+ *
+ * The return value is in r10. This follows the standard C calling
+ * convention on the RISC-V.
+ * -------------------------------------------------------------
+ */
+
+ .globl Halt
+ .type __Halt, @function
+Halt:
+ addi a7,zero,SC_HALT
+ ecall
+ jr ra
+
+
+ .globl Exit
+ .type __Exit, @function
+Exit:
+ addi a7,zero,SC_EXIT
+ ecall
+ jr ra
+
+
+ .globl Exec
+ .type __Exec, @function
+Exec:
+ addi a7,zero,SC_EXEC
+ ecall
+ jr ra
+
+
+ .globl Join
+ .type __Join, @function
+Join:
+ addi a7,zero,SC_JOIN
+ ecall
+ jr ra
+
+
+ .globl Create
+ .type __Create, @function
+Create:
+ addi a7,zero,SC_CREATE
+ ecall
+ jr ra
+
+
+ .globl Open
+ .type __Open, @function
+Open:
+ addi a7,zero,SC_OPEN
+ ecall
+ jr ra
+
+
+ .globl Read
+ .type __Read, @function
+Read:
+ addi a7,zero,SC_READ
+ ecall
+ jr ra
+
+
+
+ .globl Write
+ .type __Write, @function
+Write:
+ addi a7,zero,SC_WRITE
+ ecall
+ jr ra
+
+
+ .globl Seek
+ .type __Seek, @function
+Seek:
+ addi a7,zero,SC_SEEK
+ ecall
+ jr ra
+
+
+ .globl Close
+ .type __Close, @function
+Close:
+ addi a7,zero,SC_CLOSE
+ ecall
+ jr ra
+
+
+ .globl FSList
+ .type __FSList, @function
+FSList:
+ addi a7,zero,SC_FSLIST
+ ecall
+ jr ra
+
+
+ .globl newThread
+ .type __newThread, @function
+newThread:
+ addi a7,zero,SC_NEW_THREAD
+ ecall
+ jr ra
+
+
+ .globl Remove
+ .type __Remove, @function
+Remove:
+ addi a7,zero,SC_REMOVE
+ ecall
+ jr ra
+
+
+ .globl Yield
+ .type __Yield, @function
+Yield:
+ addi a7,zero,SC_YIELD
+ ecall
+ jr ra
+
+
+ .globl PError
+ .type __PError, @function
+PError:
+ addi a7,zero,SC_PERROR
+ ecall
+ jr ra
+
+
+ .globl P
+ .type __P, @function
+P:
+ addi a7,zero,SC_P
+ ecall
+ jr ra
+
+
+ .globl V
+ .type __V, @function
+V:
+ addi a7,zero,SC_V
+ ecall
+ jr ra
+
+ .globl SemCreate
+ .type __SemCreate, @function
+SemCreate:
+ addi a7,zero,SC_SEM_CREATE
+ ecall
+ jr ra
+
+
+ .globl SemDestroy
+ .type __SemDestroy, @function
+SemDestroy:
+ addi a7,zero,SC_SEM_DESTROY
+ ecall
+ jr ra
+
+
+ .globl SysTime
+ .type __SysTime, @function
+SysTime:
+ addi a7,zero,SC_SYS_TIME
+ ecall
+ jr ra
+
+
+ .globl LockCreate
+ .type __LockCreate, @function
+LockCreate:
+ addi a7,zero,SC_LOCK_CREATE
+ ecall
+ jr ra
+
+ .globl LockDestroy
+ .type __LockDestroy, @function
+LockDestroy:
+ addi a7,zero,SC_LOCK_DESTROY
+ ecall
+ jr ra
+
+
+ .globl LockAcquire
+ .type __LockAquire, @function
+LockAcquire:
+ addi a7,zero,SC_LOCK_ACQUIRE
+ ecall
+ jr ra
+
+
+ .globl LockRelease
+ .type __LockRelease, @function
+LockRelease:
+ addi a7,zero,SC_LOCK_RELEASE
+ ecall
+ jr ra
+
+
+ .globl CondCreate
+ .type __CondCreate, @function
+CondCreate:
+ addi a7,zero,SC_COND_CREATE
+ ecall
+ jr ra
+
+
+ .globl CondDestroy
+ .type __CondDestroy, @function
+CondDestroy:
+ addi a7,zero,SC_COND_DESTROY
+ ecall
+ jr ra
+
+
+ .globl CondWait
+ .type __CondWait, @function
+CondWait:
+ addi a7,zero,SC_COND_WAIT
+ ecall
+ jr ra
+
+
+ .globl CondSignal
+ .type __CondSignal, @function
+CondSignal:
+ addi a7,zero,SC_COND_SIGNAL
+ ecall
+ jr ra
+
+
+ .globl CondBroadcast
+ .type __CondBroadcast, @function
+CondBroadcast:
+ addi a7,zero,SC_COND_BROADCAST
+ ecall
+ jr ra
+
+
+ .globl TtySend
+ .type __TtySend, @function
+TtySend:
+ addi a7,zero,SC_TTY_SEND
+ ecall
+ jr ra
+
+
+ .globl TtyReceive
+ .type __TtyReceive, @function
+TtyReceive:
+ addi a7,zero,SC_TTY_RECEIVE
+ ecall
+ jr ra
+
+
+ .globl Mkdir
+ .type __Mkdir, @function
+Mkdir:
+ addi a7,zero,SC_MKDIR
+ ecall
+ jr ra
+
+
+ .globl Rmdir
+ .type __Rmdir, @function
+Rmdir:
+ addi a7,zero,SC_RMDIR
+ ecall
+ jr ra
+
+
+ .globl Mmap
+ .type __Mmap, @function
+Mmap:
+ addi a7,zero,SC_MMAP
+ ecall
+ jr ra
+
+ .globl Debug
+ .type __Debug, @function
+Debug:
+ addi a7,zero,SC_DEBUG
+ ecall
+ jr ra
+
+
diff --git a/test_programs/userlib/syscall.h b/test_programs/userlib/syscall.h
new file mode 100644
index 0000000..e650db1
--- /dev/null
+++ b/test_programs/userlib/syscall.h
@@ -0,0 +1,287 @@
+/*! \file syscall.h
+ \brief Nachos system call interface.
+
+ These are Nachos kernel operations
+ that can be invoked from user programs, by trapping to the kernel
+ via the "syscall" instruction.
+
+ This file is included by user programs and by the Nachos kernel.
+
+ Each of these is invoked by a user program by simply calling the
+ procedure; an assembly language stub stuffs the system call code
+ into a register, and traps to the kernel. The kernel procedures
+ are then invoked in the Nachos kernel, after appropriate error checking,
+ from the system call entry point in exception.cc.
+
+ * -----------------------------------------------------
+ * 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 ).
+ * -----------------------------------------------------
+ */
+
+
+#ifndef SYSCALLS_H
+#define SYSCALLS_H
+
+//#include "kernel/copyright.h"
+
+/* system call codes -- used by the stubs to tell the kernel which system call
+ * is being asked for
+ */
+#define SC_HALT 0
+#define SC_EXIT 1
+#define SC_EXEC 2
+#define SC_JOIN 3
+#define SC_CREATE 4
+#define SC_OPEN 5
+#define SC_READ 6
+#define SC_WRITE 7
+#define SC_SEEK 8
+#define SC_CLOSE 9
+#define SC_NEW_THREAD 10
+#define SC_YIELD 11
+#define SC_PERROR 12
+#define SC_P 13
+#define SC_V 14
+#define SC_SEM_CREATE 15
+#define SC_SEM_DESTROY 16
+#define SC_LOCK_CREATE 17
+#define SC_LOCK_DESTROY 18
+#define SC_LOCK_ACQUIRE 19
+#define SC_LOCK_RELEASE 20
+#define SC_COND_CREATE 21
+#define SC_COND_DESTROY 22
+#define SC_COND_WAIT 23
+#define SC_COND_SIGNAL 24
+#define SC_COND_BROADCAST 25
+#define SC_TTY_SEND 26
+#define SC_TTY_RECEIVE 27
+#define SC_MKDIR 28
+#define SC_RMDIR 29
+#define SC_REMOVE 30
+#define SC_FSLIST 31
+#define SC_SYS_TIME 32
+#define SC_MMAP 33
+#define SC_DEBUG 34
+
+#ifndef IN_ASM
+
+/* The system call interface. These are the operations the Nachos
+ * kernel needs to support, to be able to run user programs.
+ *
+ */
+
+typedef int t_error;
+
+/* Stop Nachos, and print out performance stats */
+void Halt();
+
+
+/* Return the time spent running Nachos */
+
+/*! \brief Defines the Nachos basic time unit */
+typedef struct {
+ long seconds;
+ long nanos;
+} Nachos_Time;
+void SysTime(Nachos_Time *t);
+
+/* Address space control operations: Exit, Exec, and Join */
+
+/* This user program is done (status = 0 means exited normally). */
+void Exit(int status);
+
+/* A unique identifier for a thread executed within a user program */
+typedef unsigned long ThreadId;
+
+/* Run the executable, stored in the Nachos file "name", and return the
+ * master thread identifier
+ */
+ThreadId Exec(char *name);
+
+/* Create a new thread in the current process
+ * Return thread identifier
+ */
+ThreadId newThread(char * debug_name, int func, int arg);
+
+/* Only return once the the thread "id" has finished.
+ */
+t_error Join(ThreadId id);
+
+/* Yield the CPU to another runnable thread, whether in this address space
+ * or not.
+ */
+void Yield();
+
+/*! Print the last error message with the personalized one "mess" */
+void PError(char *mess);
+
+/* File system operations: Create, Open, Read, Write, Seek, Close
+ * These functions are patterned after UNIX -- files represent
+ * both files *and* hardware I/O devices.
+ *
+ * If this assignment is done before doing the file system assignment,
+ * note that the Nachos file system has a stub implementation, which
+ * will work for the purposes of testing out these routines.
+ */
+
+/* A unique identifier for an open Nachos file. */
+typedef unsigned long OpenFileId;
+
+/* when an address space starts up, it has two open files, representing
+ * keyboard input and display output (in UNIX terms, stdin and stdout).
+ * Read and Write can be used directly on these, without first opening
+ * the console device.
+ */
+#define CONSOLE_INPUT 0
+#define CONSOLE_OUTPUT 1
+
+/* Create a Nachos file, with "name" */
+t_error Create(char *name,int size);
+
+/* Open the Nachos file "name", and return an "OpenFileId" that can
+ * be used to read and write to the file.
+ */
+OpenFileId Open(char *name);
+
+/* Write "size" bytes from "buffer" to the open file. */
+t_error Write(char *buffer, int size, OpenFileId id);
+
+/* Read "size" bytes from the open file into "buffer".
+ * Return the number of bytes actually read -- if the open file isn't
+ * long enough, or if it is an I/O device, and there aren't enough
+ * characters to read, return whatever is available (for I/O devices,
+ * you should always wait until you can return at least one character).
+ */
+t_error Read(char *buffer, int size, OpenFileId id);
+
+/* Seek to a specified offset into an opened file */
+t_error Seek(int offset, OpenFileId id);
+
+#ifndef SYSDEP_H
+/* Close the file, we're done reading and writing to it. */
+t_error Close(OpenFileId id);
+#endif // SYSDEP_H
+
+/* Remove the file */
+t_error Remove(char* name);
+
+/******************************************************************/
+/* system calls concerning directory management */
+
+/* Create a new repertory
+ Return a negative number if an error ocurred.
+*/
+t_error Mkdir(char* name);
+
+/* Destroy a repertory, which must be empty.
+ Return a negative number if an error ocurred.
+*/
+t_error Rmdir(char* name);
+
+/* List the content of NachOS FileSystem */
+t_error FSList();
+
+/******************************************************************/
+/* User-level synchronization operations : */
+
+/* System calls concerning semaphores management */
+
+typedef unsigned long SemId;
+
+/* Create a semaphore, initialising it at count.
+ Return a Semid, which will enable to do operations on this
+ semaphore */
+SemId SemCreate(char * debug_name, int count);
+
+/* Destroy a semaphore identified by sema.
+ Return a negative number if an error occured during the destruction */
+t_error SemDestroy(SemId sema);
+
+/* Do the operation P() on the semaphore sema */
+t_error P(SemId sema);
+
+/* Do the operation V() on the semaphore sema */
+t_error V(SemId sema);
+
+/* System calls concerning locks management */
+typedef unsigned long LockId;
+
+/* Create a lock.
+ Return an identifier */
+LockId LockCreate(char * debug_name);
+
+/* Destroy a lock.
+ Return a negative number if an error ocurred
+ during the destruction. */
+t_error LockDestroy(LockId id);
+
+/* Do the operation Acquire on the lock id.
+ Return a negative number if an error ocurred. */
+t_error LockAcquire(LockId id);
+
+/* Do the operation Release on the lock id.
+ Return a negative number if an error ocurred.
+*/
+t_error LockRelease(LockId id);
+
+/* System calls concerning conditions variables. */
+typedef unsigned long CondId;
+
+/* Create a new condition variable */
+CondId CondCreate(char * debug_name);
+
+/* Destroy a condition variable.
+ Return a negative number if an error ocurred.
+*/
+t_error CondDestroy(CondId id);
+
+/* Do the operation Wait on a condition variable.
+ Returns a negative number if an error ocurred.
+*/
+t_error CondWait(CondId cond);
+
+/* Do the operation Signal on a condition variable (wake up only one thread).
+ Return a negative number if an error ocurred.
+*/
+t_error CondSignal(CondId cond);
+
+/* Do the operation Signal on a condition variable (wake up all threads).
+ Return a negative number if an error ocurred.
+*/
+t_error CondBroadcast(CondId cond);
+
+/******************************************************************/
+/* System calls concerning serial port and console */
+
+/* Send the message on the serial communication link.
+ Returns the number of bytes successfully sent.
+*/
+int TtySend(char *mess);
+
+/* Wait for a message comming from the serial communication link.
+ The length of the buffer where the bytes will be copied is given as a parameter.
+ Returns the number of characters actually received.
+*/
+int TtyReceive(char *mess,int length);
+
+/* Map an opened file in memory. Size is the size to be mapped in bytes.
+*/
+void *Mmap(OpenFileId f, int size);
+
+/* For debug purpose
+*/
+void Debug(int param);
+
+#endif // IN_ASM
+#endif // SYSCALL_H