Text Based Console with Integers

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
chrisa128

Text Based Console with Integers

Post 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,
Tim

Re:Text Based Console with Integers

Post 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.
Curufir

Re:Text Based Console with Integers

Post 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.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Text Based Console with Integers

Post 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];
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:Text Based Console with Integers

Post 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
}
Every good solution is obvious once you've found it.
chrisa128

Re:Text Based Console with Integers

Post 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;
}
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:Text Based Console with Integers

Post 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.
Every good solution is obvious once you've found it.
Schol-R-LEA

Re:Text Based Console with Integers

Post 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.
Schol-R-LEA

Re:Text Based Console with Integers

Post 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();
    */
}
Schol-R-LEA

Re:Text Based Console with Integers

Post 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.
Post Reply