I just found out that the sprintf.asm file distributed by Analog Devices (AD) with their VisualDSP is not even compiling with g21k. Judging from the text it looks like the code was automatically generated.
The code is badly broken, it misses includes and section directives so I don't understand how did it get shipped in the very first place.
Now my biggest problem is, should I write my own sprintf function or should I fix the one broken? I can in principle write it in C instead of assembler (not sure how much will be the difference, though).
Is there anyone out there who ever needed a sprintf while using the ADSP21K targets? Any suggestions?
--
A: Because it fouls the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
The VisualDSP compiler and G21K are not the same. G21K is a GCC derivative, the VDSP compiler is based on the Amsterdam Kit. They are mostly compatible at the C/C++ level, but their assemblers are very different.
If you only really need integer output there are public domain C implementations that work fine and are tiny. I use one based originally on public domain code, extended a bit to handle fixed point using various custom format specifiers. (letters representing "Deci", "centi", "milli" etc).
/* %[flag][width][.prec][mod][conv] flag: - left justify, pad right w/ blanks DONE 0 pad left w/ 0 for numerics DONE + always print sign, + or - no ' ' (blank) no # (???) no
width: (field width) DONE
prec: (precision) no
conv: d,i decimal int DONE u decimal unsigned DONE o octal DONE x,X hex DONE f,e,g,E,G float no c char DONE s string DONE p ptr DONE
mod: N near ptr DONE F far ptr no h short (16-bit) int DONE l long (32-bit) int DONE L long long (64-bit) int no
/***************************************************************************** Stripped-down printf() Chris Giese
formatting link
Release date: Dec 12, 2003 This code is public domain (no copyright). You can do whatever you want with it.
Revised Dec 12, 2003
- fixed vsprintf() and sprintf() in test code
Revised Jan 28, 2002
- changes to make characters 0x80-0xFF display properly
Revised June 10, 2001
- changes to make vsprintf() terminate string with '\0'
Revised May 12, 2000
- math in DO_NUM is now unsigned, as it should be
- %0 flag (pad left with zeroes) now works
- actually did some TESTING, maybe fixed some other bugs
%[flag][width][.prec][mod][conv] flag: - left justify, pad right w/ blanks DONE 0 pad left w/ 0 for numerics DONE + always print sign, + or - no ' ' (blank) no # (???) no
width: (field width) DONE
prec: (precision) no
conv: d,i decimal int DONE u decimal unsigned DONE o octal DONE x,X hex DONE f,e,g,E,G float no c char DONE s string DONE p ptr DONE
mod: N near ptr DONE F far ptr no h short (16-bit) int DONE l long (32-bit) int DONE L long long (64-bit) int no
/* flags used in processing format string */ #define PR_LJ 0x01 /* left justify */ #define PR_CA 0x02 /* use A-F instead of a-f for hex */ #define PR_SG 0x04 /* signed numeric conversion (%d vs. %u) */ #define PR_32 0x08 /* long (32-bit) numeric conversion */ #define PR_16 0x10 /* short (16-bit) numeric conversion */ #define PR_WS 0x20 /* PR_SG set and num was < 0 */ #define PR_LZ 0x40 /* pad left with '0' instead of ' ' */ #define PR_FP 0x80 /* pointers are far */ #define PR_FX 0x100 /* fixed point number */ /* largest number handled is 2^32-1, lowest radix handled is 8. 2^32-1 in base 8 has 11 digits (add 5 for trailing NUL and for slop) */ #define PR_BUFLEN 16
typedef int (*fnptr_t)(unsigned c, void **helper); /***************************************************************************** name: do_printf action: minimal subfunction for ?printf, calls function 'fn' with arg 'ptr' for each character to be output returns:total number of characters output
*****************************************************************************/ int fmt_do_printf(const char *fmt, va_list args, fnptr_t fn, void *ptr) { unsigned flags, actual_wd, count, given_wd; char *where, buf[PR_BUFLEN]; unsigned char state, radix; long num; int precision_display, precision_encode; state = flags = count = given_wd = precision_display = precision_encode = 0; /* begin scanning format specifier list */ for(; *fmt; fmt++) { switch(state) { /* STATE 0: AWAITING % */ case 0: if(*fmt != '%') /* not %... */ { fn(*fmt, &ptr); /* ...just echo it */ count++; break; } /* found %, get next char and advance state to check if next char is a flag */ state++; fmt++; /* FALL THROUGH */ /* STATE 1: AWAITING FLAGS (%-0) */ case 1: if(*fmt == '%') /* %% */ { fn(*fmt, &ptr); count++; state = flags = given_wd = 0; break; } if(*fmt == '-') { if(flags & PR_LJ)/* %-- is illegal */ state = flags = given_wd = 0; else flags |= PR_LJ; break; } /* not a flag char: advance state to check if it's field width */ state++; /* check now for '%0...' */ if(*fmt == '0') { flags |= PR_LZ; fmt++; } /* FALL THROUGH */ /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */ case 2: if(*fmt >= '0' && *fmt 0) { num *= ((int []){0,10,100,1000,10000,100000,1000000})[pow]; } } /* convert binary to octal/decimal/hex ASCII OK, I found my mistake. The math here is _always_ unsigned */ int digit_counter = 0; do { if(flags & PR_FX && digit_counter == precision_display) { *--where = '.'; } unsigned long temp; temp = (unsigned long)num % radix; where--; if(temp < 10) *where = temp + '0'; else if(flags & PR_CA) *where = temp - 10 + 'A'; else *where = temp - 10 + 'a'; num = (unsigned long)num / radix; digit_counter++; } while(num != 0 || ((flags & PR_FX) && digit_counter actual_wd) { fn(flags & PR_LZ ? '0' : ' ', &ptr); count++; given_wd--; } } /* if we pad left with SPACES, do the sign now */ if((flags & (PR_WS | PR_LZ)) == PR_WS) { fn('-', &ptr); count++; } /* emit string/char/converted number */ while(*where != '\0') { fn(*where++, &ptr); count++; } /* pad on right with spaces (for left justify) */ if(given_wd < actual_wd) given_wd = 0; else given_wd -= actual_wd; for(; given_wd; given_wd--) { fn(' ', &ptr); count++; } break; default: break; } default: state = flags = given_wd = 0; break; } } return count; }
My bad, indeed the C runtime library I was compiling with g21k *is* the one delivered with g21k. I had also the archived library (libc.a), but I am working in a GNU/Linux environment and had to rebuild everything from scratch since the librarian lib21k is not available and the /ar/ from binutils is not supporting COFF format (if you are interested there's another post discussing this subject ).
Actually after some talking I also started to ask myself the same question. The main idea behind was to have a debug tool to print some characters in the output data, but then I guess it would be much easier to encode the information I want in a suitable format that I can decode later on when processing the output data.
Part of the debug functions were copied from another embedded project which was on an 8051 and the library did support it. I think I rushed in too quickly asking for an sprintf function before even realizing that I maybe don't need one.
I will try to think harder next time before posting... (even though I think I did...)
Unix/Linix static linking libraries are in a.out (assembler output) format, not in COFF (or ELF, DWARF, PE, etc.). The ar utility does not support any of these linker executable formats.
True enough, but G21K is a GCC derivative - the assembler produces and the library utility operates on a.out format object files.
The *linker* produces COFF executables, but the libraries are not in COFF format.
When I used G21K - also a while ago on 2106x - the linker stripped symbol information and produced absolute addressed load segments that were packaged into simplified COFF executable format sans symbol or string information. I guess AD figured no one needed it. I haven't used G21K since it became a Google project so I don't know if that still is the case.
VDSP 4.x (the last I used) produced ELF executables with symbol and optional debug information, but VDSP also included a chip simulator/debugger that could use the information.
I dug out some of my g21k-compiled files from last century (1999, to be exact). As I remember, but I'm not sure, these were compiled with g21k on a Solaris SPARC workstation. Here is what the "file" command reports for them:
vecdemo.c: ASCII Pascal program text, with CRLF line terminators vecdemo.exe: COFF DSP21k executable, not stripped vecdemo.o: COFF DSP21k relocatable object, not stripped
So the compiler/assembler output, vecdemo.o, is in COFF, as is the linker output vecdemo.exe. Libraries created with "ar" would also contain COFF, then. But perhaps other versions of g21k use other formats.
Yes, I also remember that the symbol-info in the executables was very poor. Especially the poor mapping between source lines and code addresses was a problem. But there was some symbol-info, at least function names and their entry addresses, as I recall.
The binary format(s) is determined by the binary utilities (binutils, as, ld, and friends) and the binary format library (BFD) which is used by the utilities.
The compiler needs some basic support by the binary format, which detemines the minimum support from needed binutils.
Well I have seen projects where they either utilized the VisualDSP background telemetry channel or setup a UART for debugging during run time.
I was just wondering since in my project I run all of the non-signal processing functions on an SiLabs 8051. After some profiling it was painfully obvious I was not getting the bang for my buck by running any of the enduser logic directly on the DSP.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.