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:
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:
Or for an 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