0

I am working on creating my own prinf() for a boot loader I am working on for a class assignment, this means I have to use the BCC compiler and I cannot use system libraries since they do not exist. I do have the ability to use the putc() function designed in assembly and the string library functions strcmp, etc to help me as needed.

I seem to have run into a logic issue.

If I define this in a test file compiled on Linux (cc):

int a = 0;
int b = 1;
int i1 = 0;
int i2 = 1;
unsigned long x = 42949672;
printf("\nHI! :%d: :%d: :%d: :%d: :%d: :%d:\n", x,a,b,i1,i2,x);

I can then run ./a.out and I receive HI! :42949672: :0: :1: :0: :1: :42949672: which is correct.

I've created my own printf function and when I see things printed I see HI! :23592: :655: :0: :1: :0: :1:, which is not correct. I've tried printing with integers only and it works fine, but when I try to print the unsigned long, I run into problems.

Here is my code:

void prints(s) char *s;
{
        int i;
        for(i=0; i<strlen(s); i++)
                putc(s[i]);
}

void gets(s) char *s;
{
        //LEC9.pdf
        while( (*s=getc()) != '\r')
        {
                putc(*s++);
        }
 *s = '\0';
}

//EXAMPLE CODE
char *ctable = "0123456789ABCDEF";
int rpi(x, BASE) unsigned long x; int BASE;
{
        char c;
        if (x)
 {
                c = ctable[x % BASE];
                rpi(x / BASE, BASE);
                putc(c);
        }
 return 0;
}

void printc(ip) unsigned long;
{
        putc(ip);
}
int printd(ip) unsigned long;
{
        if(ip < 0)
        {
                putc('-');
                ip = -ip;
        }
 if(ip == 0)
 {
  putc('0');
  return 0;
 }
 rpi(ip, 10);
}
void printx(ip) unsigned long;
{
        prints("0x"); //PUT OR OUTPUT LOOK LIKE INT
        rpi(ip, 16);
}
int printl(ip) unsigned long;
{
 if(ip == 0)
 {
  putc('0');
  return 0;
 }
        rpi(ip, 10);
        putc('L');
}
void printf(fmt) char *fmt;
{
        char *cp;               //POINTER TO LOOP THROUGH
        unsigned long *ip;     //POINTER FOR

        cp = fmt;               //SET POINTER TO START POINTER {FMT}
        ip = &fmt+1;            //Board says &fmt:but will not work without +1

        while(*cp)
        {
                //IF C != %
                if(*cp != '%')
                {
                        printf("%c", *cp);
                        if(*cp == '\n')
                        {
                                //putc('\n'); //implied
                                putc('\r');
                        }
                        cp++;
                        continue; //NEXT CHAR
                }
                else
                {
                        //MOVE ONE CHARACTER (%{x}) SO WE CAN GET x
                        cp++;
                        switch(*cp)
                        {
                                case 'c':
                                        printc(*ip);
                                        break;
                                case 's':
                                        prints(*ip);
                                        break;
                                case 'd':
                                        printd(*ip);
                                        break;
                                case 'x':
                                        printx(*ip);
                                        break;
                                case 'l':
                                        printl(*ip);
                                        break;
                                default:
                                        break;
                        }               }
                cp++;
                ip++;
        }
}

Anyone have any tips as I've been stuck and need some help.

EDIT (2:06pm): I've changed all my u16/unsigned short to unsigned long and things have changed to printing HI! :L: :0: :0: :: :: :1:

fuz
  • 88,405
  • 25
  • 200
  • 352
user591162
  • 559
  • 2
  • 6
  • 12
  • 1
    Don't write code without prototypes! The `unsigned short;` after `printc(ip)` does nothing; `ip` is still an `int`. – Jonathan Leffler Jan 30 '11 at 22:01
  • 1
    The line `int rpi(x, BASE) unsigned 32 x; int BASE;` won't compile under orthodox C compilers. – Jonathan Leffler Jan 30 '11 at 22:05
  • I am using the bcc compiler as this program is part of our OS dev class, we are using ext2 images to build slowly our own OS. It compiles using bcc -c main.c, as86 -o main.o main.s, ld86 -s main.o /usr/lib/bcc/libc.a -o u.out – user591162 Jan 30 '11 at 22:09
  • 1
    The code in `printf()` is so platform-specific, it is going to be very difficult for us to guess what might and what might not work for your environment. If you must handle variable length argument lists without ``, then you should at least take care to emulate it. What you have is extremely dubious. – Jonathan Leffler Jan 30 '11 at 22:09

5 Answers5

4

Here's a hint:

>>> hex(42949672)
'0x28f5c28'
>>> hex(23592)
'0x5c28'

Somewhere along the way you use a shorter type. I'd check your use of u16, for example.

thkala
  • 84,049
  • 23
  • 157
  • 201
  • I've fixed my u16 to unsigned long, but things are still printing out of order which should not be happening I do not think unless I have a logic error somewhere. – user591162 Jan 30 '11 at 22:01
3

What architecture are you programming for?

If you are writing a boot loader for x86, then your boot loader will at first be in 16 bit mode. Thus when the compiler issues a push instruction, which is how I would guess it passes the arguments for the printf() function, it will by default push 16 bits of data. The long data type will be a specially issued instruction (or two) to push all 32 bits onto the stack, this is assuming an int is 16 bits and a long is 32 bits (which for a 16bit mode compiler, is not an unreasonable assumption, I don't think).

So, lets assume x86 in 16 bit mode:

It would appear you are using *ip to address the arguments as they are pushed on the stack. Since ip is a pointer to a long (a 32 bit data type) when you do ip++ you are incrementing the actual value held by the pointer by 4, as in if *ip = 0x1234 then *(ip+1) = 0x1238. Thus if you are using ip++ for the 16bit ints, then you are skipping an int every time you do ip++, since ints are only 2 bytes (16 bits). A possible solution is to use void * for ip and then add sizeof(data type) to ip; i.e if you print an int, so an:

void *ip = &(fmt + 1); /* Skip over fmt. */
...
ip += sizeof(int);

Or for an unsigned long:

ip += sizeof(unsigned long);

However, without more specific details about the exact architecture you are programming for and and what ABI your compiler is using, I can only wildly speculate.

I hope this helps.

Regards, Alex

Alex W
  • 142
  • 3
  • Ahh, your welcome. I'm glad I could help. Was I right about the 16 bit mode x86 boot loader? – Alex W Jan 31 '11 at 03:34
2

Assuming that "BCC" compiler is at least somewhat modern, you should use its <stdarg.h> macros va_start, va_arg and va_end to access the variable arguments.

Modern C also requires a full prototype with ... for vararg functions.

jilles
  • 10,509
  • 2
  • 26
  • 39
  • The problem with that is I am writing a boot loader, so header libraries do not exist, this means stdarg,stdio,etc do not exist. I can only use putc and the basic string functions. – user591162 Jan 30 '11 at 22:00
  • 4
    @user591162: `` is a core part of C; it has to be provided in free-standing and hosted environments - unlike ``. And if your teacher tells you otherwise, your teacher is seriously deluded. – Jonathan Leffler Jan 30 '11 at 22:03
  • I am in an OS class and basically we are working with our own EXT2 images, using our own boot loader we boot the image, so the header files do not exist and we are not permitted to copy them over. We have to do this using putc and the basic string functions. – user591162 Jan 30 '11 at 22:08
1

Your rpi function is using u16 (unsigned short I guess) as data type which is far too small for an integer with the value 42949672.

codymanix
  • 28,510
  • 21
  • 92
  • 151
  • I should have seen that, but changing u16 to unsigned long prints out "HI! :P: :: :0: :1: :0: :1:", it still seems like things are shifting in a different order when they should not be. Any other tips that might help? – user591162 Jan 30 '11 at 21:58
1

You're better off writing and verifying more basic test cases before you try so many % arguments.

James
  • 9,064
  • 3
  • 31
  • 49
  • Well, the thing is everything works, it's just when I throw in an unsigned long everything is broken, that's why I assumed I had some kind of logic error and am testing with printing only %d's to see what is going on but so far no luck. – user591162 Jan 30 '11 at 22:25
  • What happens when you only have one unsigned long? unsigned long x = 42949672; printf("%d", x); – James Jan 30 '11 at 22:48
  • When I tested with one variable it worked, after all these changes it does not work anymore. I'm just going to have to redo things and close this question. Thanks for the help – user591162 Jan 30 '11 at 22:55