365 lines
5.4 KiB
C
365 lines
5.4 KiB
C
|
/**
|
|||
|
* @file xprintf.h
|
|||
|
* @brief Support library for formatted output.
|
|||
|
* @author Miguel I. Garcia Lopez / FloppySoftware
|
|||
|
*
|
|||
|
* Support library for formatted output,
|
|||
|
* for MESCC (Mike's Enhanced Small C Compiler for Z80 & CP/M).
|
|||
|
*
|
|||
|
* All functions with formatted output like printf(), fprintf()
|
|||
|
* and sprintf() call some private functions in this order:
|
|||
|
* - pf_sf()
|
|||
|
* - pf_s()
|
|||
|
* - pf_out()
|
|||
|
*
|
|||
|
* Revisions:
|
|||
|
* - 19 Mar 2001 : Last revision.
|
|||
|
* - 16 Apr 2007 : GPL'd.
|
|||
|
* - 09 Dec 2016 : Documented. Optimized. GPL v3.
|
|||
|
* - 02 Aug 2017 : Output '%%' as '%'.
|
|||
|
*
|
|||
|
* Copyright (c) 1999-2016 Miguel I. Garcia Lopez / FloppySoftware.
|
|||
|
*
|
|||
|
* Licensed under the GNU General Public License v3.
|
|||
|
*
|
|||
|
* http://www.floppysoftware.es
|
|||
|
* floppysoftware@gmail.com
|
|||
|
*/
|
|||
|
#ifndef XPRINTF_H
|
|||
|
|
|||
|
#define XPRINTF_H
|
|||
|
|
|||
|
// Dependencies
|
|||
|
// ------------
|
|||
|
|
|||
|
#ifndef STRING_H
|
|||
|
#include <string.h>
|
|||
|
#endif
|
|||
|
|
|||
|
// Private globals
|
|||
|
// ---------------
|
|||
|
|
|||
|
BYTE xpf_err; // True on error
|
|||
|
|
|||
|
extern WORD xpf_out; // Output function
|
|||
|
extern WORD xpf_end; // End function
|
|||
|
|
|||
|
int xpf_fw; // Field width
|
|||
|
BYTE xpf_fa; // Field alignment: 0=left, 1=right
|
|||
|
BYTE xpf_fz; // True on zero filling
|
|||
|
|
|||
|
int xpf_cnt; // # of characters sent
|
|||
|
|
|||
|
/**
|
|||
|
* @fn int xprintf(WORD funout, WORD funend, WORD adrpars)
|
|||
|
* @brief Formatted output.
|
|||
|
*
|
|||
|
* This function performs formatted output. It is used
|
|||
|
* by printf(), fprintf() and sprintf() functions.
|
|||
|
*
|
|||
|
* The format is indicated in the string as follows:
|
|||
|
*
|
|||
|
* %[-][0][w]t
|
|||
|
*
|
|||
|
* | - : Left align (default: right align).
|
|||
|
* | 0 : Zero filling on right align.
|
|||
|
* | w : Width for alignment. If the specified width
|
|||
|
* | is lower than the argument length, output is
|
|||
|
* | done without aligment. Care with sprinf()!
|
|||
|
* | t : d = Signed decimal integer.
|
|||
|
* | u = Unsigned decimal integer.
|
|||
|
* | x = Hexadecimal integer.
|
|||
|
* | s = String.
|
|||
|
* | c = Character.
|
|||
|
*
|
|||
|
* The pair %% outputs a single %.
|
|||
|
*
|
|||
|
* @param funout - function to output a character
|
|||
|
* @param funend - function to end output
|
|||
|
* @param adrpars - arguments addresses
|
|||
|
* @return # of characters sent on sucess, -1 on failure
|
|||
|
*/
|
|||
|
xprintf(funout, funend, adrpars)
|
|||
|
WORD funout, funend;
|
|||
|
WORD *adrpars;
|
|||
|
{
|
|||
|
WORD *parg; // Pointer to arguments
|
|||
|
char *pfor; // Pointer to formatted string
|
|||
|
int ivalue;
|
|||
|
char ch;
|
|||
|
|
|||
|
// Setup
|
|||
|
xpf_out = funout;
|
|||
|
xpf_end = funend;
|
|||
|
|
|||
|
pfor = *adrpars;
|
|||
|
parg = --adrpars;
|
|||
|
|
|||
|
xpf_err = xpf_cnt = 0;
|
|||
|
|
|||
|
// Loop
|
|||
|
while((ch = *pfor++))
|
|||
|
{
|
|||
|
if(ch == '%')
|
|||
|
{
|
|||
|
// Character %
|
|||
|
if(*pfor == '%')
|
|||
|
{
|
|||
|
pf_out(ch);
|
|||
|
++pfor;
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// Align
|
|||
|
if(*pfor == '-')
|
|||
|
{
|
|||
|
xpf_fa = 0; // Left align
|
|||
|
++pfor;
|
|||
|
}
|
|||
|
else
|
|||
|
xpf_fa = 1; // Right align
|
|||
|
|
|||
|
// Zero filling
|
|||
|
if(*pfor == '0')
|
|||
|
{
|
|||
|
xpf_fz = 1; // Zero filling
|
|||
|
++pfor;
|
|||
|
}
|
|||
|
else
|
|||
|
xpf_fz = 0;
|
|||
|
|
|||
|
// Width
|
|||
|
xpf_fw = 0;
|
|||
|
|
|||
|
while(*pfor >= '0' && *pfor <= '9')
|
|||
|
xpf_fw = xpf_fw * 10 + (*pfor++) - '0';
|
|||
|
|
|||
|
// Type
|
|||
|
switch(ch = *pfor++)
|
|||
|
{
|
|||
|
case 'd' :
|
|||
|
ivalue = *parg--;
|
|||
|
pf_dec(ivalue);
|
|||
|
break;
|
|||
|
case 'u' :
|
|||
|
ivalue = *parg--;
|
|||
|
pf_udec(ivalue);
|
|||
|
break;
|
|||
|
case 'x' :
|
|||
|
ivalue = *parg--;
|
|||
|
pf_hex(ivalue);
|
|||
|
break;
|
|||
|
case 'c' :
|
|||
|
pf_cf(*parg--);
|
|||
|
break;
|
|||
|
case 's' :
|
|||
|
pf_sf(*parg--);
|
|||
|
break;
|
|||
|
case '\0' :
|
|||
|
--pfor;
|
|||
|
// P'abajo
|
|||
|
default :
|
|||
|
pf_out('!');
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
pf_out(ch);
|
|||
|
|
|||
|
if(xpf_err)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
pf_end();
|
|||
|
|
|||
|
return xpf_err ? -1 : xpf_cnt;
|
|||
|
}
|
|||
|
|
|||
|
// void pf_sf(char *s) : output formatted string.
|
|||
|
|
|||
|
pf_sf(s)
|
|||
|
char *s;
|
|||
|
{
|
|||
|
int len;
|
|||
|
char fill;
|
|||
|
|
|||
|
if(xpf_fw)
|
|||
|
{
|
|||
|
if((len = strlen(s)) < xpf_fw)
|
|||
|
{
|
|||
|
xpf_fw = xpf_fw-len;
|
|||
|
|
|||
|
if(xpf_fa)
|
|||
|
{
|
|||
|
// Left align
|
|||
|
fill = (xpf_fz ? '0' : ' ');
|
|||
|
|
|||
|
while(xpf_fw--)
|
|||
|
pf_out(fill);
|
|||
|
pf_s(s);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Right align
|
|||
|
pf_s(s);
|
|||
|
|
|||
|
while(xpf_fw--)
|
|||
|
pf_out(' ');
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pf_s(s);
|
|||
|
}
|
|||
|
|
|||
|
// void pf_cf(char c) : output formatted character.
|
|||
|
|
|||
|
pf_cf(c)
|
|||
|
char c;
|
|||
|
{
|
|||
|
char tmp[2];
|
|||
|
|
|||
|
tmp[0] = c; tmp[1] = '\0';
|
|||
|
|
|||
|
pf_sf(tmp);
|
|||
|
}
|
|||
|
|
|||
|
unsigned char xpf_dst[7]; // Buffer for numbers
|
|||
|
unsigned char *xpf_dpt; // Buffer pointer
|
|||
|
|
|||
|
// void pf_dec(int i) : output signed decimal integer.
|
|||
|
|
|||
|
pf_dec(i)
|
|||
|
int i;
|
|||
|
{
|
|||
|
xpf_dpt = xpf_dst;
|
|||
|
|
|||
|
if(i < 0)
|
|||
|
{
|
|||
|
*xpf_dpt++ = '-'; i = -i;
|
|||
|
}
|
|||
|
|
|||
|
pf_dec2(i);
|
|||
|
|
|||
|
*xpf_dpt = '\0';
|
|||
|
|
|||
|
pf_sf(xpf_dst);
|
|||
|
}
|
|||
|
|
|||
|
// void pf_dec2(int i) : helper for pf_dec().
|
|||
|
|
|||
|
pf_dec2(i)
|
|||
|
int i;
|
|||
|
{
|
|||
|
int n;
|
|||
|
|
|||
|
if(n = i / 10)
|
|||
|
pf_dec2(n);
|
|||
|
|
|||
|
*xpf_dpt++ = i % 10 + '0';
|
|||
|
}
|
|||
|
|
|||
|
// void pf_udec(unsigned int i) : output unsigned decimal integer.
|
|||
|
|
|||
|
pf_udec(i)
|
|||
|
unsigned i;
|
|||
|
{
|
|||
|
xpf_dpt = xpf_dst;
|
|||
|
|
|||
|
pf_udec2(i);
|
|||
|
|
|||
|
*xpf_dpt = '\0';
|
|||
|
|
|||
|
pf_sf(xpf_dst);
|
|||
|
}
|
|||
|
|
|||
|
// void pf_udec2(unsigned int i) : helper for pf_udec().
|
|||
|
|
|||
|
pf_udec2(i)
|
|||
|
unsigned i;
|
|||
|
{
|
|||
|
unsigned n;
|
|||
|
|
|||
|
if(n = i / 10)
|
|||
|
pf_udec2(n);
|
|||
|
|
|||
|
*xpf_dpt++ = i % 10 + '0';
|
|||
|
}
|
|||
|
|
|||
|
// void pf_hex(unsigned int i) : output hexadecimal integer.
|
|||
|
|
|||
|
pf_hex(i)
|
|||
|
unsigned i;
|
|||
|
{
|
|||
|
xpf_dpt = xpf_dst;
|
|||
|
|
|||
|
pf_hex2(i);
|
|||
|
|
|||
|
*xpf_dpt = '\0';
|
|||
|
|
|||
|
pf_sf(xpf_dst);
|
|||
|
}
|
|||
|
|
|||
|
// void pf_hex2(unsigned int i) : helper for pf_hex().
|
|||
|
|
|||
|
pf_hex2(i)
|
|||
|
unsigned i;
|
|||
|
{
|
|||
|
unsigned n;
|
|||
|
|
|||
|
if(n = i / 16)
|
|||
|
pf_hex2(n);
|
|||
|
|
|||
|
i %= 16;
|
|||
|
|
|||
|
*xpf_dpt++ = i < 10 ? '0' + i : 'A' + i - 10;
|
|||
|
}
|
|||
|
|
|||
|
// void pf_s(char *s) : output string.
|
|||
|
|
|||
|
pf_s(s)
|
|||
|
char *s;
|
|||
|
{
|
|||
|
while(*s)
|
|||
|
pf_out(*s++);
|
|||
|
}
|
|||
|
|
|||
|
// void pf_out(char c) : output character.
|
|||
|
|
|||
|
#asm
|
|||
|
pf_out:
|
|||
|
PUSH HL
|
|||
|
DEFB 0CDH
|
|||
|
xpf_out:
|
|||
|
DEFW 0
|
|||
|
POP BC
|
|||
|
|
|||
|
EX DE,HL
|
|||
|
|
|||
|
LD HL,(xpf_cnt)
|
|||
|
INC HL
|
|||
|
LD (xpf_cnt),HL
|
|||
|
|
|||
|
LD A,D
|
|||
|
OR E
|
|||
|
RET Z
|
|||
|
;; LD A,255
|
|||
|
LD (xpf_err),A
|
|||
|
RET
|
|||
|
#endasm
|
|||
|
|
|||
|
// void pf_end(void) : end output.
|
|||
|
|
|||
|
#asm
|
|||
|
pf_end:
|
|||
|
DEFB 0C3H
|
|||
|
xpf_end:
|
|||
|
DEFW 0
|
|||
|
#endasm
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|