.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, tmp1, regbank, sreg
+ .importzp sp, ptr1, ptr2, tmp1, regbank, sreg
.macpack generic
ArgList = regbank+0 ; Argument list pointer
Format = regbank+2 ; Format string
-OutData = regbank+4 ; Function parameters
+OutData = regbank+4 ; Function parameters
; ----------------------------------------------------------------------------
; Other zero page cells
-Base = ptr1
-
+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
adc ptr1+1
sta ptr1+1 ; * 5
asl ptr1
- rol ptr1+1 ; * 10, assume carry clear
+ rol ptr1+1 ; * 10, assume carry clear
pla
adc ptr1 ; Add digit value
sta ptr1
lda ArgLen
ldx ArgLen+1
jsr pushax
- jmp (OutFunc)
+ jmp CallOutFunc
; ----------------------------------------------------------------------------
; ltoa: Wrapper for _ltoa that pushes all arguments
jsr pusheax ; Push value
jsr PushBufPtr ; Push the buffer pointer...
lda Base ; Restore base
- jmp _ultoa ; ultoa (l, s, 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
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
; 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
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
- pha ; 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'
jsr PutBuf
- pla ; Restore low byte
+ pla ; Restore low byte
-@Oct1: ldy #8 ; Load base
- jsr ltoa ; Push arguments, call _ltoa
+@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:
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'
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
-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
+
+
+