Page 1 of 1

Text Based Console with Integers

Posted: Sun Oct 19, 2003 3:26 pm
by chrisa128
Hi all,

I've got some debug routines that just print strings to the screens. However I need to print the contents of some integer values... Can anyone advise me how to make an Integer into a Char which I can print to the screen?

Rgds,

Re:Text Based Console with Integers

Posted: Sun Oct 19, 2003 4:08 pm
by Tim
Something like this should work to convert a number to a hex string:

Code: Select all

void itox(char *str, int n)
{
    char buf[9], ptr;
    int digit;
    ptr = buf + 8;
    do
    {
        digit = n % 16;
        n /= 16;
        if (digit >= 10)
            *ptr = digit - 10 + 'A';
        else
            *ptr = digit + '0';
        ptr--;
    } while (n != 0);

    strcpy(str, ptr + 1);
}
I'll leave it as an exercise to the reader to adapt this routine for decimal, octal, binary, etc.

Re:Text Based Console with Integers

Posted: Sun Oct 19, 2003 7:45 pm
by Curufir
Or for an asm example try something on these lines:

Code: Select all

;eax = number to convert
;ds:esi = pointer to string
mov ebx, 0xa   ;We're dividing by 10 to get to base 10
mov ebp, esp   ;Store the inital value of the stack pointer

;Do the conversion
.1:
xor edx, edx  ;Required before division
div ebx       ;Divide eax by 10
add dx, 0x30  ;Adjust decimal digit to ascii char
push dx       ;Dump the char on the stack
or eax, eax   ;Check to see if is anything left to do
jnz .1

;Store in the string
.2:
pop ax        ;Grab a char from the stack
mov byte [ds:esi], al ;Move the char into the string
cmp ebp, esp  ;All chars moved?
je .3         ;If so then end
inc esi       ;Move to next offset of next char in string
jmp .2        ;Otherwise rinse and repeat

.3:
I don't give any guarantees that code is bug free ;D.

Re:Text Based Console with Integers

Posted: Mon Oct 20, 2003 1:48 am
by Pype.Clicker
Tim Robinson wrote:

Code: Select all

        digit = n % 16;
        n /= 16;
        if (digit >= 10)
            *ptr = digit - 10 + 'A';
        else
            *ptr = digit + '0';
The "magic" of displaying numbers consist of two basic techniques:
1. in base N, the digits of a number are the successive remainders of the division by N. E.g, in base 10, 1234 is 4 + 3*10 + 2*10? + 1*10?.
Thus, 4=1234%10, 3=(1234/10)%10, 2=((1234/10)/10)%10, etc.

2. the successive digits (aswell as the successive alphabetic characters) use consecutive codes in the ASCII system. Thus
'1' is '0'+1 and 'B' is 'A'+1. You can also ignore that and code

Code: Select all

   static char hexdigits[]="0123456789abcdef";
    ...
    *ptr=hexdigits[digit];

Re:Text Based Console with Integers

Posted: Mon Oct 20, 2003 6:54 am
by Solar
Taken from my toy "kernel" and adapted to a standalone C function. This is unoptimized, unreviewed stuff and could probably be done much better. Has special formatting for hex and bin; you'd probably want to add oct handling. Assumes a function write(char*) exists.

Code: Select all

void write_int(UINT integer, UINT base = 16)
{
    // prepare C string buffer (array)
    char buf[40];           // enough for 2^128 in decimal notation!
    char* idx = buf;        // pointer into the array
    // "slice off" digits from the back with the modulo operator
    do
    {
        UINT digit = integer % base;
        if (digit < 10)
        {
            *idx++ = '0' + digit;
        }
        else
        {
            *idx++ = 'A' + (digit - 10);
        }
    } while ((integer /= base) > 0);
    // for hex output, add leading zeroes to "round" length to power of 2,
    // and "0x" to declare hexadecimal base.
    if (base == 16)
    {
        while ((idx - buf) < 8) // 32 bit - 8 hex digits
        {
            *idx++ = '0';
        }
        *idx++ = 'x';
        *idx++ = '0';
    }
    if (base == 2)
    {
        while ((idx - buf) < 32) // 32 bit - 32 binary digits
        {
            *idx++ = '0';
        }
        *idx++ = 'b';
    }
    // reverse everything between buf and idx
    char* begidx = buf;     // first unreversed character
    char* endidx = idx - 1; // last unreversed character
    while (begidx < endidx)
    {
        // exchange characters, move indexes
        char tmp = *endidx;
        *endidx-- = *begidx;
        *begidx++ = tmp;
    }
    *idx++ = 0; // terminate string
    write(buf); // write string
}

Re:Text Based Console with Integers

Posted: Mon Oct 20, 2003 3:55 pm
by chrisa128
Ok, thanks, im using the following...

Code: Select all

static void Format_D( char* buf, int val )
{
    char stack[16];
    int top = 0;
    int negative;
    unsigned uval;

    // Convert to sign and unsigned magnitude.
    if ( val < 0 ) {
   negative = 1;
   uval = -val;
    }
    else {
   negative = 0;
   uval = val;
    }

    // Convert magnitude to decimal.  We do this in order of least
    // significant to most significant digit.
    do {
   int digit = uval % 10;
   stack[top++] = digit + '0';
   uval /= 10;
    }
    while ( uval > 0 );

    // Add leading minus sign if negative.
    if ( negative ) {
   *buf++ = '-';
    }

    // Put formatted characters in the output buffer.
    do {
   *buf++ = stack[--top];
    }
    while ( top > 0 );

    // Terminate the output buffer.
    *buf = '\0';
}
And im just calling it as follows....

Code: Select all

void dprintfint(int num)
{
   char buf[64];

   Format_D(buf, num);
   dprintf(buf);
    return;
}

Re:Text Based Console with Integers

Posted: Tue Oct 21, 2003 4:14 am
by Solar
Some bunch of quick & dirty hackers we are (me included). :'(

Note that stdlib.h - which you will have to implement or port sooner or later anyway - contains functions like lltostr() or ultostr(). You might want to implement and, subsequently, use them instead of hacking some special stuff for your kernel debug.

Re:Text Based Console with Integers

Posted: Tue Oct 21, 2003 6:05 pm
by Schol-R-LEA
Actually, while the string-to-numeric functions ([tt]atoi()[/tt], [tt]strtol()[/tt], etc.) are defined in ANSI C, the numeric-to-string functions are not, and different versions of them exist. The de facto standard for the numeric-to-string conversion functions are the GCC prototype definitions:

Code: Select all

char*???itoa (int, char*, int);
char*???ltoa (long, char*, int);

char*???ecvt (double, int, int*, int*);
char*???fcvt (double, int, int*, int*);
char*???gcvt (double, int, char*);

char* lltoa(long long, char *, int);
char* ulltoa(unsigned long long , char *, int);
wchar_t* lltow(long long, wchar_t *, int);
wchar_t* ulltow(unsigned long long, wchar_t *, int);
The [tt]ltostr()[/tt] and [tt]ultostr()[/tt] variants do not seem to be as common.

This is based on the information available to me; it may be incorrect or out of date. Comments and corrections are welcome.

Re:Text Based Console with Integers

Posted: Tue Oct 21, 2003 8:42 pm
by Schol-R-LEA
It took me a while to get it right, but here's my version, designed to take advantage of the recursive nature of the underlying algorithm. Oh, I could done an iterative version that was more efficient and took less time to get right, but I just had to be different, didn't I? sigh Damn, I really need to comment this...

Code: Select all

#include <string.h>

const int maxbuffer = 33; // 32 bits + null terminator

char* u2string (unsigned int value, char* buffer, int radix)
{    
    unsigned int digit, reduction;
    char tmp[maxbuffer];
    
    const static char NUMERALS[16][2] = {"0", "1", "2", "3", 
                                          "4", "5", "6", "7",
                                          "8", "9", "A", "B",
                                          "C", "D", "E", "F"}; 
    
    memset(tmp,  '\00', (size_t) maxbuffer);
        
    digit = value % radix;
    reduction = value / radix;

    if (0 != reduction)
    {          
        strcat(buffer, u2string(reduction, tmp, radix));
    }
    strcat(buffer, NUMERALS[digit]); 
    return buffer;
}


char* i2string (int value, char* buffer, int radix)
{
    char tmp[maxbuffer];
    
    memset(tmp,  '\00', (size_t) maxbuffer);

    if ((0 > value) && (10 == radix))
    {
        value = -value;
        buffer[0] = '-';
        buffer[1] = '\00';
    }
    else
    {
        buffer[0] = '\00';
    }
            
    strcat(buffer, u2string((unsigned int) value, tmp, radix));
    return buffer;
}
You probably wouldn't do it this way in the real world, of course. The chief headache I had with this was making sure the scratch buffer I was currently using didn't evaporate before I had concatenated the results into the caller's buffer; the big breakthrough came when I realized I could allocate the scratch buffer in the call prior to the one using it, and pass it as the buffer argument. That way, when the call returned, the scratch buffer was still allocated, and could be appended to the current buffer, which was in turn the scratch buffer of it's caller, and so on. While it is incredibly inefficent in it's use of stack memory, because the function itself is much smaller, it probably doesn't take much more memory overall than the iterative version does, on average (the worst case scenario is a base 2 negative number, requiring 34 function calls with a frame overhead of ~48 bytes = 1632 bytes of stack space).

I also included the test program, just to show I didn't cheat (too much). There's still one small bug in the way u2string() handles zeroes, but other than that, it should work. C&CW.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
// #include <conio.h>  // Windows specific

int main( void )
{
    char buffer1[(maxbuffer * 2) + 2], buffer2[(maxbuffer * 2) + 2];
    int base, i;
  
    memset(buffer1,  '\00', (size_t) ((maxbuffer * 2) + 1));
    memset(buffer2,  '\00', (size_t) ((maxbuffer * 2) + 1));

    for(base = 2; base <= 16; base += 2) 
    {
        printf("%d in base %2d: %s  == %s\n", 12765, base,
                               itoa(12765, buffer1, base),
                               i2string(12765, buffer2, base));
    }

    memset(buffer1,  '\00', (size_t) ((maxbuffer * 2) + 1));
    memset(buffer2,  '\00', (size_t) ((maxbuffer * 2) + 1));

    for(base = 2; base <= 16; base += 2) 
    {
        printf("%d in base %2d: %s  == %s\n", -254, base,
                               itoa(-254, buffer1, base),
                               i2string(-254, buffer2, base));
    }

    memset(buffer1,  '\00', (size_t) ((maxbuffer * 2) + 1));
    memset(buffer2,  '\00', (size_t) ((maxbuffer * 2) + 1));

    for(base = 2; base <= 16; base += 2) 
    {
        printf("%d in base %2d: %s  == %s\n", 0, base,
                               itoa(0, buffer1, base),
                               i2string(0, buffer2, base));
    }
 
    memset(buffer1,  '\00', (size_t) ((maxbuffer * 2) + 1));
    memset(buffer2,  '\00', (size_t) ((maxbuffer * 2) + 1));

    for(base = 2; base <= 16; base += 2) 
    {
        printf("%d in base %2d: %s  == %s\n", 0, base,
                               itoa(0, buffer1, base),
                               u2string(0, buffer2, base));
    }   
    
    /* 
    // Windows specific function getch() used to pause
    // console window in Dev-C++ 
    puts("\nHit any key to continue...");
    getch();
    */
}

Re:Text Based Console with Integers

Posted: Wed Oct 22, 2003 12:29 pm
by Schol-R-LEA
Of course, if all you're doing is printing to the console, then this is the easiest solution:

Code: Select all

#include <stdio.h>

void print_unsigned(unsigned int value, unsigned int radix)
{
    int digit;
    
    digit = value % radix;
    value /= radix;
    
    if (0 != value)
    {
        print_unsigned(value, radix);
    }
        
    putchar ((char) (digit + ((10 > digit) ? (int) '0' : (int) 'A' - 10)));

}

void print_int(int value, unsigned int radix)
{
    print_unsigned((unsigned int)(10 == radix ? abs(value) : value), radix);
} 
Since this doesn't save the value, you don't have to play games with extra buffers the way that I had to with [tt]i2string()[/tt] above.