summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-03-25 17:36:52 -0400
committerFreya Murphy <freya@freyacat.org>2025-03-25 17:38:22 -0400
commit6af21e6a4f2251e71353562d5df7f376fdffc270 (patch)
treede20c7afc9878422c81e34f30c6b010075e9e69a /lib
downloadcomus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.gz
comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.bz2
comus-6af21e6a4f2251e71353562d5df7f376fdffc270.zip
initial checkout from wrc
Diffstat (limited to 'lib')
-rw-r--r--lib/Make.mk73
-rw-r--r--lib/bound.c37
-rw-r--r--lib/cvtdec.c43
-rw-r--r--lib/cvtdec0.c44
-rw-r--r--lib/cvthex.c49
-rw-r--r--lib/cvtoct.c54
-rw-r--r--lib/cvtuns.c37
-rw-r--r--lib/cvtuns0.c39
-rw-r--r--lib/entry.S25
-rw-r--r--lib/klibc.c112
-rw-r--r--lib/memclr.c37
-rw-r--r--lib/memcpy.c41
-rw-r--r--lib/memset.c38
-rw-r--r--lib/pad.c35
-rw-r--r--lib/padstr.c61
-rw-r--r--lib/sprint.c133
-rw-r--r--lib/str2int.c51
-rw-r--r--lib/strcat.c38
-rw-r--r--lib/strcmp.c32
-rw-r--r--lib/strcpy.c35
-rw-r--r--lib/strlen.c32
-rw-r--r--lib/ulibc.c162
-rw-r--r--lib/ulibs.S93
23 files changed, 1301 insertions, 0 deletions
diff --git a/lib/Make.mk b/lib/Make.mk
new file mode 100644
index 0000000..2f8de2c
--- /dev/null
+++ b/lib/Make.mk
@@ -0,0 +1,73 @@
+#
+# Makefile fragment for the library components of the system.
+#
+# THIS IS NOT A COMPLETE Makefile - run GNU make in the top-level
+# directory, and this will be pulled in automatically.
+#
+
+SUBDIRS += lib
+
+###################
+# FILES SECTION #
+###################
+
+#
+# library file lists
+#
+
+# "common" library functions, used by kernel and users
+CLIB_SRC := lib/bound.c lib/cvtdec.c lib/cvtdec0.c \
+ lib/cvthex.c lib/cvtoct.c lib/cvtuns.c \
+ lib/cvtuns0.c lib/memclr.c lib/memcpy.c \
+ lib/memset.c lib/pad.c lib/padstr.c \
+ lib/sprint.c lib/str2int.c lib/strcat.c \
+ lib/strcmp.c lib/strcpy.c lib/strlen.c
+
+# user-only library functions
+ULIB_SRC := lib/ulibc.c lib/ulibs.S lib/entry.S
+
+# kernel-only library functions
+KLIB_SRC := lib/klibc.c
+
+# lists of object files
+CLIB_OBJ:= $(patsubst lib/%.c, $(BUILDDIR)/lib/%.o, $(CLIB_SRC))
+
+ULIB_OBJ:= $(patsubst lib/%.c, $(BUILDDIR)/lib/%.o, $(ULIB_SRC))
+ULIB_OBJ:= $(patsubst lib/%.S, $(BUILDDIR)/lib/%.o, $(ULIB_OBJ))
+
+KLIB_OBJ := $(patsubst lib/%.c, $(BUILDDIR)/lib/%.o, $(KLIB_SRC))
+KLIB_OBJ := $(patsubst lib/%.S, $(BUILDDIR)/lib/%.o, $(KLIB_OBJ))
+
+# library file names
+CLIB_NAME := libcommon.a
+ULIB_NAME := libuser.a
+KLIB_NAME := libkernel.a
+
+###################
+# RULES SECTION #
+###################
+
+# how to make everything
+lib: $(BUILDDIR)/lib/$(CLIB_NAME) \
+ $(BUILDDIR)/lib/$(KLIB_NAME) \
+ $(BUILDDIR)/lib/$(ULIB_NAME)
+
+$(BUILDDIR)/lib/%.o: lib/%.c $(BUILDDIR)/.vars.CFLAGS
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILDDIR)/lib/%.o: lib/%.S $(BUILDDIR)/.vars.CFLAGS
+ @mkdir -p $(@D)
+ $(CPP) $(CPPFLAGS) -o $(@D)/$*.s $<
+ $(AS) $(ASFLAGS) -o $@ $(@D)/$*.s -a=$(@D)/$*.lst
+ $(RM) -f $(@D)/$*.s
+ $(NM) -n $@ > $(@D)/$*.sym
+
+$(BUILDDIR)/lib/$(CLIB_NAME): $(CLIB_OBJ)
+ $(AR) $(ARFLAGS) $@ $(CLIB_OBJ)
+
+$(BUILDDIR)/lib/$(ULIB_NAME): $(ULIB_OBJ)
+ $(AR) $(ARFLAGS) $@ $(ULIB_OBJ)
+
+$(BUILDDIR)/lib/$(KLIB_NAME): $(KLIB_OBJ)
+ $(AR) $(ARFLAGS) $@ $(KLIB_OBJ)
diff --git a/lib/bound.c b/lib/bound.c
new file mode 100644
index 0000000..f086473
--- /dev/null
+++ b/lib/bound.c
@@ -0,0 +1,37 @@
+/**
+** @file bound.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef BOUND_SRC_INC
+#define BOUND_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** bound(min,value,max)
+**
+** This function confines an argument within specified bounds.
+**
+** @param min Lower bound
+** @param value Value to be constrained
+** @param max Upper bound
+**
+** @return The constrained value
+*/
+uint32_t bound( uint32_t min, uint32_t value, uint32_t max ) {
+ if( value < min ){
+ value = min;
+ }
+ if( value > max ){
+ value = max;
+ }
+ return value;
+}
+
+#endif
diff --git a/lib/cvtdec.c b/lib/cvtdec.c
new file mode 100644
index 0000000..216f147
--- /dev/null
+++ b/lib/cvtdec.c
@@ -0,0 +1,43 @@
+/**
+** @file cvtdec.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTDEC_SRC_INC
+#define CVTDEC_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvtdec(buf,value)
+**
+** convert a 32-bit signed value into a NUL-terminated character string
+**
+** @param buf Destination buffer
+** @param value Value to convert
+**
+** @return The number of characters placed into the buffer
+** (not including the NUL)
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+int cvtdec( char *buf, int32_t value ) {
+ char *bp = buf;
+
+ if( value < 0 ) {
+ *bp++ = '-';
+ value = -value;
+ }
+
+ bp = cvtdec0( bp, value );
+ *bp = '\0';
+
+ return( bp - buf );
+}
+
+#endif
diff --git a/lib/cvtdec0.c b/lib/cvtdec0.c
new file mode 100644
index 0000000..87792e0
--- /dev/null
+++ b/lib/cvtdec0.c
@@ -0,0 +1,44 @@
+/**
+** @file cvtdec0.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTDEC0_SRC_INC
+#define CVTDEC0_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvtdec0(buf,value) - local support routine for cvtdec()
+**
+** convert a 32-bit unsigned integer into a NUL-terminated character string
+**
+** @param buf Destination buffer
+** @param value Value to convert
+**
+** @return The number of characters placed into the buffer
+** (not including the NUL)
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+char *cvtdec0( char *buf, int value ) {
+ int quotient;
+
+ quotient = value / 10;
+ if( quotient < 0 ) {
+ quotient = 214748364;
+ value = 8;
+ }
+ if( quotient != 0 ) {
+ buf = cvtdec0( buf, quotient );
+ }
+ *buf++ = value % 10 + '0';
+ return buf;
+}
+
+#endif
diff --git a/lib/cvthex.c b/lib/cvthex.c
new file mode 100644
index 0000000..5f3da19
--- /dev/null
+++ b/lib/cvthex.c
@@ -0,0 +1,49 @@
+/**
+** @file cvthex.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTHEX_SRC_INC
+#define CVTHEX_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvthex(buf,value)
+**
+** convert a 32-bit unsigned value into a minimal-length (up to
+** 8-character) NUL-terminated character string
+**
+** @param buf Destination buffer
+** @param value Value to convert
+**
+** @return The number of characters placed into the buffer
+** (not including the NUL)
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+int cvthex( char *buf, uint32_t value ) {
+ const char hexdigits[] = "0123456789ABCDEF";
+ int chars_stored = 0;
+
+ for( int i = 0; i < 8; i += 1 ) {
+ uint32_t val = value & 0xf0000000;
+ if( chars_stored || val != 0 || i == 7 ) {
+ ++chars_stored;
+ val = (val >> 28) & 0xf;
+ *buf++ = hexdigits[val];
+ }
+ value <<= 4;
+ }
+
+ *buf = '\0';
+
+ return( chars_stored );
+}
+
+#endif
diff --git a/lib/cvtoct.c b/lib/cvtoct.c
new file mode 100644
index 0000000..dafd8ff
--- /dev/null
+++ b/lib/cvtoct.c
@@ -0,0 +1,54 @@
+/**
+** @file cvtoct.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTOCT_SRC_INC
+#define CVTOCT_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvtoct(buf,value)
+**
+** convert a 32-bit unsigned value into a mininal-length (up to
+** 11-character) NUL-terminated character string
+**
+** @param buf Destination buffer
+** @param value Value to convert
+**
+** @return The number of characters placed into the buffer
+** (not including the NUL)
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+int cvtoct( char *buf, uint32_t value ) {
+ int i;
+ int chars_stored = 0;
+ char *bp = buf;
+ uint32_t val;
+
+ val = ( value & 0xc0000000 );
+ val >>= 30;
+ for( i = 0; i < 11; i += 1 ){
+
+ if( i == 10 || val != 0 || chars_stored ) {
+ chars_stored = 1;
+ val &= 0x7;
+ *bp++ = val + '0';
+ }
+ value <<= 3;
+ val = ( value & 0xe0000000 );
+ val >>= 29;
+ }
+ *bp = '\0';
+
+ return bp - buf;
+}
+
+#endif
diff --git a/lib/cvtuns.c b/lib/cvtuns.c
new file mode 100644
index 0000000..a0a686a
--- /dev/null
+++ b/lib/cvtuns.c
@@ -0,0 +1,37 @@
+/**
+** @file cvtuns.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTUNS_SRC_INC
+#define CVTUNS_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvtuns(buf,value)
+**
+** Convert a 32-bit unsigned value into a NUL-terminated character string
+**
+** @param buf Result buffer
+** @param value Value to be converted
+**
+** @return Length of the resulting buffer
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+int cvtuns( char *buf, uint32_t value ) {
+ char *bp = buf;
+
+ bp = cvtuns0( bp, value );
+ *bp = '\0';
+
+ return bp - buf;
+}
+
+#endif
diff --git a/lib/cvtuns0.c b/lib/cvtuns0.c
new file mode 100644
index 0000000..6a63573
--- /dev/null
+++ b/lib/cvtuns0.c
@@ -0,0 +1,39 @@
+/**
+** @file cvtuns0.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef CVTUNS0_SRC_INC
+#define CVTUNS0_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** cvtuns0(buf,value) - local support routine for cvtuns()
+**
+** Convert a 32-bit unsigned value into a NUL-terminated character string
+**
+** @param buf Result buffer
+** @param value Value to be converted
+**
+** @return Pointer to the first unused byte in the buffer
+**
+** NOTE: assumes buf is large enough to hold the resulting string
+*/
+char *cvtuns0( char *buf, uint32_t value ) {
+ uint32_t quotient;
+
+ quotient = value / 10;
+ if( quotient != 0 ){
+ buf = cvtdec0( buf, quotient );
+ }
+ *buf++ = value % 10 + '0';
+ return buf;
+}
+
+#endif
diff --git a/lib/entry.S b/lib/entry.S
new file mode 100644
index 0000000..87ad9c7
--- /dev/null
+++ b/lib/entry.S
@@ -0,0 +1,25 @@
+//
+// user-level startup routine
+//
+ .text
+ .globl _start
+ .globl main
+ .globl exit
+
+// entry point - this is where the kernel starts us running
+_start:
+ // we immediately call main()
+ call main
+
+ // if we come back from that, it means the user
+ // program didn't call exit(), in which case the
+ // value returned from main() is the exit status
+
+ // push that value onto the stack and call exit()
+ subl $12, %esp
+ pushl %eax
+ call exit
+
+ // if we come back from that, something bad has
+ // happened, so we just lock up
+1: jmp 1b
diff --git a/lib/klibc.c b/lib/klibc.c
new file mode 100644
index 0000000..ded0c78
--- /dev/null
+++ b/lib/klibc.c
@@ -0,0 +1,112 @@
+/*
+** @file klibc.c
+**
+** @author Warren R. Carithers
+**
+** Additional support functions for the kernel.
+**
+*/
+
+#define KERNEL_SRC
+
+#include <klib.h>
+#include <cio.h>
+#include <procs.h>
+#include <support.h>
+
+/**
+** Name: put_char_or_code( ch )
+**
+** Description: Prints a character on the console, unless it
+** is a non-printing character, in which case its hex code
+** is printed
+**
+** @param ch The character to be printed
+*/
+void put_char_or_code( int ch ) {
+
+ if( ch >= ' ' && ch < 0x7f ) {
+ cio_putchar( ch );
+ } else {
+ cio_printf( "\\x%02x", ch );
+ }
+}
+
+/**
+** Name: backtrace
+**
+** Perform a stack backtrace
+**
+** @param ebp Initial EBP to use
+** @param args Number of function argument values to print
+*/
+void backtrace( uint32_t *ebp, uint_t args ) {
+
+ cio_puts( "Trace: " );
+ if( ebp == NULL ) {
+ cio_puts( "NULL ebp, no trace possible\n" );
+ return;
+ } else {
+ cio_putchar( '\n' );
+ }
+
+ while( ebp != NULL ){
+
+ // get return address and report it and EBP
+ uint32_t ret = ebp[1];
+ cio_printf( " ebp %08x ret %08x args", (uint32_t) ebp, ret );
+
+ // print the requested number of function arguments
+ for( uint_t i = 0; i < args; ++i ) {
+ cio_printf( " [%u] %08x", i+1, ebp[2+i] );
+ }
+ cio_putchar( '\n' );
+
+ // follow the chain
+ ebp = (uint32_t *) *ebp;
+ }
+}
+
+/**
+** kpanic - kernel-level panic routine
+**
+** usage: kpanic( msg )
+**
+** Prefix routine for panic() - can be expanded to do other things
+** (e.g., printing a stack traceback)
+**
+** @param msg[in] String containing a relevant message to be printed,
+** or NULL
+*/
+void kpanic( const char *msg ) {
+
+ cio_puts( "\n\n***** KERNEL PANIC *****\n\n" );
+
+ if( msg ) {
+ cio_printf( "%s\n", msg );
+ }
+
+ delay( DELAY_5_SEC ); // approximately
+
+ // dump a bunch of potentially useful information
+
+ // dump the contents of the current PCB
+ pcb_dump( "Current", current, true );
+
+ // dump the basic info about what's in the process table
+ ptable_dump_counts();
+
+ // dump information about the queues
+ pcb_queue_dump( "R", ready, true );
+ pcb_queue_dump( "W", waiting, true );
+ pcb_queue_dump( "S", sleeping, true );
+ pcb_queue_dump( "Z", zombie, true );
+ pcb_queue_dump( "I", sioread, true );
+
+ // perform a stack backtrace
+ backtrace( (uint32_t *) r_ebp(), 3 );
+
+ // could dump other stuff here, too
+
+ panic( "KERNEL PANIC" );
+}
diff --git a/lib/memclr.c b/lib/memclr.c
new file mode 100644
index 0000000..490fc6d
--- /dev/null
+++ b/lib/memclr.c
@@ -0,0 +1,37 @@
+/**
+** @file memclr.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef MEMCLR_SRC_INC
+#define MEMCLR_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** memclr(buf,len)
+**
+** Initialize all bytes of a block of memory to zero
+**
+** @param buf The buffer to initialize
+** @param len Buffer size (in bytes)
+*/
+void memclr( void *buf, register uint32_t len ) {
+ register uint8_t *dest = buf;
+
+ /*
+ ** We could speed this up by unrolling it and clearing
+ ** words at a time (instead of bytes).
+ */
+
+ while( len-- ) {
+ *dest++ = 0;
+ }
+}
+
+#endif
diff --git a/lib/memcpy.c b/lib/memcpy.c
new file mode 100644
index 0000000..e5add26
--- /dev/null
+++ b/lib/memcpy.c
@@ -0,0 +1,41 @@
+/**
+** @file memcpy.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef MEMCPY_SRC_INC
+#define MEMCPY_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** memcpy(dst,src,len)
+**
+** Copy a block from one place to another
+**
+** May not correctly deal with overlapping buffers
+**
+** @param dst Destination buffer
+** @param src Source buffer
+** @param len Buffer size (in bytes)
+*/
+void memcpy( void *dst, register const void *src, register uint32_t len ) {
+ register uint8_t *dest = dst;
+ register const uint8_t *source = src;
+
+ /*
+ ** We could speed this up by unrolling it and copying
+ ** words at a time (instead of bytes).
+ */
+
+ while( len-- ) {
+ *dest++ = *source++;
+ }
+}
+
+#endif
diff --git a/lib/memset.c b/lib/memset.c
new file mode 100644
index 0000000..a93815c
--- /dev/null
+++ b/lib/memset.c
@@ -0,0 +1,38 @@
+/**
+** @file memset.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef MEMSET_SRC_INC
+#define MEMSET_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** memset(buf,len,value)
+**
+** initialize all bytes of a block of memory to a specific value
+**
+** @param buf The buffer to initialize
+** @param len Buffer size (in bytes)
+** @param value Initialization value
+*/
+void memset( void *buf, register uint32_t len, register uint32_t value ) {
+ register uint8_t *bp = buf;
+
+ /*
+ ** We could speed this up by unrolling it and copying
+ ** words at a time (instead of bytes).
+ */
+
+ while( len-- ) {
+ *bp++ = value;
+ }
+}
+
+#endif
diff --git a/lib/pad.c b/lib/pad.c
new file mode 100644
index 0000000..5220c99
--- /dev/null
+++ b/lib/pad.c
@@ -0,0 +1,35 @@
+/**
+** @file pad.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef PAD_SRC_INC
+#define PAD_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** pad(dst,extra,padchar) - generate a padding string
+**
+** @param dst Pointer to where the padding should begin
+** @param extra How many padding bytes to add
+** @param padchar What character to pad with
+**
+** @return Pointer to the first byte after the padding
+**
+** NOTE: does NOT NUL-terminate the buffer
+*/
+char *pad( char *dst, int extra, int padchar ) {
+ while( extra > 0 ){
+ *dst++ = (char) padchar;
+ extra -= 1;
+ }
+ return dst;
+}
+
+#endif
diff --git a/lib/padstr.c b/lib/padstr.c
new file mode 100644
index 0000000..b83229f
--- /dev/null
+++ b/lib/padstr.c
@@ -0,0 +1,61 @@
+/**
+** @file padstr.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef PADSTR_SRC_INC
+#define PADSTR_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** padstr(dst,str,len,width,leftadjust,padchar - add padding characters
+** to a string
+**
+** @param dst The destination buffer
+** @param str The string to be padded
+** @param len The string length, or -1
+** @param width The desired final length of the string
+** @param leftadjust Should the string be left-justified?
+** @param padchar What character to pad with
+**
+** @return Pointer to the first byte after the padded string
+**
+** NOTE: does NOT NUL-terminate the buffer
+*/
+char *padstr( char *dst, char *str, int len, int width,
+ int leftadjust, int padchar ) {
+ int extra;
+
+ // determine the length of the string if we need to
+ if( len < 0 ){
+ len = strlen( str );
+ }
+
+ // how much filler must we add?
+ extra = width - len;
+
+ // add filler on the left if we're not left-justifying
+ if( extra > 0 && !leftadjust ){
+ dst = pad( dst, extra, padchar );
+ }
+
+ // copy the string itself
+ for( int i = 0; i < len; ++i ) {
+ *dst++ = str[i];
+ }
+
+ // add filler on the right if we are left-justifying
+ if( extra > 0 && leftadjust ){
+ dst = pad( dst, extra, padchar );
+ }
+
+ return dst;
+}
+
+#endif
diff --git a/lib/sprint.c b/lib/sprint.c
new file mode 100644
index 0000000..d2e0a7e
--- /dev/null
+++ b/lib/sprint.c
@@ -0,0 +1,133 @@
+/**
+** @file sprint.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef SPRINT_SRC_INC
+#define SPRINT_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** sprint(dst,fmt,...) - formatted output into a string buffer
+**
+** @param dst The string buffer
+** @param fmt Format string
+**
+** The format string parameter is followed by zero or more additional
+** parameters which are interpreted according to the format string.
+**
+** NOTE: assumes the buffer is large enough to hold the result string
+**
+** NOTE: relies heavily on the x86 parameter passing convention
+** (parameters are pushed onto the stack in reverse order as
+** 32-bit values).
+*/
+void sprint( char *dst, char *fmt, ... ) {
+ int32_t *ap;
+ char buf[ 12 ];
+ char ch;
+ char *str;
+ int leftadjust;
+ int width;
+ int len;
+ int padchar;
+
+ /*
+ ** Get characters from the format string and process them
+ **
+ ** We use the "old-school" method of handling variable numbers
+ ** of parameters. We assume that parameters are passed on the
+ ** runtime stack in consecutive longwords; thus, if the first
+ ** parameter is at location 'x', the second is at 'x+4', the
+ ** third at 'x+8', etc. We use a pointer to a 32-bit thing
+ ** to point to the next "thing", and interpret it according
+ ** to the format string.
+ */
+
+ // get the pointer to the first "value" parameter
+ ap = (int *)(&fmt) + 1;
+
+ // iterate through the format string
+ while( (ch = *fmt++) != '\0' ){
+ /*
+ ** Is it the start of a format code?
+ */
+ if( ch == '%' ){
+ /*
+ ** Yes, get the padding and width options (if there).
+ ** Alignment must come at the beginning, then fill,
+ ** then width.
+ */
+ leftadjust = 0;
+ padchar = ' ';
+ width = 0;
+ ch = *fmt++;
+ if( ch == '-' ){
+ leftadjust = 1;
+ ch = *fmt++;
+ }
+ if( ch == '0' ){
+ padchar = '0';
+ ch = *fmt++;
+ }
+ while( ch >= '0' && ch <= '9' ){
+ width *= 10;
+ width += ch - '0';
+ ch = *fmt++;
+ }
+
+ /*
+ ** What data type do we have?
+ */
+ switch( ch ) {
+
+ case 'c': // characters are passed as 32-bit values
+ ch = *ap++;
+ buf[ 0 ] = ch;
+ buf[ 1 ] = '\0';
+ dst = padstr( dst, buf, 1, width, leftadjust, padchar );
+ break;
+
+ case 'd':
+ len = cvtdec( buf, *ap++ );
+ dst = padstr( dst, buf, len, width, leftadjust, padchar );
+ break;
+
+ case 's':
+ str = (char *) (*ap++);
+ dst = padstr( dst, str, -1, width, leftadjust, padchar );
+ break;
+
+ case 'x':
+ len = cvthex( buf, *ap++ );
+ dst = padstr( dst, buf, len, width, leftadjust, padchar );
+ break;
+
+ case 'o':
+ len = cvtoct( buf, *ap++ );
+ dst = padstr( dst, buf, len, width, leftadjust, padchar );
+ break;
+
+ case 'u':
+ len = cvtuns( buf, *ap++ );
+ dst = padstr( dst, buf, len, width, leftadjust, padchar );
+ break;
+
+ }
+ } else {
+ // no, it's just an ordinary character
+ *dst++ = ch;
+ }
+ }
+
+ // NUL-terminate the result
+ *dst = '\0';
+}
+
+#endif
diff --git a/lib/str2int.c b/lib/str2int.c
new file mode 100644
index 0000000..c0f777d
--- /dev/null
+++ b/lib/str2int.c
@@ -0,0 +1,51 @@
+/**
+** @file str2int.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef STR2INT_SRC_INC
+#define STR2INT_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** str2int(str,base) - convert a string to a number in the specified base
+**
+** @param str The string to examine
+** @param base The radix to use in the conversion
+**
+** @return The converted integer
+*/
+int str2int( register const char *str, register int base ) {
+ register int num = 0;
+ register char bchar = '9';
+ int sign = 1;
+
+ // check for leading '-'
+ if( *str == '-' ) {
+ sign = -1;
+ ++str;
+ }
+
+ if( base != 10 ) {
+ bchar = '0' + base - 1;
+ }
+
+ // iterate through the characters
+ while( *str ) {
+ if( *str < '0' || *str > bchar )
+ break;
+ num = num * base + *str - '0';
+ ++str;
+ }
+
+ // return the converted value
+ return( num * sign );
+}
+
+#endif
diff --git a/lib/strcat.c b/lib/strcat.c
new file mode 100644
index 0000000..b0a8726
--- /dev/null
+++ b/lib/strcat.c
@@ -0,0 +1,38 @@
+/**
+** @file strcat.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef STRCAT_SRC_INC
+#define STRCAT_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** strcat(dst,src) - append one string to another
+**
+** @param dst The destination buffer
+** @param src The source buffer
+**
+** @return The dst parameter
+**
+** NOTE: assumes dst is large enough to hold the resulting string
+*/
+char *strcat( register char *dst, register const char *src ) {
+ register char *tmp = dst;
+
+ while( *dst ) // find the NUL
+ ++dst;
+
+ while( (*dst++ = *src++) ) // append the src string
+ ;
+
+ return( tmp );
+}
+
+#endif
diff --git a/lib/strcmp.c b/lib/strcmp.c
new file mode 100644
index 0000000..c59f4f7
--- /dev/null
+++ b/lib/strcmp.c
@@ -0,0 +1,32 @@
+/**
+** @file strcmp.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef STRCMP_SRC_INC
+#define STRCMP_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** strcmp(s1,s2) - compare two NUL-terminated strings
+**
+** @param s1 The first source string
+** @param s2 The second source string
+**
+** @return negative if s1 < s2, zero if equal, and positive if s1 > s2
+*/
+int strcmp( register const char *s1, register const char *s2 ) {
+
+ while( *s1 != 0 && (*s1 == *s2) )
+ ++s1, ++s2;
+
+ return( *s1 - *s2 );
+}
+
+#endif
diff --git a/lib/strcpy.c b/lib/strcpy.c
new file mode 100644
index 0000000..036e4be
--- /dev/null
+++ b/lib/strcpy.c
@@ -0,0 +1,35 @@
+/**
+** @file strcpy.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef STRCPY_SRC_INC
+#define STRCPY_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** strcpy(dst,src) - copy a NUL-terminated string
+**
+** @param dst The destination buffer
+** @param src The source buffer
+**
+** @return The dst parameter
+**
+** NOTE: assumes dst is large enough to hold the copied string
+*/
+char *strcpy( register char *dst, register const char *src ) {
+ register char *tmp = dst;
+
+ while( (*dst++ = *src++) )
+ ;
+
+ return( tmp );
+}
+
+#endif
diff --git a/lib/strlen.c b/lib/strlen.c
new file mode 100644
index 0000000..b41fe69
--- /dev/null
+++ b/lib/strlen.c
@@ -0,0 +1,32 @@
+/**
+** @file strlen.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+*/
+
+#ifndef STRLEN_SRC_INC
+#define STRLEN_SRC_INC
+
+#include <common.h>
+
+#include <lib.h>
+
+/**
+** strlen(str) - return length of a NUL-terminated string
+**
+** @param str The string to examine
+**
+** @return The length of the string, or 0
+*/
+uint32_t strlen( register const char *str ) {
+ register uint32_t len = 0;
+
+ while( *str++ ) {
+ ++len;
+ }
+
+ return( len );
+}
+#endif
diff --git a/lib/ulibc.c b/lib/ulibc.c
new file mode 100644
index 0000000..f3783a4
--- /dev/null
+++ b/lib/ulibc.c
@@ -0,0 +1,162 @@
+/**
+** @file ulibc.c
+**
+** @author CSCI-452 class of 20245
+**
+** @brief C implementations of user-level library functions
+*/
+
+#include <common.h>
+
+/*
+** PRIVATE DEFINITIONS
+*/
+
+/*
+** PRIVATE DATA TYPES
+*/
+
+/*
+** PRIVATE GLOBAL VARIABLES
+*/
+
+/*
+** PUBLIC GLOBAL VARIABLES
+*/
+
+/*
+** PRIVATE FUNCTIONS
+*/
+
+/*
+** PUBLIC FUNCTIONS
+*/
+
+/*
+**********************************************
+** CONVENIENT "SHORTHAND" VERSIONS OF SYSCALLS
+**********************************************
+*/
+
+/**
+** wait - wait for any child to exit
+**
+** usage: pid = wait(&status)
+**
+** Calls waitpid(0,status)
+**
+** @param status Pointer to int32_t into which the child's status is placed,
+** or NULL
+**
+** @returns The PID of the terminated child, or an error code
+*/
+int wait( int32_t *status ) {
+ return( waitpid(0,status) );
+}
+
+/**
+** spawn - create a new process running a different program
+**
+** usage: pid = spawn(what,args);
+**
+** Creates a new process and then execs 'what'
+**
+** @param what The program table index of the program to spawn
+** @param args The command-line argument vector for the new process
+**
+** @returns PID of the new process, or an error code
+*/
+int32_t spawn( uint_t what, char **args ) {
+ int32_t pid;
+ char buf[256];
+
+ pid = fork();
+ if( pid != 0 ) {
+ // failure, or we are the parent
+ return( pid );
+ }
+
+ // we are the child
+ pid = getpid();
+
+ // child inherits parent's priority level
+
+ exec( what, args );
+
+ // uh-oh....
+
+ sprint( buf, "Child %d exec() #%u failed\n", pid, what );
+ cwrites( buf );
+
+ exit( EXIT_FAILURE );
+ return( 0 ); // shut the compiler up
+}
+
+/**
+** cwritech(ch) - write a single character to the console
+**
+** @param ch The character to write
+**
+** @returns The return value from calling write()
+*/
+int cwritech( char ch ) {
+ return( write(CHAN_CIO,&ch,1) );
+}
+
+/**
+** cwrites(str) - write a NUL-terminated string to the console
+**
+** @param str The string to write
+**
+*/
+int cwrites( const char *str ) {
+ int len = strlen(str);
+ return( write(CHAN_CIO,str,len) );
+}
+
+/**
+** cwrite(buf,size) - write a sized buffer to the console
+**
+** @param buf The buffer to write
+** @param size The number of bytes to write
+**
+** @returns The return value from calling write()
+*/
+int cwrite( const char *buf, uint32_t size ) {
+ return( write(CHAN_CIO,buf,size) );
+}
+
+/**
+** swritech(ch) - write a single character to the SIO
+**
+** @param ch The character to write
+**
+** @returns The return value from calling write()
+*/
+int swritech( char ch ) {
+ return( write(CHAN_SIO,&ch,1) );
+}
+
+/**
+** swrites(str) - write a NUL-terminated string to the SIO
+**
+** @param str The string to write
+**
+** @returns The return value from calling write()
+*/
+int swrites( const char *str ) {
+ int len = strlen(str);
+ return( write(CHAN_SIO,str,len) );
+}
+
+/**
+** swrite(buf,size) - write a sized buffer to the SIO
+**
+** @param buf The buffer to write
+** @param size The number of bytes to write
+**
+** @returns The return value from calling write()
+*/
+int swrite( const char *buf, uint32_t size ) {
+ return( write(CHAN_SIO,buf,size) );
+}
diff --git a/lib/ulibs.S b/lib/ulibs.S
new file mode 100644
index 0000000..46fcb89
--- /dev/null
+++ b/lib/ulibs.S
@@ -0,0 +1,93 @@
+/**
+** @file ulibs.S
+**
+** @author CSCI-452 class of 20245
+**
+** @brief assembly-language user-level library functions
+*/
+
+#define ASM_SRC
+
+// get the system call codes
+
+#include <syscalls.h>
+
+/**
+** System call stubs
+**
+** All have the same structure:
+**
+** move a code into EAX
+** generate the interrupt
+** return to the caller
+**
+** As these are simple "leaf" routines, we don't use
+** the standard enter/leave method to set up a stack
+** frame - that takes time, and we don't really need it.
+**
+** Could be modified to use the UNIX/Linux convention of
+** having the syscall code set the 'C' flag to indicate that
+** the value being returned in %EAX is an error code:
+**
+** ...
+** int $VEC_SYSCALL
+** jc set_errno
+** ret
+** ...
+**
+** .globl errno
+** set_errno:
+** movl %eax, errno
+** movl $-1, %eax
+** ret
+*/
+
+#define SYSCALL(name) \
+ .globl name ; \
+name: ; \
+ movl $SYS_##name, %eax ; \
+ int $VEC_SYSCALL ; \
+ ret
+
+/*
+** "real" system calls
+*/
+
+SYSCALL(exit)
+SYSCALL(waitpid)
+SYSCALL(fork)
+SYSCALL(exec)
+SYSCALL(read)
+SYSCALL(write)
+SYSCALL(getpid)
+SYSCALL(getppid)
+SYSCALL(gettime)
+SYSCALL(getprio)
+SYSCALL(setprio)
+SYSCALL(kill)
+SYSCALL(sleep)
+
+/*
+** This is a bogus system call; it's here so that we can test
+** our handling of out-of-range syscall codes in the syscall ISR.
+*/
+SYSCALL(bogus)
+
+/*
+** Other library functions
+*/
+
+/**
+** fake_exit()
+**
+** Dummy "startup" function
+**
+** calls exit(%eax) - serves as the "return to" code for
+** main() functions, in case they don't call exit() themselves
+*/
+
+ .globl fake_exit
+fake_exit:
+ // alternate: could push a "fake exit" status
+ pushl %eax // termination status returned by main()
+ call exit // terminate this process