]> git.sur5r.net Git - cc65/blobdiff - libsrc/common/_printf.s
The spans do now contain the size of a span, no longer the end offset.
[cc65] / libsrc / common / _printf.s
index f0013c7ae830d42a8515d72d588707004feb4648..a8470df3e55ddc802ee455b41c0cd34e7d79c84e 100644 (file)
@@ -6,11 +6,9 @@
 
        .export         __printf
 
-       .import         popax, pushax, pusheax, push1, axlong, axulong
-       .import         __ctype
+       .import         popax, pushax, pusheax, decsp6, push1, axlong, axulong
        .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
+ArgList                = regbank+0             ; Argument list pointer
+Format                 = regbank+2             ; Format string
+OutData                = regbank+4             ; Function parameters
 
+; ----------------------------------------------------------------------------
+; Other zero page cells
+
+Base           = ptr1
+FSave          = ptr1
+FCount         = ptr2
 
 .code
 
@@ -54,8 +58,8 @@ Output1:
        lda     #<CharArg
        ldx     #>CharArg
        jsr     pushax
-       jsr     push1
-       jmp     (OutFunc)       ; fout (OutData, &CharArg, 1)
+       jsr     push1
+       jmp     CallOutFunc     ; fout (OutData, &CharArg, 1)
 
 ; ----------------------------------------------------------------------------
 ; Decrement the argument list pointer by 2
@@ -109,52 +113,52 @@ GetIntArg:
        rts
 
 ; ----------------------------------------------------------------------------
-; Add the a new digit in X to the value in ptr1. Does leave Y alone.
+; Read an integer from the format string. Will return zero in Y.
 
-AddDigit:
-       txa                             ; Move digit into A
+ReadInt:
+       ldy     #0
+       sty     ptr1
+       sty     ptr1+1                  ; Start with zero
+@Loop: lda     (Format),y              ; Get format string character
        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
+       bcc     @L9                     ; Jump if done
+       cmp     #9+1
+       bcs     @L9                     ; Jump if done
 
-; ----------------------------------------------------------------------------
-; Read an integer from the format string. Will return zero in Y.
+; Skip the digit character
 
-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
+       jsr     IncFormatPtr
+
+; Add the digit to the value we have in ptr1
+
+       pha                             ; Save digit value
+       lda     ptr1
+       ldx     ptr1+1
+       asl     ptr1
+       rol     ptr1+1                  ; * 2
+       asl     ptr1
+       rol     ptr1+1                  ; * 4, assume carry clear
+               adc     ptr1
+       sta     ptr1
+       txa
+       adc     ptr1+1
+       sta     ptr1+1                  ; * 5
+       asl     ptr1
+       rol     ptr1+1                  ; * 10, assume carry clear
+       pla
+               adc     ptr1                    ; Add digit value
+       sta     ptr1
+       bcc     @Loop
+       inc     ptr1+1
+       bcs     @Loop                   ; Branch always
+
+; We're done converting
 
 @L9:   lda     ptr1
-       ldx     ptr1+1                  ; Load result
+       ldx     ptr1+1                  ; Load result
        rts
 
+
 ; ----------------------------------------------------------------------------
 ; Put a character into the argument buffer and increment the buffer index
 
@@ -164,15 +168,15 @@ PutBuf:   ldy     BufIdx
        rts
 
 ; ----------------------------------------------------------------------------
-; Get a pointer to the current buffer end
+; Get a pointer to the current buffer end and push it onto the stack
 
-GetBufPtr:
-       lda     #<Buf
-       ldx     #>Buf
-       add     BufIdx
-       bcc     @L1
-       inx
-@L1:   rts
+PushBufPtr:
+       lda     #<Buf
+       ldx     #>Buf
+       add     BufIdx
+       bcc     @L1
+       inx
+@L1:   jmp     pushax
 
 ; ----------------------------------------------------------------------------
 ; Push OutData onto the software stack
@@ -207,7 +211,27 @@ OutputArg:
        lda     ArgLen
        ldx     ArgLen+1
        jsr     pushax
-       jmp     (OutFunc)
+       jmp     CallOutFunc
+
+; ----------------------------------------------------------------------------
+; ltoa: Wrapper for _ltoa that pushes all arguments
+
+ltoa:  sty     Base                    ; Save base
+       jsr     pusheax                 ; Push value
+       jsr     PushBufPtr              ; Push the buffer pointer...
+               lda     Base                    ; Restore base
+       jmp     _ltoa                   ; ultoa (l, s, base);
+
+
+; ----------------------------------------------------------------------------
+; ultoa: Wrapper for _ultoa that pushes all arguments
+
+ultoa:         sty     Base                    ; Save base
+       jsr     pusheax                 ; Push value
+       jsr     PushBufPtr              ; Push the buffer pointer...
+               lda     Base                    ; Restore base
+       jmp     _ultoa                  ; ultoa (l, s, base);
+
 
 ; ----------------------------------------------------------------------------
 ;
@@ -216,23 +240,24 @@ __printf:
 
 ; Save the register bank variables into the save area
 
-       ldx     #5
-Save:  lda     regbank,x
-       sta     RegSave,x
-       dex
+       pha                             ; Save low byte of ap
+               ldy     #5
+Save:  lda     regbank,y
+       sta     RegSave,y
+       dey
                bpl     Save
 
 ; Get the parameters from the stack
 
-       jsr     popax                   ; Argument list pointer
-       sta     ArgList
+       pla                             ; Restore low byte of ap
+       sta     ArgList                 ; Argument list pointer
        stx     ArgList+1
 
        jsr     popax                   ; Format string
        sta     Format
        stx     Format+1
 
-       jsr     popax                   ; Output descriptor
+       jsr     popax                   ; Output descriptor
        sta     OutData
        stx     OutData+1
 
@@ -248,24 +273,85 @@ Save:     lda     regbank,x
 
        iny
        lda     (OutData),y
-       sta     OutFunc
+       sta     CallOutFunc+1
        iny
        lda     (OutData),y
-       sta     OutFunc+1
+       sta     CallOutFunc+2
 
 ; 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
+       lda     Format                  ; Remember current format pointer
+       sta     FSave
+       lda     Format+1
+       sta     FSave+1
+
+       ldy     #0                      ; Index
+@L1:   lda     (Format),y              ; Get next char
+               beq     @L2                     ; Jump on end of string
+       cmp     #'%'                    ; Format spec?
+       beq     @L2
+       iny                             ; Bump pointer
+               bne     @L1
+       inc     Format+1                ; Bump high byte of pointer
+       bne     @L1                     ; Branch always
+
+; Found a '%' character or end of string. Update the Format pointer so it is
+; current (points to this character).
+
+@L2:   tya                             ; Low byte of offset
+       add     Format
+       sta     Format
+       bcc     @L3
+       inc     Format+1
+
+; Calculate, how many characters must be output. Beware: This number may
+; be zero. A still contains the low byte of the pointer.
+
+@L3:   sub     FSave
+       sta     FCount
+       lda     Format+1
+       sbc     FSave+1
+       sta     FCount+1
+       ora     FCount                  ; Is the result zero?
+               beq     @L4                     ; Jump if yes
+
+; Output the characters that we have until now. To make the call to out
+; faster, build the stack frame by hand (don't use pushax)
+
+       jsr     decsp6                  ; 3 args
+       ldy     #5
+       lda     OutData+1
+       sta     (sp),y
+       dey
+       lda     OutData
+       sta     (sp),y
+       dey
+       lda     FSave+1
+       sta     (sp),y
+       dey
+       lda     FSave
+       sta     (sp),y
+       dey
+       lda     FCount+1
+       sta     (sp),y
+       dey
+       lda     FCount
+       sta     (sp),y
+       jsr     CallOutFunc             ; Call the output function
+
+; We're back from out(), or we didn't call it. Check for end of string.
+
+@L4:   jsr     GetFormatChar           ; Get one char, zero in Y
+       tax                             ; End of format string reached?
+               bne     NotDone                 ; End not reached
 
 ; End of format string reached. Restore the zeropage registers and return.
 
-       ldx     #5
+       ldx     #5
 Rest:  lda     RegSave,x
-       sta     regbank,x
-       dex
+       sta     regbank,x
+       dex
        bpl     Rest
        rts
 
@@ -275,12 +361,12 @@ Rest:     lda     RegSave,x
 NotDone:
        cmp     #'%'
        bne     @L1
-       lda     (Format),y              ; Check for "%%"
+       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
+       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
@@ -296,9 +382,8 @@ FormatSpec:
        dex
        bpl     @L1
 
-; Start with reading the flags if there are any
-
-       ldx     #$FF                    ; "true" flag
+; Start with reading the flags if there are any. X is $FF which is used
+; for "true"
 
 ReadFlags:
        lda     (Format),y              ; Get next char...
@@ -374,19 +459,21 @@ ReadPrec:
 
 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'
+       cmp     #'z'                    ; size_t - same as unsigned
+       beq     @L2
+       cmp     #'h'                    ; short - same as int
+       beq     @L2
+        cmp     #'t'                    ; ptrdiff_t - same as int
+        beq     @L2
+        cmp     #'j'                    ; intmax_t/uintmax_t - same as long
+        beq     @L1
+       cmp     #'L'                    ; long double
+       beq     @L1
+       cmp     #'l'                    ; long int
        bne     DoFormat
-       lda     #$FF
+@L1:    lda    #$FF
        sta     IsLong
-@L1:   jsr     IncFormatPtr
+@L2:   jsr     IncFormatPtr
        jmp     ReadMod
 
 ; Initialize the argument buffer pointers. We use a static buffer (ArgBuf) to
@@ -414,9 +501,9 @@ DoFormat:
 ; 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
+       sta     Buf                     ; Place it as zero terminated string...
+       lda     #0
+       sta     Buf+1                   ; ...into the buffer
        jmp     HaveArg                 ; Done
 
 ; Is it an integer?
@@ -440,7 +527,7 @@ CheckInt:
 
 ; Integer argument
 
-       jsr     GetSignedArg                    ; Get argument as a long
+       jsr     GetSignedArg            ; Get argument as a long
        ldy     sreg+1                  ; Check sign
        bmi     @Int1
        ldy     Leader
@@ -448,11 +535,8 @@ CheckInt:
        sty     Buf
        inc     BufIdx
 
-@Int1: jsr     pusheax
-       jsr     GetBufPtr
-       jsr     pushax
-       lda     #10
-       jsr     _ltoa                   ; ltoa (va_arg (ap, long), s, 10);
+@Int1: ldy     #10                     ; Base
+       jsr     ltoa                    ; Push arguments, call _ltoa
        jmp     HaveArg
 
 ; Is it a count pseudo format?
@@ -478,33 +562,44 @@ CheckCount:
 
 CheckOctal:
        cmp     #'o'
-       bne     CheckString
+       bne     CheckPointer
 
 ; 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
+       jsr     GetSignedArg            ; Get argument as a long
+               ldy     AltForm                 ; Alternative form?
+       beq     @Oct1                   ; Jump if no
+       pha                             ; 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
+       ora     Prec+1                  ; Check if value or Prec != 0
        beq     @Oct1
        lda     #'0'
-       sta     Buf
-       inc     BufIdx
-       tya                             ; Restore low byte
+       jsr     PutBuf
+       pla                             ; 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);
+@Oct1: ldy     #8                      ; Load base
+       jsr     ltoa                    ; Push arguments, call _ltoa
        jmp     HaveArg
 
+; Check for a pointer specifier (%p)
+
+CheckPointer:
+       cmp     #'p'
+       bne     CheckString
+
+; It's a pointer. Use %#x conversion
+
+       ldx     #0
+       stx     IsLong                  ; IsLong = 0;
+       inx
+               stx     AltForm                 ; AltForm = 1;
+       lda     #'x'
+       bne     IsHex                   ; Branch always
+
 ; Check for a string specifier (%s)
 
 CheckString:
@@ -527,24 +622,21 @@ CheckUnsigned:
 ; 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);
+               ldy     #10                     ; Load base
+               jsr     ultoa                   ; Push arguments, call _ultoa
        jmp     HaveArg
 
 ; Check for a hexadecimal integer (%x)
 
 CheckHex:
        cmp     #'x'
-       beq     @IsHex
+       beq     IsHex
        cmp     #'X'
        bne     UnknownFormat
 
 ; Hexadecimal integer
 
-@IsHex:        pha                             ; Save the format spec
+IsHex: pha                             ; Save the format spec
        lda     AltForm
        beq     @L1
        lda     #'0'
@@ -552,12 +644,9 @@ CheckHex:
        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);
+@L1:   jsr     GetUnsignedArg          ; Get argument as an unsigned long
+               ldy     #16                     ; Load base
+               jsr     ultoa                   ; Push arguments, call _ultoa
 
        pla                             ; Get the format spec
        cmp     #'x'                    ; Lower case?
@@ -567,7 +656,7 @@ CheckHex:
        jsr     _strlower               ; Make characters lower case
 @L2:   jmp     HaveArg
 
-; Unsigned format character, skip it
+; Unknown format character, skip it
 
 UnknownFormat:
        jmp     MainLoop
@@ -594,7 +683,7 @@ HaveArg:
        lda     Prec+1
        tay
        sbc     ArgLen+1
-       bcc     @L1
+        bcs    @L1
        stx     ArgLen
        sty     ArgLen+1
 
@@ -663,10 +752,6 @@ HaveArg:
 ; 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
 
@@ -689,3 +774,10 @@ Buf:               .res    20
 Str:           .word   0
 ArgLen:                .res    2
 
+.data
+
+; Stuff from OutData. Is used as a vector and must be aligned
+CallOutFunc:   jmp     $0000
+
+
+