+++ /dev/null
-/*
- * 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;
- }
- }
-
- }
-}
-
-
-
--- /dev/null
+;
+; _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
+