Shop OBEX P1 Docs P2 Docs Learn Events
A smaller printf ... — Parallax Forums

A smaller printf ...

jazzedjazzed Posts: 11,803
edited 2013-04-05 12:04 in Propeller 1
The printf function is probably the most used in a C library.
The standard, full featured printf can eat 12KB or more HUB memory.

Attached is a version of Eric's COG printf that can be used with LMM, CMM, or XMM modes.
This is a result of questions regarding reducing memory footprint.

The actual compiled size of this file in CMM mode is 648 bytes, and LMM mode is 1152 bytes (compiler from the default branch).

Advantages:

* No COG usage.
* Just drop contents below into your project to save KBs of memory.

Caveats:

* Only suitable for serial output up to 57600 baud.
* No stdio devices, no file IO.
* No floating point specifier like %f.
* Only %d, %x, %c, and %s specifies available.
/*
 * very simple printf, adapted from one written by me
 * for the MiNT OS long ago
 * placed in the public domain
 *   - Eric Smith
 */
#include <ctype.h>
#include <stdarg.h>
#include "propeller.h"


/* globals that the loader may change; these represent the default
 * pins to use
 */
unsigned int _rxpin = 31;
unsigned int _txpin = 30;
unsigned int _baud = 57600;
unsigned int _bitcycles;


HUBTEXT int putchar(int origval)
{
  int value = origval;
  int i;
  int txmask = (1<<_txpin);
  int bitcycles;
  int waitcycles;


  _DIRA |= txmask;
  _OUTA |= txmask;


  if (origval == '\n')
    putchar('\r');


  if (_bitcycles == 0)
    _bitcycles = _clkfreq / _baud;


  bitcycles = _bitcycles;
  waitcycles = _CNT + bitcycles;


  value = (value | 256) << 1;
  for (i = 0; i < 10; i++)
  {
      waitcycles = waitcnt2(waitcycles, bitcycles);
      if (value & 1)
        _OUTA |= txmask;
      else
        _OUTA &= ~txmask;
      value >>= 1;
  }
  return origval;
}


/*
 * very simple printf -- just understands a few format features
 */


static HUBTEXT int PUTC(int c)
{
    putchar(c);
    return 1;
}


static HUBTEXT int PUTS(const char *s)
{
    int r = 0;
    while (*s) {
      putchar(*s++);
      r++;
    }
    return r;
}




static HUBTEXT int PUTL(unsigned long u, int base, int width, int fill_char)
{
    int r = 0;
    static char obuf[32];
    char *t;


    t = obuf;


    do {
        *t++ = "0123456789abcdef"[u % base];
        u /= base;
        width--;
    } while (u > 0);


    while (width-- > 0) {
        putchar(fill_char);
        r++;
    }
    while (t != obuf) {
        putchar(*--t);
        r++;
    }
    return r;
}


static HUBTEXT int _doprnt( const char *fmt, va_list args )
{
    int outbytes = 0;
    char c, fill_char;
    char *s_arg;
    int i_arg;
    long l_arg;
    int width;


    while( (c = *fmt++) != 0 ) {
        if (c != '%') {
            outbytes += PUTC(c);
            continue;
        }
        c = *fmt++;
        width = 0;
        fill_char = ' ';
        if (c == '0') fill_char = '0';
        while (c && isdigit(c)) {
            width = 10*width + (c-'0');
            c = *fmt++;
        }
        if (!c) break;


        switch (c) {
        case '%':
            outbytes += PUTC(c);
            break;
        case 'c':
            i_arg = va_arg(args, int);
            outbytes += PUTC(i_arg);
            break;
        case 's':
            s_arg = va_arg(args, char *);
            outbytes += PUTS(s_arg);
            break;
        case 'd':
        case 'u':
              l_arg = va_arg(args, int);
            if (l_arg < 0 && c == 'd') {
                outbytes += PUTC('-');
                width--;
                l_arg = -l_arg;
            }
            outbytes += PUTL(l_arg, 10, width, fill_char);
            break;
        case 'x':
                l_arg = va_arg(args, unsigned int);
            outbytes += PUTL(l_arg, 16, width, fill_char);
            break;


        }
    }
    return outbytes;
}


HUBTEXT int printf(const char *fmt, ...)
{
    va_list args;
    int r;
    va_start(args, fmt);
    r = _doprnt(fmt, args);
    va_end(args);
    return r;
}

Comments

  • RaymanRayman Posts: 14,665
    edited 2013-04-04 02:55
    How does this compare to using the -ltiny GCC linker option?
    The note I have says "inlude a smaller standard I/O library with no floating point".

    I suppose to use this, you add this file and remove all the "#include <stdio.h>" lines in your code, right?
  • jazzedjazzed Posts: 11,803
    edited 2013-04-04 07:34
    Here are some snapshots with 3 LMM builds and 1 CMM build results.
    The difference is relatively small, but sometimes just a little bit helps.

    #include <stdio.h> doesn't make any difference here because there is no file IO.

    Carrying the LMM/CMM kernels in the image costs about 2KB.
    Not having those would eliminate parallel LMM/CMM COG functions.


    attachment.php?attachmentid=100664&d=1365085144

    attachment.php?attachmentid=100665&d=1365085158

    attachment.php?attachmentid=100666&d=1365085169

    attachment.php?attachmentid=100669&d=1365085572
    716 x 430 - 90K
    718 x 432 - 93K
    719 x 432 - 94K
    713 x 429 - 87K
  • photomankcphotomankc Posts: 943
    edited 2013-04-05 12:04
    Awesome! I was working on something like that a while back because I hated having to chose between blinking LEDs for debug or having any non-trivial program thrown into an XMMC project because you wanted print a value or two. I was sort-of mimicking the Debug() functions in Arduino. This will help a great deal in that regard. Less for me to do!
Sign In or Register to comment.