Send integers from 8051 UART
The existing call to sprintf looks like the right way to do the number formatting, since stdio.h seems to be available. The bufffer buf should contain "0", "1", "2", etc. But it's unclear whether or not that is happening...

A good first diagnostic is to add a banner message, to test whether the 8051 serial link is working at all. Maybe the serial port was not being initialized? Maybe the terminal software is configured to the wrong baud rate? Or maybe the serial cable itself is defective?

As a second diagnostic, try printing a constant, known number value. Suggested code edits below:

char[] szBannerMessage = "\r\n""Hello from 8051""\r\n"; // [MarkU] test message
/* main function */
void main()
{
    initialize(); // [MarkU] -- looks like initialize() wan't being called?
    // [MarkU] -- test serial port by sending a string message
    p = szBannerMessage; puts_p();
    // [MarkU] -- test sprintf decimal number formatting
    sprintf(buf, "%d\n", 231);
    p = buf; puts_p();
    IE = 0x81;
    EX0 = 1;
    EA = 1; 
    while (1)
    {
    }
}

// [MarkU] -- put string.  @pre global char* p points to null-terminated C string.
// encapsulated d-coder's existing code
void puts_p(void)
{
    while(*p)
    {
        P2 = *p; // diagnostic: also send character to port P2.7..P2.0
        putc1(*p++);
    }
}


With these edits, every time the 8051 is reset, its firmware should send:

Hello from 8051
231


If you don't see these either of two lines appear on your terminal window, there's something wrong with the serial link.


reset the 8051 but keep its power on. Maybe the clock isn't stable when first powered up?
the terminal software is configured to the wrong baud rate? 
the serial cable itself is defective?


If you see only the first line but not the 231, then the serial port is working but for some reason the sprintf(buf, "%d", c)  isn't formatting the number.


check the listing, is buf[] declared in the right memory address space?


Further diagnostics: See if your C compiler generates an assembly listing of the actual 8051 instuctions, and a map file showing where all of the functions and variables are. The 8051 has several address spaces, so C compilers that target 8051 have extensions to direct which address space each variable belongs in. Also, check whether the linker is targeting the correct 8051 variant. This microcontroller architecture has been around for decades, and there are lots of variations. If the compiler is building code for a 65K chip and putting data in external memory (xram), but then the target chip is a smaller 2k chip with no xram, that's not going to work.

About the interrupt handler. Long-term it would be better practice to avoid sending serial port strings inside the interrupt handler, because interrupt service routines are usually meant to quickly respond to a hardware condition and return to the foreground task as quickly as possible. Sending a character over the serial port is relatively slow, so the 8051 will be spending a lot of time in the interrupt. It's pretty common for an interrupt routine to just capture the hardware registers and set a global flag, for the main while loop to deal with it later. Also, serial port routines are sometimes implemented using interrupts, so in that kind of system it's important to avoid using the serial port from within another interrupt handler, because the processor is already handling one interrupt and can't manage another until the first one RETI returns and the 8051 exits interrupt mode. But since the main task is an empty loop and the serial routine isn't using interrupts, there should be no conflict in this case.