.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
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
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
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
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);
+
; ----------------------------------------------------------------------------
;
; 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
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
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
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...
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
; 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?
; 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
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?
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:
; 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'
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?
jsr _strlower ; Make characters lower case
@L2: jmp HaveArg
-; Unsigned format character, skip it
+; Unknown format character, skip it
UnknownFormat:
jmp MainLoop
lda Prec+1
tay
sbc ArgLen+1
- bcc @L1
+ bcs @L1
stx ArgLen
sty ArgLen+1
; 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
Str: .word 0
ArgLen: .res 2
+.data
+
+; Stuff from OutData. Is used as a vector and must be aligned
+CallOutFunc: jmp $0000
+
+
+