Index: trunk/kernel/kern/printk.c
===================================================================
--- trunk/kernel/kern/printk.c	(revision 662)
+++ trunk/kernel/kern/printk.c	(revision 669)
@@ -2,5 +2,5 @@
  * printk.c - Kernel Log & debug messages API implementation.
  * 
- * authors  Alain Greiner (2016,2017,2018)
+ * authors  Alain Greiner (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -34,25 +34,37 @@
 
 ///////////////////////////////////////////////////////////////////////////////////
-//      Extern variables
+//      Extern 
 ///////////////////////////////////////////////////////////////////////////////////
 
 extern chdev_directory_t  chdev_dir;  // defined in chdev.h / allocated in kernel_init.c
 
-/////////////////////////////////////
-uint32_t snprintf( char     * string,
-                   uint32_t   length,
-                   char     * format, ... )
-{
-
-#define TO_STREAM(x) do { string[ps] = (x); ps++; if(ps==length) return -1; } while(0);
-
-    va_list    args;      // printf arguments
-    uint32_t   ps;        // pointer to the string buffer
-
-    ps = 0;   
-    va_start( args , format );
-
-xprintf_text:
-
+//////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by printk(), nolock_printk(), and snprintk(),
+// functions to build a string from a printf-like format, and stores it 
+// in the buffer defined by the <string> and <length> arguments.
+// It does NOT add a terminating NUL character in the <string> buffer.
+// If success, it returns the number of bytes actually copied in the string buffer.
+// It returns -2 in case of illegal format, it returns -1 if the formated string 
+// exceeds the length argument. 
+//////////////////////////////////////////////////////////////////////////////////////
+// @ string    : buffer allocated by caller.
+// @ length    : buffer size in bytes
+// @ format    : printf like format.
+// @ args      : va_list of arguments.
+// @ return string length if success / -1 if buffer too small / -2 if illegal format. 
+//////////////////////////////////////////////////////////////////////////////////////
+static int32_t format_to_string( char       * string,
+                                 uint32_t     length,
+                                 const char * format, 
+                                 va_list    * args ) 
+{
+
+#define TO_STRING(x) do { string[ps] = (x); ps++; if(ps==length) return -1; } while(0);
+
+    uint32_t   ps = 0;        // index in string buffer
+
+format_to_string_text:
+
+    // handle one character per iteration
     while ( *format != 0 ) 
     {
@@ -60,20 +72,17 @@
         {
             format++;
-            goto xprintf_arguments;
+            goto format_to_string_arguments;
         }
         else                  // copy one char to string
         {
-            TO_STREAM( *format );
+            TO_STRING( *format );
             format++;
         }
     }
 
-    va_end( args );
-    
-    // add terminating NUL chracter
-    TO_STREAM( 0 );
+    TO_STRING( 0 );
     return ps;
 
-xprintf_arguments:
+format_to_string_arguments:
 
     {
@@ -84,34 +93,20 @@
         uint32_t          i;
         
-        // Ignore fields width and precision 
-        for ( ; (*format >= '0' && *format <= '9') || (*format == '.') ; format++ );
-
         switch (*format) 
         {
             case ('c'):             // char conversion 
             {
-                int val = va_arg( args, int );
-                buf[0] = val;
+                int val = va_arg( *args , int );
+                buf[0] = (char)val;
                 pbuf   = buf;
                 len    = 1;
                 break;
             }
-            case ('b'):             // excactly 2 digits hexadecimal integer
-            {
-                int  val = va_arg( args, int );
-                int  val_lsb = val & 0xF;
-                int  val_msb = (val >> 4) & 0xF;
-                buf[0] = HexaTab[val_msb];
-                buf[1] = HexaTab[val_lsb];
-                len  = 2;
-                pbuf = buf;
-                break;
-            }
             case ('d'):             // up to 10 digits decimal signed integer
             {
-                int val = va_arg( args, int );
+                int32_t val = va_arg( *args , int32_t );
                 if (val < 0) 
                 {
-                    TO_STREAM( '-' );
+                    TO_STRING( '-' );
                     val = -val;
                 }
@@ -127,160 +122,4 @@
             case ('u'):             // up to 10 digits decimal unsigned integer
             {
-                uint32_t val = va_arg( args, uint32_t );
-                for(i = 0; i < 10; i++) 
-                {
-                    buf[9 - i] = HexaTab[val % 10];
-                    if (!(val /= 10)) break;
-                }
-                len =  i + 1;
-                pbuf = &buf[9 - i];
-                break;
-            }
-            case ('x'):             // up to 8 digits hexadecimal 
-            case ('l'):             // up to 16 digits hexadecimal 
-            {
-                uint32_t imax;
-                uint64_t val;
-                
-                if ( *format == 'l' )   // 64 bits
-                {
-                    val = va_arg( args, uint64_t);
-                    imax = 16;
-                }
-                else                    // 32 bits
-                {
-                    val = va_arg( args, uint32_t);
-                    imax = 8;
-                }
-                
-                TO_STREAM( '0' );
-                TO_STREAM( 'x' );
-                
-                for(i = 0; i < imax; i++) 
-                {
-                    buf[(imax-1) - i] = HexaTab[val % 16];
-                    if (!(val /= 16))  break;
-                }
-                len =  i + 1;
-                pbuf = &buf[(imax-1) - i];
-                break;
-            }
-            case ('X'):             // exactly 8 digits hexadecimal
-            {
-                uint32_t val = va_arg( args , uint32_t );
-                for(i = 0; i < 8; i++) 
-                {
-                    buf[7 - i] = HexaTab[val % 16];
-                    val = (val>>4);
-                }
-                len =  8;
-                pbuf = buf;
-                break;
-            }
-            case ('s'):             /* string */
-            {
-                char* str = va_arg( args, char* );
-                while (str[len]) { len++; }
-                pbuf = str;
-                break;
-            }
-            default:       // unsupported argument type
-            {
-                return -1;
-            }
-        }  // end switch on  argument type
-
-        format++;
-
-        // copy argument to string
-        for( i = 0 ; i < len ; i++ )
-        {
-            TO_STREAM( pbuf[i] );
-        }
-        
-        goto xprintf_text;
-    }
-} // end snprintf()
-
-//////////////////////////////////////////////////////////////////////////////////////
-// This static function is called by printk(), assert() and nolock_printk()
-// to display a formated string on TXT0, using a busy waiting policy.
-//////////////////////////////////////////////////////////////////////////////////////
-// @ format    : printf like format.
-// @ args      : va_list of arguments.
-//////////////////////////////////////////////////////////////////////////////////////
-static void kernel_printf( const char * format, 
-                           va_list    * args ) 
-{
-
-printf_text:
-
-    while (*format) 
-    {
-        uint32_t i;
-        for (i = 0 ; format[i] && (format[i] != '%') ; i++);
-        if (i) 
-        {
-            dev_txt_sync_write( format, i );
-            format += i;
-        }
-        if (*format == '%') 
-        {
-            format++;
-            goto printf_arguments;
-        }
-    }
-
-    return;
-
-printf_arguments:
-
-    {
-        char      buf[20];
-        char    * pbuf = NULL;
-        uint32_t  len  = 0;
-        static const char HexaTab[] = "0123456789ABCDEF";
-        uint32_t i;
-
-        switch (*format++) 
-        {
-            case ('c'):             /* char conversion */
-            {
-                int  val = va_arg( *args , int );
-                len = 1;
-                buf[0] = (char)val;
-                pbuf = &buf[0];
-                break;
-            }
-            case ('b'):             /* exactly 2 digits hexadecimal */
-            {
-                int  val = va_arg( *args, int );
-                int  val_lsb = val & 0xF;
-                int  val_msb = (val >> 4) & 0xF;
-                buf[0] = HexaTab[val_msb];
-                buf[1] = HexaTab[val_lsb];
-                len  = 2;
-                pbuf = buf;
-                break;
-            }
-            case ('d'):             /* up to 10 digits signed decimal */
-            {
-                int val = va_arg( *args , int );
-                if (val < 0) 
-                {
-                    val = -val;
-                    dev_txt_sync_write( "-" , 1 );
-                }
-                for(i = 0; i < 10; i++) 
-                {
-                    buf[9 - i] = HexaTab[val % 10];
-                    if (!(val /= 10)) break;
-                }
-                len =  i + 1;
-                pbuf = &buf[9 - i];
-                break;
-            }
-            case ('u'):             /* up to 10 digits unsigned decimal */
-            {
                 uint32_t val = va_arg( *args , uint32_t );
                 for(i = 0; i < 10; i++) 
@@ -293,12 +132,15 @@
                 break;
             }
-            case ('x'):             /* up to 8 digits hexadecimal */
+            case ('x'):             // up to 8 digits hexad after "0x"
+            case ('X'):             // exactly 8 digits hexa after "0x"
             {
                 uint32_t val = va_arg( *args , uint32_t );
-                dev_txt_sync_write( "0x" , 2 );
-                for(i = 0; i < 8; i++) 
+                TO_STRING( '0' );
+                TO_STRING( 'x' );
+                for(i = 0 ; i < 8 ; i++) 
                 {
                     buf[7 - i] = HexaTab[val & 0xF];
-                    if (!(val = (val>>4)))  break;
+                    if( (*format == 'x') && ((val >> 4) == 0) )  break;
+                    val = val >> 4;
                 }
                 len =  i + 1;
@@ -306,25 +148,16 @@
                 break;
             }
-            case ('X'):             /* exactly 8 digits hexadecimal */
+            case ('l'):             // up to 16 digits hexa after "0x"
+            case ('L'):             // exactly 16 digits hexa after "0x"
             {
-                uint32_t val = va_arg( *args , uint32_t );
-                dev_txt_sync_write( "0x" , 2 );
-                for(i = 0; i < 8; i++) 
-                {
-                    buf[7 - i] = HexaTab[val & 0xF];
-                    val = (val>>4);
-                }
-                len =  8;
-                pbuf = buf;
-                break;
-            }
-            case ('l'):            /* up to 16 digits hexadecimal */
-            {
-                uint64_t val = va_arg( *args , uint64_t );
-                dev_txt_sync_write( "0x" , 2 );
-                for(i = 0; i < 16; i++) 
+                uint64_t val = (((uint64_t)va_arg( *args, uint32_t)) << 32) |
+                               ((uint64_t)va_arg( *args, uint32_t));
+                TO_STRING( '0' );
+                TO_STRING( 'x' );
+                for(i = 0 ; i < 16 ; i++) 
                 {
                     buf[15 - i] = HexaTab[val & 0xF];
-                    if (!(val = (val>>4)))  break;
+                    if( (*format == 'l') && ((val >> 4) == 0) )  break;
+                    val = val >> 4;
                 }
                 len =  i + 1;
@@ -332,44 +165,38 @@
                 break;
             }
-            case ('L'):           /* exactly 16 digits hexadecimal */ 
-            {
-                uint64_t val = va_arg( *args , uint64_t );
-                dev_txt_sync_write( "0x" , 2 );
-                for(i = 0; i < 16; i++) 
-                {
-                    buf[15 - i] = HexaTab[val & 0xF];
-                    val = (val>>4);
-                }
-                len =  16;
-                pbuf = buf;
-                break;
-            }
             case ('s'):             /* string */
             {
                 char* str = va_arg( *args , char* );
-                while (str[len]) 
-                {
-                    len++;
-                }
+                while (str[len]) { len++; }
                 pbuf = str;
                 break;
             }
-            default:
+            default:       // unsupported argument type
             {
-                dev_txt_sync_write( "\n[PANIC] in kernel_printf() : illegal format\n", 45 );
+                return -2;
             }
-        }
-
-        if( pbuf != NULL ) dev_txt_sync_write( pbuf, len );
+        }  // end switch on  argument type
+
+        format++;
+
+        // copy argument sub-string to the string buffer
+        for( i = 0 ; i < len ; i++ )
+        {
+            TO_STRING( pbuf[i] );
+        }
         
-        goto printf_text;
-    }
-
-}  // end kernel_printf()
+        goto format_to_string_text;
+    }
+}   // end format_to_string()
 
 //////////////////////////////////
 void printk( char * format , ... )
 {
+    char          buffer[CONFIG_PRINTK_BUF_SIZE];
     va_list       args;
+    int32_t       length;
+
+    // build args va_list
+    va_start( args , format );
 
     // get pointers on TXT0 chdev
@@ -384,68 +211,149 @@
     remote_busylock_acquire( lock_xp );
 
-    // display format on TXT0 in busy waiting mode
-    va_start( args , format );
-    kernel_printf( format , &args );
+    // build a string from format
+    length = format_to_string( buffer,
+                      CONFIG_PRINTK_BUF_SIZE,
+                      format,
+                      &args );
     va_end( args );
+
+    if( length > 0  )        // call TXT driver to display formated string on TXT0
+    {
+        dev_txt_sync_write( buffer , length );
+    }
+    else if( length == -2 )  // illegal format => display a warning on TXT0
+    {
+        thread_t * this = CURRENT_THREAD;
+
+        nolock_printk("\n[PANIC] from printk : illegal format\n"
+                      "thread[%x,%x] on core[%x,%d] at cycle %l\n",
+                      this->process->pid, this->trdid,
+                      local_cxy, this->core->lid, hal_get_cycles() );
+    }
+    else                     // format too long => display a warning on TXT0
+    {
+        thread_t * this = CURRENT_THREAD;
+
+        nolock_printk("\n[PANIC] from printk : formated string too long\n"
+                      "thread[%x,%x] on core[%x,%d] at cycle %l\n",
+                      this->process->pid, this->trdid,
+                      local_cxy, this->core->lid, hal_get_cycles() );
+    }
 
     // release TXT0 lock
     remote_busylock_release( lock_xp );
-}
+
+}   // end printk()
 
 /////////////////////////////////////////
 void nolock_printk( char * format , ... )
 {
+    char          buffer[CONFIG_PRINTK_BUF_SIZE];
     va_list       args;
-
-    // call kernel_printf on TXT0, in busy waiting mode
+    int32_t       length;
+
+    // build args va_list
     va_start( args , format );
-    kernel_printf( format , &args );
+
+    // build a string from format
+    length = format_to_string( buffer,
+                      CONFIG_PRINTK_BUF_SIZE,
+                      format,
+                      &args );
     va_end( args );
-}
-
-////////////////////////////////////
-void panic( const char * function_name,
-            uint32_t     line,
-            cycle_t      cycle,
-            const char * format,
-            ... )
-{
-    // get pointers on TXT0 chdev
-    xptr_t    txt0_xp = chdev_dir.txt_tx[0];
-    cxy_t     txt0_cxy = GET_CXY(txt0_xp);
-    chdev_t * txt0_ptr = GET_PTR(txt0_xp);
-
-    // get extended pointer on remote TXT0 lock
-    xptr_t lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
-
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
-
-    // get calling thread
-    thread_t * current = CURRENT_THREAD;
-
-    // print generic infos
-    nolock_printk("\n\n[PANIC] in %s: line %d | cycle %d\n"
-                  "core[%x,%d] | thread %x (%x) | process %x (%x)\n",
-                  function_name, line, (uint32_t)cycle,
-                  local_cxy, current->core->lid, 
-                  current->trdid, current,
-                  current->process->pid, current->process );
-
-    // call kernel_printf to print format
-    va_list args;
-    va_start(args, format);
-    kernel_printf(format, &args);
-    va_end(args);
-
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-
-    // suicide
-    hal_core_sleep();
-}
-
-//////////////////////////
-void puts( char * string ) 
+
+    if( length > 0  )        // call TXT driver to display formated string on TXT0
+    {
+        dev_txt_sync_write( buffer , length );
+    }
+    else if( length == -2 )  // illegal format => display a warning on TXT0
+    {
+        thread_t * this = CURRENT_THREAD;
+
+        nolock_printk("\n[PANIC] from print : illegal format\n"
+                      "thread[%x,%x] on core[%x,%d] at cycle %l\n",
+                      this->process->pid, this->trdid,
+                      local_cxy, this->core->lid, hal_get_cycles() );
+    }
+    else                  // buffer too small => display a warning on TXT0
+    {
+        thread_t * this = CURRENT_THREAD;
+
+        nolock_printk("\n[PANIC] from printk : formated string too long\n"
+                      "thread[%x,%x] on core[%x,%d] at cycle %l\n",
+                      this->process->pid, this->trdid,
+                      local_cxy, this->core->lid, hal_get_cycles() );
+    }
+
+}   // end nolock_printk()
+
+//////////////////////////////////////
+void assert( const char   * func_name,
+             bool_t         expr,
+             char         * format , ... )
+{
+    if( expr == false )
+    {
+        thread_t * this  = CURRENT_THREAD;
+        trdid_t    trdid = this->trdid;
+        pid_t      pid   = this->process->pid;
+        uint32_t   lid   = this->core->lid;
+        uint32_t   cycle = (uint32_t)hal_get_cycles();
+
+        char       buffer[CONFIG_PRINTK_BUF_SIZE];
+        va_list    args;
+
+        va_start( args , format );
+
+        // build a string from format
+        int32_t   length = format_to_string( buffer,
+                                             CONFIG_PRINTK_BUF_SIZE,
+                                             format,
+                                             &args );
+        va_end( args );
+
+        if( length > 0  )  // display panic message on TXT0, including formated string
+        {
+            printk("\n[ASSERT] in %s / core[%x,%d] / thread[%x,%x] / cycle %d\n       %s\n",
+            func_name, local_cxy, lid, pid, trdid, cycle, buffer );
+        }
+        else              // display minimal panic message on TXT0
+        {
+            printk("\n[ASSERT] in %s / core[%x,%d] / thread[%x,%x] / cycle %d\n",
+            func_name, local_cxy, lid, pid, trdid, cycle );
+        }
+    }
+}   // end assert( __FUNCTION__,)
+   
+//////////////////////////////////////
+int32_t snprintk( char       * buffer,
+                  uint32_t     size,
+                  char       * format, ... )
+{
+    va_list       args;
+    int32_t       string_length;
+
+    // build args va_list
+    va_start( args , format );
+
+    // build a string from format
+    string_length = format_to_string( buffer , size , format , &args );
+    va_end( args );
+
+    if( (string_length < 0) || (string_length == (int32_t)size) )  // failure
+    {
+        return -1;
+    }
+    else                                                           // success
+    {
+        // add NUL character
+        buffer[string_length] = 0;
+
+        return string_length;
+    }
+}   // end snprintk()
+
+////////////////////////////////
+void puts( const char * string ) 
 {
     uint32_t   n = 0;
@@ -459,16 +367,29 @@
     chdev_t * txt0_ptr = GET_PTR( txt0_xp );
 
-    // get extended pointer on remote TXT0 lock
-    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
-
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
+    if( txt0_xp != XPTR_NULL )
+    {
+        // get extended pointer on remote TXT0 lock
+        xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+        // display string on TTY0
+        remote_busylock_acquire( lock_xp );
+        dev_txt_sync_write( string , n );
+        remote_busylock_release( lock_xp );
+    }
+}   // end puts()
+
+///////////////////////////////////////
+void nolock_puts( const char * string ) 
+{
+    uint32_t   n = 0;
+
+    // compute string length
+    while ( string[n] > 0 ) n++;
 
     // display string on TTY0
     dev_txt_sync_write( string , n );
-
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-}
+  
+}   // end nolock_puts()
+
 
 
@@ -496,16 +417,38 @@
     chdev_t * txt0_ptr = GET_PTR( txt0_xp );
 
-    // get extended pointer on remote TXT0 chdev lock
-    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
-
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
+    if( txt0_xp != XPTR_NULL )
+    {
+        // get extended pointer on remote TXT0 chdev lock
+        xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+        // display buf on TTY0
+        remote_busylock_acquire( lock_xp );
+        dev_txt_sync_write( buf , 10 );
+        remote_busylock_release( lock_xp );
+    }
+}   // end putx()
+
+////////////////////////////////
+void nolock_putx( uint32_t val )
+{
+    static const char HexaTab[] = "0123456789ABCDEF";
+
+    char      buf[10];
+    uint32_t  c;
+
+    buf[0] = '0';
+    buf[1] = 'x';
+
+    // build buffer
+    for (c = 0; c < 8; c++) 
+    { 
+        buf[9 - c] = HexaTab[val & 0xF];
+        val = val >> 4;
+    }
 
     // display buf on TTY0
     dev_txt_sync_write( buf , 10 );
 
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-}
+}   // end nilock_putx()
 
 ////////////////////////
@@ -525,6 +468,36 @@
     xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
 
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
+    if( txt0_xp != XPTR_NULL )
+    {
+        // get TXT0 lock 
+        remote_busylock_acquire( lock_xp );
+
+        if (val < 0) 
+        {
+            val = -val;
+            dev_txt_sync_write( "-" , 1 );
+        }
+
+        for(i = 0; i < 10 ; i++) 
+        {
+            buf[9 - i] = HexaTab[val % 10];
+            if (!(val /= 10)) break;
+        }
+
+        // display buf on TTY0
+        dev_txt_sync_write( &buf[9-i] , i+1 );
+
+        // release TXT0 lock
+        remote_busylock_release( lock_xp );
+    }
+}   // end putd()
+
+///////////////////////////////
+void nolock_putd( int32_t val )
+{
+    static const char HexaTab[] = "0123456789ABCDEF";
+
+    char      buf[10];
+    uint32_t  i;
 
     if (val < 0) 
@@ -543,7 +516,5 @@
     dev_txt_sync_write( &buf[9-i] , i+1 );
 
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-}
+}   // end nolock_putd()
 
 /////////////////////////
@@ -570,16 +541,38 @@
     chdev_t * txt0_ptr = GET_PTR( txt0_xp );
 
-    // get extended pointer on remote TXT0 chdev lock
-    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
-
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
+    if( txt0_xp != XPTR_NULL )
+    {
+        // get extended pointer on remote TXT0 chdev lock
+        xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
+
+        // display string on TTY0
+        remote_busylock_acquire( lock_xp );
+        dev_txt_sync_write( buf , 18 );
+        remote_busylock_release( lock_xp );
+    }
+}   // end putl()
+
+////////////////////////////////
+void nolock_putl( uint64_t val )
+{
+    static const char HexaTab[] = "0123456789ABCDEF";
+
+    char      buf[18];
+    uint32_t  c;
+
+    buf[0] = '0';
+    buf[1] = 'x';
+
+    // build buffer
+    for (c = 0; c < 16; c++) 
+    { 
+        buf[17 - c] = HexaTab[(unsigned int)val & 0xF];
+        val = val >> 4;
+    }
 
     // display string on TTY0
     dev_txt_sync_write( buf , 18 );
 
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-}
+}   // end nolock_putl()
 
 /////////////////////////////
@@ -603,25 +596,28 @@
     xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
 
-    // get TXT0 lock 
-    remote_busylock_acquire( lock_xp );
-
-    // display string on TTY0
-    nolock_printk("\n***** %s *****\n", string );
-
-    for ( line = 0 , byte = 0 ; line < nlines ; line++ )
-    {
-         nolock_printk(" %X | %b %b %b %b | %b %b %b %b | %b %b %b %b | %b %b %b %b |\n",
-         buffer + byte,
-         buffer[byte+ 0],buffer[byte+ 1],buffer[byte+ 2],buffer[byte+ 3],
-         buffer[byte+ 4],buffer[byte+ 5],buffer[byte+ 6],buffer[byte+ 7],
-         buffer[byte+ 8],buffer[byte+ 9],buffer[byte+10],buffer[byte+11],
-         buffer[byte+12],buffer[byte+13],buffer[byte+14],buffer[byte+15] );
-
-         byte += 16;
-    }
-
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-}
+    if( txt0_xp != XPTR_NULL )
+    {
+        // get TXT0 lock 
+        remote_busylock_acquire( lock_xp );
+
+        // display string on TTY0
+        nolock_printk("\n***** %s *****\n", string );
+
+        for ( line = 0 , byte = 0 ; line < nlines ; line++ )
+        {
+            nolock_printk(" %X | %b %b %b %b | %b %b %b %b | %b %b %b %b | %b %b %b %b |\n",
+            buffer + byte,
+            buffer[byte+ 0],buffer[byte+ 1],buffer[byte+ 2],buffer[byte+ 3],
+            buffer[byte+ 4],buffer[byte+ 5],buffer[byte+ 6],buffer[byte+ 7],
+            buffer[byte+ 8],buffer[byte+ 9],buffer[byte+10],buffer[byte+11],
+            buffer[byte+12],buffer[byte+13],buffer[byte+14],buffer[byte+15] );
+
+            byte += 16;
+        }
+
+        // release TXT0 lock
+        remote_busylock_release( lock_xp );
+    }
+}   // end putb()
 
 
