Skip to content

Commit

Permalink
Merge pull request #2012 from ghaerr/printf
Browse files Browse the repository at this point in the history
[libc] Rewrite vfprintf.c and tiny_printf.c to use __divmod for speed
  • Loading branch information
ghaerr authored Sep 14, 2024
2 parents 2a9d258 + 5cb1f2c commit e0dc002
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 91 deletions.
6 changes: 4 additions & 2 deletions elks/init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,10 @@ void start_kernel(void)
printk(" 4x: '%4x'\n", 0x2ab);
printk("04d: '%04d'\n", 0x200);
printk(" 4d: '%4d'\n", 0x200);
printk("05d: '%05d'\n", -200);
printk(" 5d: '%5d'\n", -200);
printk("05d: '%05d'\n", -20);
printk(" 5d: '%5d'\n", -20);
printk("+5d: '%5d'\n", -20);
printk("+5d: '%5d'\n", 20);
printk(" ld: '%ld'\n", -123456789L);
printk(" lx: '%lx'\n", 0x87654321L);
printk(" lo: '%lo'\n", 0xFFFFFFFFL);
Expand Down
28 changes: 28 additions & 0 deletions elkscmd/file_utils/cat.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,33 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

static char readbuf[BUFSIZ]; /* use disk block size for stack limit and efficiency*/

#define TEST 0
#if TEST
void test(void)
{
printf("#04X: '%#04X'\n", 0x2ab);
printf("04X: '%04X'\n", 0x2ab);
printf("04x: '%04x'\n", 0x2ab);
printf(" 4x: '%4x'\n", 0x2ab);
printf("04d: '%04d'\n", 0x200);
printf(" 4d: '%4d'\n", 0x200);
printf("05d: '%05d'\n", -20);
printf(" 5d: '%5d'\n", -20);
printf("+5d: '%5d'\n", -20);
printf("+5d: '%5d'\n", 20);
printf(" ld: '%ld'\n", -123456789L);
printf(" lx: '%lx'\n", 0x87654321L);
printf(" lo: '%lo'\n", 0xFFFFFFFFL);
printf(" s: '%s'\n", "thisisatest");
printf(" 6s: '%6s'\n", "thisisatest");
printf("20s: '%20s'\n", "thisisatest");
}
#endif

static int copyfd(int fd)
{
int n;
Expand All @@ -25,6 +49,10 @@ int main(int argc, char **argv)
{
int i, fd;

#if TEST
test();
exit(0);
#endif
if (argc <= 1) {
if (copyfd(STDIN_FILENO)) {
perror("stdin");
Expand Down
101 changes: 64 additions & 37 deletions elkscmd/lib/tiny_vfprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <arch/divmod.h>

static unsigned char bufout[80];

Expand Down Expand Up @@ -51,7 +50,7 @@ FILE stderr[1] =

static void __fflush(FILE *fp)
{
int len;
int len;

/* Return if this is a fake FILE from sprintf */
if (fp->fd < 0)
Expand Down Expand Up @@ -79,21 +78,28 @@ static void __fputc(int ch, FILE *fp)
* the number of characters output.
*/
static int
__fmt(FILE *op, unsigned char *buf, int ljustf, int width, int preci, char pad)
__fmt(FILE *op, unsigned char *buf, int ljustf, int width, int preci, char pad, char sign)
{
int cnt = 0, len;
unsigned char ch;

len = strlen((char *)buf);

if ((preci != -1) && (len > preci)) /* limit max data width */
if (*buf == '-')
sign = *buf++;
else if (sign)
len++;


if (preci != -1 && len > preci) /* limit max data width */
len = preci;

if (width < len) /* flexible field width or width overflow */
width = len;

/*
* at this point: width = total field width len = actual data width
* at this point: width = total field width, len = actual data width
* (including possible sign character)
*/
cnt = width;
width -= len;
Expand All @@ -102,12 +108,21 @@ __fmt(FILE *op, unsigned char *buf, int ljustf, int width, int preci, char pad)
{
if (!ljustf && width) /* left padding */
{
if (len && sign && pad == '0')
goto showsign;
ch = pad;
--width;
}
else if (len)
{
ch = *buf++; /* main field */
if (sign)
{
showsign:
ch = sign; /* sign */
sign = '\0';
}
else
ch = *buf++; /* main field */
--len;
}
else
Expand All @@ -126,43 +141,42 @@ vfprintf(FILE *op, const char *fmt, va_list ap)
{
int i, cnt = 0, ljustf, lval;
int preci, width, radix;
unsigned int c;
unsigned long v;
char pad, dpoint;
char *ptmp;
char tmp[64];
char *p;
char sign;
char buf[64];

while (*fmt)
{
if (*fmt == '%')
{
while (*fmt) {
if (*fmt == '%') {
ljustf = 0; /* left justify flag */
dpoint = 0; /* found decimal point */
sign = '\0'; /* sign char & status */
lval = 0;
pad = ' '; /* justification padding char */
width = -1; /* min field width */
preci = -1; /* max data width */
radix = 10; /* number base */
ptmp = tmp; /* pointer to area to print */
p = buf; /* pointer to area to print */
fmtnxt:
i = 0;
for(;;)
{
for (;;) {
++fmt;
if(*fmt < '0' || *fmt > '9' )
if (*fmt < '0' || *fmt > '9')
break;
i = (i * 10) + (*fmt - '0');
i = i * 10 + *fmt - '0';
if (dpoint)
preci = i;
else if (!i && (pad == ' '))
{
else if (!i && pad == ' ') {
pad = '0';
goto fmtnxt;
}
else
width = i;
}

switch (*fmt)
{
switch (*fmt) {
case '-': /* left justification */
ljustf = 1;
goto fmtnxt;
Expand All @@ -176,43 +190,56 @@ vfprintf(FILE *op, const char *fmt, va_list ap)
case 'h': /* short data */
goto fmtnxt;

case 'd': /* Signed decimal */
ptmp = ltostr((long) ((lval) ? va_arg(ap, long) : va_arg(ap, int)), 10);
goto printit;

case 'o': /* Unsigned octal */
radix = 8;
goto usproc;

case 'x': /* Unsigned hexadecimal */
radix = 16;
/* fall thru */
goto usproc;

case 'd': /* Signed decimal */
v = lval? va_arg(ap, long) : (long)va_arg(ap, int);
if ((long)v < 0) {
v = -(long)v;
sign = '-';
}
goto convert;

case 'u': /* Unsigned decimal */
usproc:
ptmp = ultostr((unsigned long) ((lval)
? va_arg(ap, unsigned long)
: va_arg(ap, unsigned int)), radix);
v = lval? va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int);
convert:
p = buf + sizeof(buf) - 1;
*p = '\0';
do {
c = radix;
v = __divmod(v, &c); /* remainder returned in c */
if (c > 9)
*--p = 'A' - 10 + c;
else
*--p = '0' + c;
} while (v != 0);
goto printit;

case 'c': /* Character */
ptmp[0] = va_arg(ap, int);
ptmp[1] = '\0';
p[0] = va_arg(ap, int);
p[1] = '\0';
goto nopad;

case 's': /* String */
ptmp = va_arg(ap, char*);
p = va_arg(ap, char *);
nopad:
sign = '\0';
pad = ' ';
printit:
cnt += __fmt(op, (unsigned char *)ptmp, ljustf, width, preci, pad);
cnt += __fmt(op, (unsigned char *)p, ljustf, width, preci, pad, sign);
break;

default: /* unknown character */
goto charout;
}
}
else
{
} else {
charout:
__fputc(*fmt, op); /* normal char out */
++cnt;
Expand Down
1 change: 1 addition & 0 deletions libc/asm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SRCS = \
memset-s.S \
strcpy-s.S \
strlen-s.S \
divmod.S \
# end of list

LEFTOUT = \
Expand Down
53 changes: 53 additions & 0 deletions libc/asm/divmod.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Fast 32-bit combined divide and modulo routine
//
// unsigned long __divmod(unsigned long val, unsigned int *baserem)
// Unsigned divide 32-bits by 16-bits
// Store denominator in *baserem before calling
// Returns 32-bit quotient in DX:AX and remainder in *baserem
//
// Designed for a fast replacement of the following code which calls __udivsi3/__umodsi3:
// unsigned int rem, base;
// rem = val % base;
// val = val / base;
// New code:
// rem = base;
// val = __divmod(val, &rem);
//
// inspired by OpenWatcom ltoa.c __uldiv routine
// 13 Sep 2024 Greg Haerr

#include <libc-private/call-cvt.h>

#define NUMLO 2+FAR_ADJ_
#define NUMHI 4+FAR_ADJ_
#define ADDR 6+FAR_ADJ_

.arch i8086, nojumps
.code16
.text

.global __divmod
__divmod:
#ifndef __IA16_CALLCVT_REGPARMCALL
mov %sp,%bx
mov NUMLO(%bx),%ax
mov NUMHI(%bx),%dx
mov ADDR(%bx),%bx
#else
mov %cx,%bx // AX:DX = val, BX = &rem
#endif

// divides DX:AX / [BX]
// returns DX:AX with remainder in [BX]

xor %cx,%cx // temp CX = 0
cmp (%bx),%dx // is upper 16 bits numerator less than denominator
jb 1f // yes - only one DIV needed
xchg %dx,%ax // AX = upper numerator, DX = lower numerator
xchg %dx,%cx // DX = 0, CX = lower numerator
divw (%bx) // AX = upper numerator / base, DX = remainder
xchg %cx,%ax // AX = lower numerator, CX = high quotient
1: divw (%bx) // AX = lower numerator / base, DX = remainder
mov %dx,(%bx) // store remainder
mov %cx,%dx // DX = high quotient, AX = low quotient
RET_(6)
Loading

0 comments on commit e0dc002

Please sign in to comment.