mirror of
https://github.com/cc65/cc65.git
synced 2025-01-25 11:30:06 +00:00
_printf rewritten in assembler - basic tests ok, needs some more tests and
optimizations. git-svn-id: svn://svn.cc65.org/cc65/trunk@502 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
d3e6d87662
commit
643f468295
@ -1,7 +1,6 @@
|
|||||||
_afailed.s
|
_afailed.s
|
||||||
_fopen.s
|
_fopen.s
|
||||||
_hextab.s
|
_hextab.s
|
||||||
_printf.s
|
|
||||||
abort.s
|
abort.s
|
||||||
bsearch.s
|
bsearch.s
|
||||||
calloc.s
|
calloc.s
|
||||||
|
@ -15,16 +15,17 @@ C_OBJS = fclose.o fgets.o fprintf.o calloc.o _fopen.o\
|
|||||||
fputs.o fread.o fwrite.o gets.o realloc.o bsearch.o strxfrm.o\
|
fputs.o fread.o fwrite.o gets.o realloc.o bsearch.o strxfrm.o\
|
||||||
printf.o _hextab.o vfprintf.o fdopen.o strtok.o\
|
printf.o _hextab.o vfprintf.o fdopen.o strtok.o\
|
||||||
_afailed.o fopen.o fgetc.o fputc.o puts.o gets.o perror.o getchar.o\
|
_afailed.o fopen.o fgetc.o fputc.o puts.o gets.o perror.o getchar.o\
|
||||||
_printf.o vprintf.o vsprintf.o sprintf.o abort.o qsort.o putchar.o\
|
vprintf.o vsprintf.o sprintf.o abort.o qsort.o putchar.o\
|
||||||
errormsg.o cprintf.o vcprintf.o freopen.o locale.o fsetpos.o\
|
errormsg.o cprintf.o vcprintf.o freopen.o locale.o fsetpos.o\
|
||||||
fgetpos.o rewind.o fseek.o ftell.o
|
fgetpos.o rewind.o fseek.o ftell.o
|
||||||
|
|
||||||
S_OBJS = _fdesc.o \
|
S_OBJS = _fdesc.o \
|
||||||
_file.o \
|
_file.o \
|
||||||
_hadd.o \
|
_hadd.o \
|
||||||
_heap.o \
|
_heap.o \
|
||||||
_oserror.o \
|
_oserror.o \
|
||||||
_stksize.o \
|
_printf.o \
|
||||||
|
_stksize.o \
|
||||||
_swap.o \
|
_swap.o \
|
||||||
_sys.o \
|
_sys.o \
|
||||||
abs.o \
|
abs.o \
|
||||||
@ -58,7 +59,7 @@ S_OBJS = _fdesc.o \
|
|||||||
memcpy.o \
|
memcpy.o \
|
||||||
memset.o \
|
memset.o \
|
||||||
rand.o \
|
rand.o \
|
||||||
setjmp.o \
|
setjmp.o \
|
||||||
stkcheck.o \
|
stkcheck.o \
|
||||||
strcat.o \
|
strcat.o \
|
||||||
strchr.o \
|
strchr.o \
|
||||||
|
@ -1,264 +0,0 @@
|
|||||||
/*
|
|
||||||
* Helper function for the printf family.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include "_printf.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Use static variables for locals */
|
|
||||||
#pragma staticlocals (1);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _printf (struct outdesc* d, const char* f, va_list ap)
|
|
||||||
{
|
|
||||||
outfunc fout; /* Output function */
|
|
||||||
unsigned char type; /* variable argument type */
|
|
||||||
char str [20]; /* string buffer */
|
|
||||||
char c; /* Current format char */
|
|
||||||
char leftjust; /* left justify string */
|
|
||||||
char addsign; /* always add + or - */
|
|
||||||
char addblank; /* add blank instead of + */
|
|
||||||
char altform; /* alternate form? */
|
|
||||||
char padchar; /* pad with space or zeroes? */
|
|
||||||
char islong; /* l modifier found */
|
|
||||||
unsigned arglen; /* length of argument string */
|
|
||||||
unsigned prec; /* Precision */
|
|
||||||
unsigned width; /* Width of output field */
|
|
||||||
int i; /* Integer value */
|
|
||||||
long l; /* Long value */
|
|
||||||
char* sptr; /* pointer to argument string */
|
|
||||||
register char* s; /* work pointer to argument string */
|
|
||||||
|
|
||||||
/* Remember the format string in a register variable for shorter code */
|
|
||||||
register const char* format = f;
|
|
||||||
|
|
||||||
/* Remember the output function in a local variable for speed and size */
|
|
||||||
fout = d->fout;
|
|
||||||
|
|
||||||
/* */
|
|
||||||
d->ccount = 0;
|
|
||||||
while (c = *format++) {
|
|
||||||
|
|
||||||
if (c != '%') {
|
|
||||||
fout (d, &c, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* %%? */
|
|
||||||
if (*format == '%') {
|
|
||||||
fout (d, format, 1);
|
|
||||||
++format;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* format is: %[flags][width][.precision][mod]type */
|
|
||||||
|
|
||||||
/* flags */
|
|
||||||
leftjust = addsign = addblank = altform = 0;
|
|
||||||
do {
|
|
||||||
switch (c = *format) {
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
leftjust = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '+':
|
|
||||||
addsign = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '#':
|
|
||||||
altform = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
addblank = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto flags_done;
|
|
||||||
|
|
||||||
}
|
|
||||||
++format;
|
|
||||||
} while (1);
|
|
||||||
flags_done:
|
|
||||||
|
|
||||||
/* width */
|
|
||||||
padchar = ' ';
|
|
||||||
if (*format == '0') {
|
|
||||||
padchar = '0';
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
if (*format == '*') {
|
|
||||||
width = va_arg (ap, int);
|
|
||||||
++format;
|
|
||||||
} else {
|
|
||||||
width = 0;
|
|
||||||
while (isdigit (c = *format)) {
|
|
||||||
width = width * 10 + (c - '0');
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* precision */
|
|
||||||
prec = 0;
|
|
||||||
if (*format == '.') {
|
|
||||||
++format;
|
|
||||||
if (*format == '*') {
|
|
||||||
prec = va_arg (ap, int);
|
|
||||||
++format;
|
|
||||||
} else {
|
|
||||||
while (isdigit (c = *format)) {
|
|
||||||
prec = prec * 10 + (c - '0');
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* modifiers */
|
|
||||||
islong = 0;
|
|
||||||
while (strchr ("FNhlL", c = *format)) {
|
|
||||||
switch (c) {
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
islong = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the format specifier */
|
|
||||||
sptr = s = str;
|
|
||||||
type = *format++;
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
str [0] = va_arg (ap, char);
|
|
||||||
str [1] = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
|
||||||
case 'i':
|
|
||||||
if (islong) {
|
|
||||||
l = va_arg (ap, long);
|
|
||||||
if (l >= 0) {
|
|
||||||
if (addsign) {
|
|
||||||
*s++ = '+';
|
|
||||||
} else if (addblank) {
|
|
||||||
*s++ = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ltoa (l, s, 10);
|
|
||||||
} else {
|
|
||||||
i = va_arg (ap, int);
|
|
||||||
if (i >= 0) {
|
|
||||||
if (addsign) {
|
|
||||||
*s++ = '+';
|
|
||||||
} else if (addblank) {
|
|
||||||
*s++ = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itoa (i, s, 10);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n':
|
|
||||||
*va_arg (ap, int*) = d->ccount;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'o':
|
|
||||||
if (islong) {
|
|
||||||
l = va_arg (ap, unsigned long);
|
|
||||||
if (altform && (l || prec)) {
|
|
||||||
*s++ = '0';
|
|
||||||
}
|
|
||||||
ultoa (l, s, 8);
|
|
||||||
} else {
|
|
||||||
i = va_arg (ap, unsigned);
|
|
||||||
if (altform && (i || prec)) {
|
|
||||||
*s++ = '0';
|
|
||||||
}
|
|
||||||
utoa (i, s, 8);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
sptr = va_arg (ap, char*);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
if (islong) {
|
|
||||||
ultoa (va_arg (ap, unsigned long), str, 10);
|
|
||||||
} else {
|
|
||||||
utoa (va_arg (ap, unsigned), str, 10);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
if (altform) {
|
|
||||||
*s++ = '0';
|
|
||||||
*s++ = 'X';
|
|
||||||
}
|
|
||||||
if (islong) {
|
|
||||||
ultoa (va_arg (ap, unsigned long), s, 16);
|
|
||||||
} else {
|
|
||||||
utoa (va_arg (ap, unsigned), s, 16);
|
|
||||||
}
|
|
||||||
if (type == 'x') {
|
|
||||||
strlower (str);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Unknown type char - skip it */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do argument string formatting */
|
|
||||||
arglen = strlen (sptr);
|
|
||||||
if (prec && prec < arglen) {
|
|
||||||
arglen = prec;
|
|
||||||
}
|
|
||||||
if (width > arglen) {
|
|
||||||
width -= arglen; /* padcount */
|
|
||||||
} else {
|
|
||||||
width = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do padding on the left side if needed */
|
|
||||||
if (!leftjust) {
|
|
||||||
/* argument right justified */
|
|
||||||
while (width) {
|
|
||||||
fout (d, &padchar, 1);
|
|
||||||
--width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Output the argument string */
|
|
||||||
fout (d, sptr, arglen);
|
|
||||||
|
|
||||||
/* Output right padding bytes if needed */
|
|
||||||
if (leftjust) {
|
|
||||||
/* argument left justified */
|
|
||||||
while (width) {
|
|
||||||
fout (d, &padchar, 1);
|
|
||||||
--width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,9 +20,13 @@ typedef void (*outfunc) (struct outdesc* desc, const char* buf, unsigned count);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Control structure passed to the low level worker function.
|
||||||
|
* Beware: This function will access the structure on the assembly level,
|
||||||
|
* so check this when altering the structure.
|
||||||
|
*/
|
||||||
struct outdesc {
|
struct outdesc {
|
||||||
outfunc fout; /* Routine used to output data */
|
|
||||||
int ccount; /* Character counter */
|
int ccount; /* Character counter */
|
||||||
|
outfunc fout; /* Routine used to output data */
|
||||||
void* ptr; /* Data internal to print routine */
|
void* ptr; /* Data internal to print routine */
|
||||||
unsigned uns; /* Data internal to print routine */
|
unsigned uns; /* Data internal to print routine */
|
||||||
};
|
};
|
||||||
|
691
libsrc/common/_printf.s
Normal file
691
libsrc/common/_printf.s
Normal file
@ -0,0 +1,691 @@
|
|||||||
|
;
|
||||||
|
; _printf: Basic layer for all printf type functions.
|
||||||
|
;
|
||||||
|
; Ullrich von Bassewitz, 21.10.2000
|
||||||
|
;
|
||||||
|
|
||||||
|
.export __printf
|
||||||
|
|
||||||
|
.import popax, pushax, pusheax, push1, axlong, axulong
|
||||||
|
.import __ctype
|
||||||
|
.import _ltoa, _ultoa
|
||||||
|
.import _strlower, _strlen
|
||||||
|
.import jmpvec
|
||||||
|
.importzp sp, ptr1, ptr2, tmp1, regbank, sreg
|
||||||
|
|
||||||
|
.macpack generic
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; We will store variables into the register bank in the zeropage. Define
|
||||||
|
; equates for these variables.
|
||||||
|
|
||||||
|
ArgList = regbank+0 ; Argument list pointer
|
||||||
|
Format = regbank+2 ; Format string
|
||||||
|
OutData = regbank+4 ; Function parameters
|
||||||
|
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get one character from the format string and increment the pointer. Will
|
||||||
|
; return zero in Y.
|
||||||
|
|
||||||
|
GetFormatChar:
|
||||||
|
ldy #0
|
||||||
|
lda (Format),y
|
||||||
|
IncFormatPtr:
|
||||||
|
inc Format
|
||||||
|
bne @L1
|
||||||
|
inc Format+1
|
||||||
|
@L1: rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Output a pad character: outfunc (d, &padchar, 1)
|
||||||
|
|
||||||
|
OutputPadChar:
|
||||||
|
lda PadChar
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Call the output function with one character in A
|
||||||
|
|
||||||
|
Output1:
|
||||||
|
sta CharArg
|
||||||
|
jsr PushOutData
|
||||||
|
lda #<CharArg
|
||||||
|
ldx #>CharArg
|
||||||
|
jsr pushax
|
||||||
|
jsr push1
|
||||||
|
jmp (OutFunc) ; fout (OutData, &CharArg, 1)
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Decrement the argument list pointer by 2
|
||||||
|
|
||||||
|
DecArgList2:
|
||||||
|
lda ArgList
|
||||||
|
sub #2
|
||||||
|
sta ArgList
|
||||||
|
bcs @L1
|
||||||
|
dec ArgList+1
|
||||||
|
@L1: rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get an unsigned int or long argument depending on the IsLong flag.
|
||||||
|
|
||||||
|
GetUnsignedArg:
|
||||||
|
lda IsLong ; Check flag
|
||||||
|
bne GetLongArg ; Long sets all
|
||||||
|
jsr GetIntArg ; Get an integer argument
|
||||||
|
jmp axulong ; Convert to unsigned long
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get an signed int or long argument depending on the IsLong flag.
|
||||||
|
|
||||||
|
GetSignedArg:
|
||||||
|
lda IsLong ; Check flag
|
||||||
|
bne GetLongArg ; Long sets all
|
||||||
|
jsr GetIntArg ; Get an integer argument
|
||||||
|
jmp axlong ; Convert to long
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get a long argument from the argument list. Returns 0 in Y.
|
||||||
|
|
||||||
|
GetLongArg:
|
||||||
|
jsr GetIntArg ; Get high word
|
||||||
|
sta sreg
|
||||||
|
stx sreg+1
|
||||||
|
|
||||||
|
; Run into GetIntArg fetching the low word
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get an integer argument from the argument list. Returns 0 in Y.
|
||||||
|
|
||||||
|
GetIntArg:
|
||||||
|
jsr DecArgList2
|
||||||
|
ldy #1
|
||||||
|
lda (ArgList),y
|
||||||
|
tax
|
||||||
|
dey
|
||||||
|
lda (ArgList),y
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Add the a new digit in X to the value in ptr1. Does leave Y alone.
|
||||||
|
|
||||||
|
AddDigit:
|
||||||
|
txa ; Move digit into A
|
||||||
|
sub #'0' ; Make number from ascii digit
|
||||||
|
pha
|
||||||
|
lda ptr1
|
||||||
|
ldx ptr1+1
|
||||||
|
asl ptr1
|
||||||
|
rol ptr1+1 ; * 2
|
||||||
|
asl ptr1
|
||||||
|
rol ptr1+1 ; * 4
|
||||||
|
add ptr1
|
||||||
|
sta ptr1
|
||||||
|
txa
|
||||||
|
adc ptr1+1
|
||||||
|
sta ptr1+1 ; * 5
|
||||||
|
asl ptr1
|
||||||
|
rol ptr1+1 ; * 10
|
||||||
|
pla
|
||||||
|
add ptr1 ; Add digit value
|
||||||
|
sta ptr1
|
||||||
|
bcc @L1
|
||||||
|
inc ptr1+1
|
||||||
|
@L1: rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Read an integer from the format string. Will return zero in Y.
|
||||||
|
|
||||||
|
ReadInt:
|
||||||
|
ldy #0
|
||||||
|
sty ptr1
|
||||||
|
sty ptr1+1 ; Start with zero
|
||||||
|
@L1: lda (Format),y ; Get format string character
|
||||||
|
tax ; Format character --> X
|
||||||
|
lda __ctype,x ; Get character classification
|
||||||
|
and #$04 ; Digit?
|
||||||
|
beq @L9 ; Jump if done
|
||||||
|
jsr AddDigit ; Add the digit to ptr1
|
||||||
|
jsr IncFormatPtr ; Skip the character
|
||||||
|
jmp @L1
|
||||||
|
|
||||||
|
@L9: lda ptr1
|
||||||
|
ldx ptr1+1 ; Load result
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Put a character into the argument buffer and increment the buffer index
|
||||||
|
|
||||||
|
PutBuf: ldy BufIdx
|
||||||
|
inc BufIdx
|
||||||
|
sta Buf,y
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Get a pointer to the current buffer end
|
||||||
|
|
||||||
|
GetBufPtr:
|
||||||
|
lda #<Buf
|
||||||
|
ldx #>Buf
|
||||||
|
add BufIdx
|
||||||
|
bcc @L1
|
||||||
|
inx
|
||||||
|
@L1: rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Push OutData onto the software stack
|
||||||
|
|
||||||
|
PushOutData:
|
||||||
|
lda OutData
|
||||||
|
ldx OutData+1
|
||||||
|
jmp pushax
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Output Width pad characters
|
||||||
|
;
|
||||||
|
|
||||||
|
PadLoop:
|
||||||
|
jsr OutputPadChar
|
||||||
|
OutputPadding:
|
||||||
|
inc Width
|
||||||
|
bne PadLoop
|
||||||
|
inc Width+1
|
||||||
|
bne PadLoop
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Output the argument itself: outfunc (d, str, arglen);
|
||||||
|
;
|
||||||
|
|
||||||
|
OutputArg:
|
||||||
|
jsr PushOutData
|
||||||
|
lda Str
|
||||||
|
ldx Str+1
|
||||||
|
jsr pushax
|
||||||
|
lda ArgLen
|
||||||
|
ldx ArgLen+1
|
||||||
|
jsr pushax
|
||||||
|
jmp (OutFunc)
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
;
|
||||||
|
|
||||||
|
__printf:
|
||||||
|
|
||||||
|
; Save the register bank variables into the save area
|
||||||
|
|
||||||
|
ldx #5
|
||||||
|
Save: lda regbank,x
|
||||||
|
sta RegSave,x
|
||||||
|
dex
|
||||||
|
bpl Save
|
||||||
|
|
||||||
|
; Get the parameters from the stack
|
||||||
|
|
||||||
|
jsr popax ; Argument list pointer
|
||||||
|
sta ArgList
|
||||||
|
stx ArgList+1
|
||||||
|
|
||||||
|
jsr popax ; Format string
|
||||||
|
sta Format
|
||||||
|
stx Format+1
|
||||||
|
|
||||||
|
jsr popax ; Output descriptor
|
||||||
|
sta OutData
|
||||||
|
stx OutData+1
|
||||||
|
|
||||||
|
; Initialize the output counter in the output descriptor to zero
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
tay
|
||||||
|
sta (OutData),y
|
||||||
|
iny
|
||||||
|
sta (OutData),y
|
||||||
|
|
||||||
|
; Get the output function from the output descriptor and remember it
|
||||||
|
|
||||||
|
iny
|
||||||
|
lda (OutData),y
|
||||||
|
sta OutFunc
|
||||||
|
iny
|
||||||
|
lda (OutData),y
|
||||||
|
sta OutFunc+1
|
||||||
|
|
||||||
|
; Start parsing the format string
|
||||||
|
|
||||||
|
MainLoop:
|
||||||
|
jsr GetFormatChar ; Get one char, zero in Y
|
||||||
|
tax ; End of format string reached?
|
||||||
|
bne NotDone ; Continue of end not reached
|
||||||
|
|
||||||
|
; End of format string reached. Restore the zeropage registers and return.
|
||||||
|
|
||||||
|
ldx #5
|
||||||
|
Rest: lda RegSave,x
|
||||||
|
sta regbank,x
|
||||||
|
dex
|
||||||
|
bpl Rest
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Still a valid format character. Check for '%' and a '%%' sequence. Output
|
||||||
|
; anything that is not a format specifier. On intro, Y is zero.
|
||||||
|
|
||||||
|
NotDone:
|
||||||
|
cmp #'%'
|
||||||
|
bne @L1
|
||||||
|
lda (Format),y ; Check for "%%"
|
||||||
|
cmp #'%'
|
||||||
|
bne FormatSpec ; Jump if really a format specifier
|
||||||
|
jsr IncFormatPtr ; Skip the second '%'
|
||||||
|
@L1: jsr Output1 ; Output the character...
|
||||||
|
jmp MainLoop ; ...and continue
|
||||||
|
|
||||||
|
; We have a real format specifier
|
||||||
|
; Format is: %[flags][width][.precision][mod]type
|
||||||
|
; Y is zero on entry.
|
||||||
|
|
||||||
|
FormatSpec:
|
||||||
|
|
||||||
|
; Initialize the flags
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
ldx #FormatVarSize-1
|
||||||
|
@L1: sta FormatVars,x
|
||||||
|
dex
|
||||||
|
bpl @L1
|
||||||
|
|
||||||
|
; Start with reading the flags if there are any
|
||||||
|
|
||||||
|
ldx #$FF ; "true" flag
|
||||||
|
|
||||||
|
ReadFlags:
|
||||||
|
lda (Format),y ; Get next char...
|
||||||
|
cmp #'-'
|
||||||
|
bne @L1
|
||||||
|
stx LeftJust
|
||||||
|
beq @L4
|
||||||
|
|
||||||
|
@L1: cmp #'+'
|
||||||
|
bne @L2
|
||||||
|
stx AddSign
|
||||||
|
beq @L4
|
||||||
|
|
||||||
|
@L2: cmp #' '
|
||||||
|
bne @L3
|
||||||
|
stx AddBlank
|
||||||
|
beq @L4
|
||||||
|
|
||||||
|
@L3: cmp #'#'
|
||||||
|
bne ReadPadding
|
||||||
|
stx AltForm
|
||||||
|
|
||||||
|
@L4: jsr IncFormatPtr
|
||||||
|
jmp ReadFlags ; ...and start over
|
||||||
|
|
||||||
|
; Done with flags, read the pad char. Y is still zero if we come here.
|
||||||
|
|
||||||
|
ReadPadding:
|
||||||
|
ldx #' ' ; PadChar
|
||||||
|
cmp #'0'
|
||||||
|
bne @L1
|
||||||
|
tax ; PadChar is '0'
|
||||||
|
jsr IncFormatPtr
|
||||||
|
lda (Format),y ; Read current for later
|
||||||
|
@L1: stx PadChar
|
||||||
|
|
||||||
|
; Read the width. Even here, Y is still zero. A contains the current character
|
||||||
|
; from the format string
|
||||||
|
|
||||||
|
ReadWidth:
|
||||||
|
cmp #'*'
|
||||||
|
bne @L1
|
||||||
|
jsr IncFormatPtr
|
||||||
|
jsr GetIntArg ; Width is an additional argument
|
||||||
|
jmp @L2
|
||||||
|
|
||||||
|
@L1: jsr ReadInt ; Read integer from format string...
|
||||||
|
@L2: sta Width
|
||||||
|
stx Width+1 ; ...and remember in Width
|
||||||
|
|
||||||
|
; Read the precision. Even here, Y is still zero.
|
||||||
|
|
||||||
|
sty Prec ; Assume Precision is zero
|
||||||
|
sty Prec+1
|
||||||
|
lda (Format),y ; Load next format string char
|
||||||
|
cmp #'.' ; Precision given?
|
||||||
|
bne ReadMod ; Branch if no precision given
|
||||||
|
|
||||||
|
ReadPrec:
|
||||||
|
jsr IncFormatPtr ; Skip the '.'
|
||||||
|
lda (Format),y
|
||||||
|
cmp #'*' ; Variable precision?
|
||||||
|
bne @L1
|
||||||
|
jsr IncFormatPtr ; Skip the '*'
|
||||||
|
jsr GetIntArg ; Get integer argument
|
||||||
|
jmp @L2
|
||||||
|
|
||||||
|
@L1: jsr ReadInt ; Read integer from format string
|
||||||
|
@L2: sta Prec
|
||||||
|
stx Prec+1
|
||||||
|
|
||||||
|
; Read the modifiers. Y is still zero.
|
||||||
|
|
||||||
|
ReadMod:
|
||||||
|
lda (Format),y
|
||||||
|
cmp #'F'
|
||||||
|
beq @L1 ; Read and ignore this one
|
||||||
|
cmp #'N'
|
||||||
|
beq @L1 ; Read and ignore this one
|
||||||
|
cmp #'h'
|
||||||
|
beq @L1 ; Read and ignore this one
|
||||||
|
cmp #'L'
|
||||||
|
beq @L1 ; Read and ignore this one
|
||||||
|
cmp #'l'
|
||||||
|
bne DoFormat
|
||||||
|
lda #$FF
|
||||||
|
sta IsLong
|
||||||
|
@L1: jsr IncFormatPtr
|
||||||
|
jmp ReadMod
|
||||||
|
|
||||||
|
; Initialize the argument buffer pointers. We use a static buffer (ArgBuf) to
|
||||||
|
; assemble strings. A zero page index (BufIdx) is used to keep the current
|
||||||
|
; write position. A pointer to the buffer (Str) is used to point to the the
|
||||||
|
; argument in case we will not use the buffer but a user supplied string.
|
||||||
|
; Y is zero when we come here.
|
||||||
|
|
||||||
|
DoFormat:
|
||||||
|
sty BufIdx ; Clear BufIdx
|
||||||
|
ldx #<Buf
|
||||||
|
stx Str
|
||||||
|
ldx #>Buf
|
||||||
|
stx Str+1
|
||||||
|
|
||||||
|
; Skip the current format character, then check it (current char in A)
|
||||||
|
|
||||||
|
jsr IncFormatPtr
|
||||||
|
|
||||||
|
; Is it a character?
|
||||||
|
|
||||||
|
cmp #'c'
|
||||||
|
bne CheckInt
|
||||||
|
|
||||||
|
; It is a character
|
||||||
|
|
||||||
|
jsr GetIntArg ; Get the argument (promoted to int)
|
||||||
|
jsr PutBuf
|
||||||
|
lda #0 ; Place it as zero terminated string...
|
||||||
|
jsr PutBuf ; ...into the buffer
|
||||||
|
jmp HaveArg ; Done
|
||||||
|
|
||||||
|
; Is it an integer?
|
||||||
|
|
||||||
|
CheckInt:
|
||||||
|
cmp #'d'
|
||||||
|
beq @L1
|
||||||
|
cmp #'i'
|
||||||
|
bne CheckCount
|
||||||
|
|
||||||
|
; It is an integer
|
||||||
|
|
||||||
|
@L1: ldx #0
|
||||||
|
lda AddBlank ; Add a blank for positives?
|
||||||
|
beq @L2 ; Jump if no
|
||||||
|
ldx #' '
|
||||||
|
@L2: lda AddSign ; Add a plus for positives (precedence)?
|
||||||
|
beq @L3
|
||||||
|
ldx #'+'
|
||||||
|
@L3: stx Leader
|
||||||
|
|
||||||
|
; Integer argument
|
||||||
|
|
||||||
|
jsr GetSignedArg ; Get argument as a long
|
||||||
|
ldy sreg+1 ; Check sign
|
||||||
|
bmi @Int1
|
||||||
|
ldy Leader
|
||||||
|
beq @Int1
|
||||||
|
sty Buf
|
||||||
|
inc BufIdx
|
||||||
|
|
||||||
|
@Int1: jsr pusheax
|
||||||
|
jsr GetBufPtr
|
||||||
|
jsr pushax
|
||||||
|
lda #10
|
||||||
|
jsr _ltoa ; ltoa (va_arg (ap, long), s, 10);
|
||||||
|
jmp HaveArg
|
||||||
|
|
||||||
|
; Is it a count pseudo format?
|
||||||
|
|
||||||
|
CheckCount:
|
||||||
|
cmp #'n'
|
||||||
|
bne CheckOctal
|
||||||
|
|
||||||
|
; It is a count pseudo argument
|
||||||
|
|
||||||
|
jsr GetIntArg
|
||||||
|
sta ptr1
|
||||||
|
stx ptr1+1 ; Get user supplied pointer
|
||||||
|
ldy #0
|
||||||
|
lda (OutData),y ; Low byte of OutData->ccount
|
||||||
|
sta (ptr1),y
|
||||||
|
iny
|
||||||
|
lda (OutData),y ; High byte of OutData->ccount
|
||||||
|
sta (ptr1),y
|
||||||
|
jmp MainLoop ; Done
|
||||||
|
|
||||||
|
; Check for an octal digit
|
||||||
|
|
||||||
|
CheckOctal:
|
||||||
|
cmp #'o'
|
||||||
|
bne CheckString
|
||||||
|
|
||||||
|
; Integer in octal representation
|
||||||
|
|
||||||
|
jsr GetSignedArg ; Get argument as a long
|
||||||
|
ldy AltForm ; Alternative form?
|
||||||
|
beq @Oct1 ; Jump if no
|
||||||
|
tay ; Save low byte of value
|
||||||
|
stx tmp1
|
||||||
|
ora tmp1
|
||||||
|
ora sreg
|
||||||
|
ora sreg+1
|
||||||
|
ora Prec
|
||||||
|
ora Prec+1 ; Check if value or Prec != 0
|
||||||
|
beq @Oct1
|
||||||
|
lda #'0'
|
||||||
|
sta Buf
|
||||||
|
inc BufIdx
|
||||||
|
tya ; Restore low byte
|
||||||
|
|
||||||
|
@Oct1: jsr pusheax ; Push value
|
||||||
|
jsr GetBufPtr ; Get buffer pointer...
|
||||||
|
jsr pushax ; ...and push it
|
||||||
|
lda #8 ; Load base
|
||||||
|
jsr _ltoa ; ltoa (l, s, 8);
|
||||||
|
jmp HaveArg
|
||||||
|
|
||||||
|
; Check for a string specifier (%s)
|
||||||
|
|
||||||
|
CheckString:
|
||||||
|
cmp #'s'
|
||||||
|
bne CheckUnsigned
|
||||||
|
|
||||||
|
; It's a string
|
||||||
|
|
||||||
|
jsr GetIntArg ; Get 16bit argument
|
||||||
|
sta Str
|
||||||
|
stx Str+1
|
||||||
|
jmp HaveArg
|
||||||
|
|
||||||
|
; Check for an unsigned integer (%u)
|
||||||
|
|
||||||
|
CheckUnsigned:
|
||||||
|
cmp #'u'
|
||||||
|
bne CheckHex
|
||||||
|
|
||||||
|
; It's an unsigned integer
|
||||||
|
|
||||||
|
jsr GetUnsignedArg ; Get argument as unsigned long
|
||||||
|
jsr pusheax
|
||||||
|
jsr GetBufPtr ; Get buffer pointer...
|
||||||
|
jsr pushax ; ...and push it
|
||||||
|
lda #10 ; Load base
|
||||||
|
jsr _ultoa ; ultoa (l, s, 10);
|
||||||
|
jmp HaveArg
|
||||||
|
|
||||||
|
; Check for a hexadecimal integer (%x)
|
||||||
|
|
||||||
|
CheckHex:
|
||||||
|
cmp #'x'
|
||||||
|
beq @IsHex
|
||||||
|
cmp #'X'
|
||||||
|
bne UnknownFormat
|
||||||
|
|
||||||
|
; Hexadecimal integer
|
||||||
|
|
||||||
|
@IsHex: pha ; Save the format spec
|
||||||
|
lda AltForm
|
||||||
|
beq @L1
|
||||||
|
lda #'0'
|
||||||
|
jsr PutBuf
|
||||||
|
lda #'X'
|
||||||
|
jsr PutBuf
|
||||||
|
|
||||||
|
@L1: jsr GetUnsignedArg ; Get argument as an unsigned long
|
||||||
|
jsr pusheax
|
||||||
|
jsr GetBufPtr ; Get buffer pointer...
|
||||||
|
jsr pushax ; ...and push it
|
||||||
|
lda #16 ; Load base
|
||||||
|
jsr _ultoa ; ultoa (l, s, 16);
|
||||||
|
|
||||||
|
pla ; Get the format spec
|
||||||
|
cmp #'x' ; Lower case?
|
||||||
|
bne @L2
|
||||||
|
lda Str
|
||||||
|
ldx Str+1
|
||||||
|
jsr _strlower ; Make characters lower case
|
||||||
|
@L2: jmp HaveArg
|
||||||
|
|
||||||
|
; Unsigned format character, skip it
|
||||||
|
|
||||||
|
UnknownFormat:
|
||||||
|
jmp MainLoop
|
||||||
|
|
||||||
|
; We have the argument, do argument string formatting
|
||||||
|
|
||||||
|
HaveArg:
|
||||||
|
|
||||||
|
; ArgLen = strlen (Str);
|
||||||
|
|
||||||
|
lda Str
|
||||||
|
ldx Str+1
|
||||||
|
jsr _strlen ; Get length of argument
|
||||||
|
sta ArgLen
|
||||||
|
stx ArgLen+1
|
||||||
|
|
||||||
|
; if (Prec && Prec < ArgLen) ArgLen = Prec;
|
||||||
|
|
||||||
|
lda Prec
|
||||||
|
ora Prec+1
|
||||||
|
beq @L1
|
||||||
|
ldx Prec
|
||||||
|
cpx ArgLen
|
||||||
|
lda Prec+1
|
||||||
|
tay
|
||||||
|
sbc ArgLen+1
|
||||||
|
bcc @L1
|
||||||
|
stx ArgLen
|
||||||
|
sty ArgLen+1
|
||||||
|
|
||||||
|
; if (Width > ArgLen) {
|
||||||
|
; Width -= ArgLen; /* padcount */
|
||||||
|
; } else {
|
||||||
|
; Width = 0;
|
||||||
|
; }
|
||||||
|
; Since width is used as a counter below, calculate -(width+1)
|
||||||
|
|
||||||
|
@L1: sec
|
||||||
|
lda Width
|
||||||
|
sbc ArgLen
|
||||||
|
tax
|
||||||
|
lda Width+1
|
||||||
|
sbc ArgLen+1
|
||||||
|
bcs @L2
|
||||||
|
lda #0
|
||||||
|
tax
|
||||||
|
@L2: eor #$FF
|
||||||
|
sta Width+1
|
||||||
|
txa
|
||||||
|
eor #$FF
|
||||||
|
sta Width
|
||||||
|
|
||||||
|
; /* Do padding on the left side if needed */
|
||||||
|
; if (!leftjust) {
|
||||||
|
; /* argument right justified */
|
||||||
|
; while (width) {
|
||||||
|
; fout (d, &padchar, 1);
|
||||||
|
; --width;
|
||||||
|
; }
|
||||||
|
; }
|
||||||
|
|
||||||
|
lda LeftJust
|
||||||
|
bne @L3
|
||||||
|
jsr OutputPadding
|
||||||
|
|
||||||
|
; Output the argument itself
|
||||||
|
|
||||||
|
@L3: jsr OutputArg
|
||||||
|
|
||||||
|
; /* Output right padding bytes if needed */
|
||||||
|
; if (leftjust) {
|
||||||
|
; /* argument left justified */
|
||||||
|
; while (width) {
|
||||||
|
; fout (d, &padchar, 1);
|
||||||
|
; --width;
|
||||||
|
; }
|
||||||
|
; }
|
||||||
|
|
||||||
|
lda LeftJust
|
||||||
|
beq @L4
|
||||||
|
jsr OutputPadding
|
||||||
|
|
||||||
|
; Done, parse next chars from format string
|
||||||
|
|
||||||
|
@L4: jmp MainLoop
|
||||||
|
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Local data (all static)
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
; Save area for the zero page registers
|
||||||
|
RegSave: .res 6
|
||||||
|
|
||||||
|
; Stuff from OutData. Is used as a vector and must be aligned
|
||||||
|
.align 2
|
||||||
|
OutFunc: .word 0
|
||||||
|
|
||||||
|
; One character argument for OutFunc
|
||||||
|
CharArg: .byte 0
|
||||||
|
|
||||||
|
; Format variables
|
||||||
|
FormatVars:
|
||||||
|
LeftJust: .byte 0
|
||||||
|
AddSign: .byte 0
|
||||||
|
AddBlank: .byte 0
|
||||||
|
AltForm: .byte 0
|
||||||
|
PadChar: .byte 0
|
||||||
|
Width: .word 0
|
||||||
|
Prec: .word 0
|
||||||
|
IsLong: .byte 0
|
||||||
|
Leader: .byte 0
|
||||||
|
BufIdx: .byte 0 ; Argument string pointer
|
||||||
|
FormatVarSize = * - FormatVars
|
||||||
|
|
||||||
|
; Argument buffer and pointer
|
||||||
|
Buf: .res 20
|
||||||
|
Str: .word 0
|
||||||
|
ArgLen: .res 2
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user