OpenCores
URL https://opencores.org/ocsvn/forwardcom/forwardcom/trunk

Subversion Repositories forwardcom

[/] [forwardcom/] [libraries/] [printf_light.as] - Rev 168

Go to most recent revision | Compare with Previous | Blame | View Log

/****************************  printf_light.as ********************************
* Author:        Agner Fog
* date created:  2021-05-24
* Last modified: 2021-05-25
* Version:       1.11
* Project:       ForwardCom library libc_light.li
* Description:   printf, sprintf, snprintf: Print formatted output
* This version is for small CPUs with limited capabilities
* The following instructions are avoided: mul, div, push, pop, sys_call.
* Output to stdout goes directly to output port 10. It does not wait if
* the output buffer is full.
*
* C declarations:
* int printf   (const char * format, ... );
* int sprintf  (char * string, const char * format, ... );
* int snprintf (char * string, size_t max_length, const char * format, ... );
*
* printf:   print to stdout
* sprintf:  print to string
* snprintf: print to string with length limit
* fprintf:  print to file. not implemented yet
*
* These functions are following the definitions in the C standard.
* All standard features are supported, except the following:
* - cannot print floating point numbers
* - cannot print octal
* - cannot print decimal integers with more than 32 bits
*
* Copyright 2021 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/

const section read ip

// call table for dispatching format string specifiers
call_table: int8  (specif_space-def)/4 // ' ': print space or minus
int8  0                                // !
int8  0                                // "
int8  (specif_hash-def)/4              // #: prepend 0x
int8  0                                // $
int8  (specif_percent-def)/4           // %: print percent sign
int8  0                                // &
int8  0                                // '
int8  0                                // (
int8  0                                // )
int8  (specif_star-def)/4              // *: width specified by parameter
int8  (specif_plus-def)/4              // +: print sign
int8  0                                // ,
int8  (specif_minus-def)/4             // -: left justified
int8  0                                // .
int8  0                                // /
int8  (specif_number-def)/4            // 0
int8  (specif_number-def)/4            // 1
int8  (specif_number-def)/4            // 2
int8  (specif_number-def)/4            // 3
int8  (specif_number-def)/4            // 4
int8  (specif_number-def)/4            // 5
int8  (specif_number-def)/4            // 6
int8  (specif_number-def)/4            // 7
int8  (specif_number-def)/4            // 8
int8  (specif_number-def)/4            // 9
int8  0                                // :
int8  0                                // ;
int8  0                                // <
int8  0                                // =
int8  0                                // >
int8  0                                // ?
int8  0                                // @
int8  (specif_hex-def)/4               // A: hexadecimal float
int8  0                                // B
int8  0                                // C
int8  0                                // D 
int8  (specif_float-def)/4             // E: float
int8  (specif_float-def)/4             // F: float
int8  (specif_float-def)/4             // G: float
int8  0                                // H
int8  0                                // I
int8  0                                // J
int8  0                                // K
int8  (specif_l-def)/4                 // L: long (long double)
int8  0                                // M
int8  0                                // N
int8  0                                // O
int8  0                                // P
int8  0                                // Q
int8  0                                // R
int8  0                                // S
int8  0                                // T
int8  0                                // U
int8  0                                // V
int8  0                                // W
int8  (specif_hex-def)/4               // X: hexadecimal
int8  0                                // Y
int8  0                                // Z
int8  0                                // [
int8  0                                // \
int8  0                                // ]
int8  0                                // ^
int8  0                                // _
int8  0                                // `
int8  (specif_hex-def)/4               // a: hexadecimal float
int8  0                                // b
int8  (specif_char-def)/4              // c: character
int8  (specif_dec-def)/4               // d: signed decimal
int8  (specif_float-def)/4             // e: float
int8  (specif_float-def)/4             // f: float
int8  (specif_float-def)/4             // g: float
int8  (specif_h-def)/4                 // h: short
int8  (specif_dec-def)/4               // i: signed decimal
int8  (specif_l-def)/4                 // j: intmax_t
int8  0                                // k
int8  (specif_l-def)/4                 // l: long
int8  0                                // m
int8  (specif_num-def)/4               // n: read number of characters printed so far
int8  0                                // o
int8  (specif_hex-def)/4               // p: pointer, print as hexadecimal
int8  0                                // q
int8  0                                // r
int8  (specif_string-def)/4            // s: string
int8  (specif_l-def)/4                 // t: ptrdiff_t
int8  (specif_uns-def)/4               // u: unsigned decimal
int8  0                                // v
int8  0                                // w
int8  (specif_hex-def)/4               // x: hexadecimal
int8  0                                // y
int8  (specif_l-def)/4                 // z: size_t
const end

code section execute align = 4         // code section


// _printf: print formatted string to stdout
// parameters: r0: format string, r1: parameter list
_printf function public reguse = 0xF, 0
  int64 r2 = r0                        // format string
  int64 r3 = r1                        // parameter list
  int64 sp -= 10*8                     // start saving registers
  int64 [sp+0x30] = r10                // save r10. The rest are saved under printf_generic
  int64 r10 = address ([char_to_stdout])
  jump printf_generic


// _sprintf: print formatted string to string buffer
// parameters: r0: destination string, r1: format string, r2: parameter list
_sprintf function public reguse = 0xF, 0
  int64 r3 = r2                        // parameter list
  int64 r2 = r1                        // format string
  int64 r1 = -1                        // character count limit = UINT_MAX
  int64 sp -= 10*8                     // start saving registers
  int64 [sp+0x30] = r10                // save r10. The rest are saved under printf_generic
  int64 r10 = address ([char_to_string])
  jump printf_generic


// _snprintf: print formatted string to string buffer with limit
// parameters: r0: destination string, r1: length limit, r2: format string, r3: parameter list
_snprintf function public reguse = 0xF, 0
  int64 r1--                           // character count limit. make space for terminating zero
  int64 sp -= 10*8                     // start saving registers
  int64 [sp+0x30] = r10                // save r10. The rest are saved under printf_generic
  int64 r10 = address ([char_to_string])
  // continue in printf_generic

printf_generic:                        // common procedure for all printf variants
  // save r4 - r13
  //int64 sp -= 10*8                   // this is done above
  int64 [sp+0x00] = r4
  int64 [sp+0x08] = r5
  int64 [sp+0x10] = r6
  int64 [sp+0x18] = r7
  int64 [sp+0x20] = r8
  int64 [sp+0x28] = r9
  // int64 [sp+0x30] = r10             // r10 saved above
  int64 [sp+0x38] = r11
  int64 [sp+0x40] = r12
  int64 [sp+0x48] = r13

  if (int64 r2 == 0) {jump finish}     // format string pointer is null
  int r13  = 0                         // reset field width
  int r11 = 0                          // reset modifiers
  int r12 = 0                          // state start
  
  // loop through format string
  while (true) {
    int8 r8 = [r2]                     // read character from format string
    if (int8+ r8 == 0) {break}         // end of format string
    int64 r2++                         // increment format string pointer

    if (int r12 == 0) {                // state start

      if (int8+ r8 == '%') {
        int r12 = 1                    // state after '%'
      }
      else {
        call (r10)                     // print character from format string
      }
      nop

    }
    else {                             // after '%' or modifier
      int r6 = r8 - ' '                // table index
      int64 r4 = address ([unknown_character])
      if (uint r6 > 91) {
        call (r4)                      // call unknown_character
      }
      else {
        int64 r5 = address ([call_table])
        int8 call_relative (r4, [r5 + r6*1])  // dispatch format specifier or modifier
      }
    }
  }
  finish:

  // if printing to string, insert terminating zero
  int64 r4 = address ([char_to_string])
  if (uint64 r10 >= r4) {
    int r4 = 0
    int8 [r0] = r4
  }
  int64 r0 = r9                        // return number of characters written

  // restore r4 - r13
  int64 r4  = [sp+0x00]
  int64 r5  = [sp+0x08]
  int64 r6  = [sp+0x10]
  int64 r7  = [sp+0x18]
  int64 r8  = [sp+0x20]
  int64 r9  = [sp+0x28]
  int64 r10 = [sp+0x30]
  int64 r11 = [sp+0x38]
  int64 r12 = [sp+0x40]
  int64 r13 = [sp+0x48]
  int64 sp += 10*8
  return                               // return from _printf, etc.


/////////////////////////////////////////////////////////////
// subfunctions for different characters in format string
/////////////////////////////////////////////////////////////

specif_space:                          // ' ': print space or minus
  if (int r12 != 1) {jump unknown_character}
  int r11 |= 8                         // set modifier flag
  return

specif_plus:                           // +: print sign
  if (int r12 != 1) {jump unknown_character}
  int r11 |= 4                         // set modifier flag
  return

specif_minus:                          // -: left justified
  if (int r12 != 1) {jump unknown_character}
  int r11 |= 1                         // set modifier flag
  return

specif_hash:                           // hash sign: print prefix 0x
  if (int r12 != 1) {jump unknown_character}
  int r11 |= 0x10                      // set modifier flag
  return

specif_h:                              // h: short
  int r4 = test_bits_or(r11, 0x40)     // is there a preceding h?
  int r11 |= 0x40                      // set modifier flag h
  int r11 |= 0x80, mask = r4           // set modifier flag hh
  int r12 = 3                          // state after sub_specifier
  return

specif_l:                              // l: long
  int r11 |= 0x20                      // set modifier flag l
  int r12 = 3                          // state after sub_specifier
  return

specif_percent:                        // %: print percent sign
  jump unknown_character               // just print % sign and reset state

specif_star:                           // *: width specified by parameter
  if (int r12 != 1) {jump unknown_character}
  int r13 = [r3]                        // read width from paramter
  int64 r3 += 8
  int r12 = 2                          // state after width
  int r11 |= 0x100                     // set modifier flag
  return

specif_number:                         // 0-9
  if (int r12 == 1) {
    if (int r8 == '0') {               // leading '0' means print leading zeroes
      int r11 |= 2                     // set modifier flag 2
      return
    }  
  }
  // number specifies width
  // width = previous_width * 10 + new_digit
  int r4 = r13 << 3                     // multiply width by 10
  int r13 <<= 1
  int r13 += r4
  int r8 -= '0'                        // convert from ASCII
  int r13 += r8                        // new width
  int r12 = 2                          // state after width
  int r11 |= 0x100                     // set modifier flag
  return

// subfunction for writing to stdout
char_to_stdout:
  int8 output(r8, r8, 10)              // write to stdout
  int64 r9++                           // count characters written
  return

// subfunction for writing to string
char_to_string:
  int64 r9++                           // count characters written or potentially written
  if (uint64 r9 > r1) {                // compare with string length limit
    jump char_to_string9               // stop printing
  }
  int8 [r0] = r8                       // write to string
  int64 r0++                           // increment string pointer
char_to_string9:
  return


// %n: read number of characters printed so far
specif_num:                            // n: read number of characters printed so far
  int64 r4 = [r3]                      // read pointer from parameter list
  int64 r3 += 8                        // increment parameter list
  int32 [r4] = r9                      // save number of characters written
  jump reset_state


// %c: Print character
specif_char:                           // c: print character
  int r13--                            // number of leading or trailing spaces to pring
  if (int !(r11 & 1)) {                // field is right justified. print leading spaces
    int r8 = ' '                       // leading spaces
    for (int; r13 > 0; r13--) {
      call (r10)                       // print space
    }
  }
  int8 r8 = [r3]                       // read character from parameter list
  int64 r3 += 8                        // increment parameter list
  call (r10)                           // print character
  int r8 = ' '                         // print any trailing spaces
  for (int; r13 > 0; r13--) {
    call (r10)                         // print space
  }
  jump reset_state


// %s: Print string
specif_string:                         // s: string
  int64 r6 = [r3]                      // read string pointer from parameter list
  int64 r3 += 8                        // increment parameter list
  if (int64 r6 == 0) {jump unknown_character} // null string
  int r4 = r11 ^ 1                     // check if right justified
  int16+ test_bits_and(r4, 0x101), jump_false specif_string2 
  // string is right justified with specified width. check if leading spaces are needed
  for (int r5 = 0; r5 < r13; r5++) {
    int8 r8 = [r6+r5]                  // read string character
    if (int8+ r8 == 0) {break}         // string is shorter. leading spaces needed
  }
  // print leading spaces
  int r8 = ' '
  for (int ; r5 < r13; r5++) {
    call (r10)                         // print space
  }
  int r13 = 0                           // avoid printing trailing spaces also
  specif_string2:
  while (true) {
    int8 r8 = [r6]                     // read character from string
    if (int8+ r8 == 0) {break}         // end of string
    int64 r6++                         // increment string pointer
    call (r10)                         // print character
    int r13--                           // count down field width
  }
  // print any trailing space
  int r8 = ' '
  while (int r13 > 0) {
    call (r10)                         // print character
    int r13--                           // count down field width
  }
  jump reset_state


// %e, %f, %g: Print floating point number
specif_float:
    //jump specif_float1               // (intermediate jump target specif_float placed here to avoid overflow in call table)

    // float not implemented. print as %#LX
    int r11 |= 0x30                    // print 0x prefix, 64 bits
    jump specif_hex


// reference point, default in call table, print unknown character:
def:                                   // default, reference point in call table
unknown_character:                     // unknown character after '%'
  call r10                             // print character
  //jump reset_state
reset_state:                           // reset state after '%' command
  int r11 = 0                          // reset modifiers
  int r12 = 0                          // state start
  int r13 = 0                          // reset field width
  return


// %x: Print hexadecimal
specif_hex:                            // X: hexadecimal
  int r7 = r8 & 0x20                   // is lower case
  int64 r4 = [r3]                      // read integer from parameter list
  int64 r3 += 8                        // increment parameter list
  if (int !(r11 & 0x20)) {             // check if long int
    int32 r4 = r4                      // not long. truncate to 32 bits
  }
specif_hex2:                           // entry from %i (decimal) if number too big
  int64 r5 = bitscan(r4, 1)            // find number of bits
  uint32 r5 >>= 2
  int32 r5++                           // number of digits to print
  int r13 -= r5                        // number of leading or trailing spaces to print
  int r6 = test_bit(r11, 4)            // '#' option
  int r13 -= 2, mask = r6              // make space of 0x prefix

  if (int !(r11 & 1)) {                // field is right justified. print leading spaces
    int r8 = ' '                       // leading spaces
    int r6 = test_bit(r11, 1)          // get bit 1: leading zeroes
    int r8 = '0', mask=r6, fallback=r8 // leading zeroes instead of leading spaces
    while (int r13 > 0) {              // print r13 leading spaces or zeroes
      call r10
      int r13--
    }  
  }

  if (int r11 & 0x10) {                // '#' flag. Print 0x prefix
    int r8 = '0'
    call r10
    int r8 = 'X' | r7                  // 'x' or 'X'
    call r10
  }

  // convert to hexadecimal
  if (int r5 > 8) {                    // must use 64 bits
      int r6 = 16 - r5                 // number of digits to skip
      int r6 <<= 2                     // number of bits to skip
      uint64 r4 <<= r6                 // remove leading zero bits
      for (int ; r5 > 0; r5--) {       // loop for r5 digits
          uint64 r4 = rotate(r4, 4)    // get digit into low position
          int r8 = r4 & 0xF            // get digit
          int r8 += '0'                // convert to ASCII
          int r6 = r8 > '9'            // digit is A - F
          int r8 += 7, mask = r6       // add 7 to get letter A - F
          int r8 |= r7                 // lower case
          call r10                     // print character
      }
  }
  else {   // use 32 bits. CPU may not support 64 bits
      int r6 = 8 - r5                  // number of digits to skip
      int r6 <<= 2                     // number of bits to skip
      uint32 r4 <<= r6                 // remove leading zero bits
      for (int ; r5 > 0; r5--) {       // loop for r5 digits
          uint32 r4 = rotate(r4, 4)    // get digit into low position
          int r8 = r4 & 0xF            // get digit
          int r8 += '0'                // convert to ASCII
          int r6 = r8 > '9'            // digit is A - F
          int r8 += 7,  mask = r6      // add 7 to get letter A - F
          int r8 |= r7                 // lower case
          call r10                     // print character
      }
  }
  // print any trailing spaces
  int r8 = ' '                         // trailing spaces
  while (int r13 > 0) {                // print r13 trailing spaces
    call r10
    int r13--
  }  
  jump reset_state


// %i, %d: Print signed decimal integer
specif_dec:                            // i: signed decimal
  int64 r4 = [r3]                      // read integer from parameter list
  int64 r3 += 8                        // increment parameter list
  if (int r11 & 0x20) {                // %li: 64-bit integer
    int64 r5 = test_bit(r4, 63)        // get sign bit
    int64 r4 = -r4, mask = r5          // change sign if negative
    int r11 |= 0x200, mask = r5        // set bit to remember negative
    int64 r6 = bitscan(r4, 1)          // find number of bits
    if (int r6 > 31) {                 // cannot handle 64 bits decimal. Do hexadecimal instead
      int r11 |= 0x11                  // prepare for hexadecimal escape. get 0x prefix and left justify
      int r7 = 0                       // use upper case for hexadecimal escape
      if (int8+ r5 & 1) {
        int r8 = '-'                   // print '-'
        call r10
      }
      jump specif_hex2                 // print as hexadecimal
    }
  }
  else {
    if (int r11 & 0x80) {              // %hhi: int8
      int32 r5 = test_bit(r4, 7)       // get sign bit
      int8 r4 = -r4, mask = r5         // change sign if negative. truncate to 8 bits
      int r11 |= 0x200, mask = r5      // set bit to remember negative
    }
    else {
      if (int r11 & 0x40) {            // %hhi: int16
        int32 r5 = test_bit(r4, 15)    // get sign bit
        int16 r4 = -r4, mask = r5      // change sign if negative. truncate to 16 bits
        int r11 |= 0x200, mask = r5    // set bit to remember negative      
      }
      else {
        // %i: int32
        int32 r5 = test_bit(r4, 31)    // get sign bit
        int32 r4 = -r4, mask = r5      // change sign if negative. truncate to 16 bits
        int r11 |= 0x200, mask = r5    // set bit to remember negative
      }
    }
  }
  jump specif_uns2                     // sign has been stored in r11. continue in unsigned


// %u: Print unsigned decimal integer
specif_uns:                           // u: unsigned decimal
  int64 r4 = [r3]                      // read integer from parameter list
  int64 r3 += 8                        // increment parameter list
  if (int r11 & 0x20) {                // %li: 64-bit integer
    int64 r6 = bitscan(r4, 1)          // find number of bits
    int r8 |= 0x10                     // prepare for hexadecimal escape. get 0x prefix
    int r7 = 0                         // use upper case for hexadecimal escape
    if (int r6 > 31) {jump specif_hex2}// cannot handle 64 bits decimal. Do hexadecimal instead
  }

specif_uns2:
  int64 sp -= 8                        // save r14
  int64 [sp] = r14

  // 32 bit decimal signed or unsigned
  // First two BCD digits are made with simple subtraction to avoid the need for a larger bit field
  int r5 = 0                           // upper two BCD digits

  while (uint32 r4 >= 1000000000) {
  nop
    uint32 r4 -= 1000000000
    uint32 r5 += 0x10
    nop
  }
  while (uint32 r4 >= 100000000) {
    uint32 r4 -= 100000000
    uint32 r5 += 0x01
  }
 
  // Generate 8 BCD digits using double dabble algorithm
  int32 r14 = 0                        // generate BCD in r14 
  for (int r6 = 0; r6 < 32; r6++) {    // loop for 32 bits
      int32 r7 = r14 + 0x33333333      // digit values 5-9 will set bit 3 in each 4-bit nibble
      int32 r7 &= 0x88888888           // isolate bit 3 in each nibble
      int32 r8 = r7 >> 3               // generate value 3 in nibbles with value 5-9
      int32 r7 >>= 2
      int32 r7 |= r8                   // this will have 3 for each nibble with a value 5-9
      int32 r14 += r7                  // add 3 to nibble values 5-9 to generate 8-12
      int32 r14 = funnel_shift(r4, r14, 31)  // shift most significant bit of r4 into r14
      int32 r4 <<= 1
  }
  // r5:r14 = BCD value

  // determine width
  int r6 = bitscan(r14, 1)             // number of significant bits
  uint r6 >>= 2                        // number of significant digits in low part
  int r6++                             // number of digits in low part
  if (int r5 != 0) {                   // high part is nonzero
    int r6 = bitscan(r5, 1)            // find number of digits in high part
    uint r6 >>= 2
    int r6 += 9                        // number of digits total
  }
  int r7 = test_bits_or(r11, 0x20C)    // leading sign needed
  int r6 += r7                         // number of characters needed
  int r13 -= r6                        // number of leading or trailing spaces needed

  if (int !(r11 & 1)) {                // right justified
    int r6 = r13 > 0 && r7             // leading sign && leading spaces
    int r4 = test_bit(r11, 1), options=1, fallback=r6 // leading zeroes and leading sign. sign must come first
    int r8 = ' '                       // leading sign to print
    int r6 = test_bit(r11, 2)          // flag for '+' prefix
    int r8 = '+', mask=r6, fallback=r8 // leading + required if not negative
    int r6 = test_bit(r11, 9)          // flag for negative
    int r8 = '-', mask=r6, fallback=r8 // leading -. value is negative
    if (int r4 & 1) {                  // sign must come before leading zeroes
      call r10                         // print leading sign
      int r7 = 0                       // remember leading sign has been written
    }
    // print leading spaces or zeroes
    int r8 = ' '                       // space
    int r6 = test_bit(r11, 1)          // flag for leading zeroes
    int r8 = '0', mask=r6, fallback=r8
    while (int r13 > 0) {              // write leading spaces or zeroes
      call r10
      int r13--
    }
  }
  if (int r7 & 1) {                  // sign after leading spaces
    int r8 = ' '                     // space
    int r6 = test_bit(r11, 2)        // flag for '+' prefix
    int r8 = '+', mask=r6, fallback=r8 // leading + required if not negative
    int r6 = test_bit(r11, 9)        // flag for negative
    int r8 = '-', mask=r6, fallback=r8 // leading -. value is negative
    call r10                         // write sign
  }

  int r4 = 0                           // remember if first digit has been printed

  // print high two digits
  uint32 r5 <<= 24                     // left justify high part
  for (int r6 = 2; r6 > 0; r6--) {     // print two high digits if any
      int32 r5 = rotate(r5, 4)         // get most significant digit first
      int8 r8 = r5 & 0x0F
      int r4 = (r8 != 0) || r4         // digit has been printed
      int8 r8 += '0'                   // convert to ASCII
      if (int r4 != 0) {
          call r10                     // print character to stdout
      }
  }

  // print low 8 decimal digits
  for (int r6 = 8; r6 > 0; r6--) {
      int32 r14 = rotate(r14, 4)       // get most significant digit first
      int8 r8 = r14 & 0x0F
      int r4 = (r8 != 0) || r4         // digit has been printed
      int r4 = (r6 == 1) || r4         // last digit must be printed
      int8 r8 += '0'                   // convert to ASCII
      if (int r4 != 0) {
          call r10                     // print character to stdout
      }
  }

  // print trailing spaces
  int r8 = ' '                         // space
  while (int r13 > 0) {                // write trailing spaces
    call r10
    int r13--
  }

  int64 r14 = [sp]                     // restore r14
  int64 sp += 8                        

  jump reset_state                     // finished


  // %e, %f, %g: Print floating point number. not implemented
specif_float1:
  nop
  jump reset_state                     // finished

code end

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.