;
; int printf(char *format, ...) -- just like C
;
;   Only supports %d, %x, and %s
;
;   internally:
;     HL = pointer to format string
;     IX = pointer to output buffer
;     IY = pointer to next arg to be processed
;

_printf
        PUSH    IY
        LD      IY, 0
        ADD     IY, SP

        PUSH    AF
        PUSH    IX

        ; IY points at IY on stack
        INC     IY
        INC     IY      ; points at return address
        INC     IY
        INC     IY      ; points at first argument

        LD      L, (IY + 0)     ; format string into HL
        LD      H, (IY + 1)
        INC     IY
        INC     IY

        LD      IX, PRINTF_BUF

PRINTF_LOOP
        LD      A, (HL)
        CP      '%'
        JP      NZ, PRINTF_NOT_PERCENT

        INC     HL
        LD      A, (HL)
        CP      'd'
        JP      NZ, PRINTF_NOT_DECIMAL

        CALL    PRINTF_CONVERT_DECIMAL

        INC     HL
        JP      PRINTF_LOOP

PRINTF_NOT_DECIMAL
        CP      'x'
        JP      NZ, PRINTF_NOT_HEX

        CALL    PRINTF_CONVERT_HEX

        INC     HL
        JP      PRINTF_LOOP

PRINTF_NOT_HEX
        CP      's'
        JP      NZ, PRINTF_NOT_PERCENT

        CALL    PRINTF_CONVERT_STRING

        INC     HL
        JP      PRINTF_LOOP

PRINTF_NOT_PERCENT
        LD      (IX), A
        INC     IX
        INC     HL
        CP      0
        JP      NZ, PRINTF_LOOP

        LD      HL, PRINTF_BUF
        CALL    LCDPRINT
        LD      HL, 0

        POP     IX
        POP     AF
        POP     IY

        RET

PRINTF_BUF      DS      48  ; 24 bytes on the display, pad to be sure

;
; PRINTF_CONVERT_DECIMAL
;
;   Input:
;       IY = pointer to int
;       IX = buffer to convert into
;   Output:
;       IY = incremented past int
;       IX = incremented past decimal version of int
;   Internally:
;       HL = int
;

PRINTF_CONVERT_DECIMAL
        PUSH    HL
        PUSH    AF
        PUSH    BC

        LD      L, (IY + 0)
        LD      H, (IY + 1)
        INC     IY
        INC     IY

        XOR     A               ; reset carry and set A to 0
        LD      B, A            ; BC = 0
        LD      C, A
        ADC     HL, BC          ; HL = HL (set zero flag)
        JP      NZ, PCD_NOT_ZERO

        LD      A, '0'
        LD      (IX), A
        INC     IX

        JP      PCD_END

PCD_NOT_ZERO
        PUSH    IY
        PUSH    DE

        LD      A, 0            ; D = 0 (printed flag)
        LD      D, A

        LD      IY, PCD_TABLE

PCD_LOOP
        LD      A, (IY)
        INC     IY              ; does not affect condition bits
        CP      '$'             ; end of table
        JP      Z, PCD_LOOP_END

        LD      C, (IY)         ; get word to compare to
        INC     IY
        LD      B, (IY)
        INC     IY

        CP      '0'             ; special case
        JP      NZ, PCD_NOT_ZERO_DIGIT

        LD      A, D            ; printed flag
        CP      0
        JP      Z, PCD_LOOP     ; before first digit, do not print zero

        ; the number in BC represents the smallest number which HL
        ; has to be to not print a zero right now
        AND     A               ; clear carry
        PUSH    HL              ; do not clobber it
        SBC     HL, BC          ; HL -= BC
        POP     HL
        JP      NC, PCD_LOOP    ; HL >= BC

        LD      A, '0'          ; print zero
        LD      (IX), A
        INC     IX
        JP      PCD_LOOP

PCD_NOT_ZERO_DIGIT
        AND     A               ; clear carry
        PUSH    HL              ; do not clobber HL
        SBC     HL, BC          ; HL -= BC
        POP     HL
        JP      C, PCD_LOOP     ; HL < BC

        LD      (IX), A         ; print digit
        INC     IX

        AND     A               ; clear carry
        SBC     HL, BC          ; HL -= BC

        LD      A, 1            ; printed flag = 1
        LD      D, A

        JP      PCD_LOOP

PCD_LOOP_END
        POP     DE
        POP     IY

PCD_END
        POP     BC
        POP     AF
        POP     HL
        RET

PCD_TABLE
        DB      '6'
        DW      60000
        DB      '5'
        DW      50000
        DB      '4'
        DW      40000
        DB      '3'
        DW      30000
        DB      '2'
        DW      20000
        DB      '1'
        DW      10000
        DB      '0'
        DW      1000
        DB      '9'
        DW      9000
        DB      '8'
        DW      8000
        DB      '7'
        DW      7000
        DB      '6'
        DW      6000
        DB      '5'
        DW      5000
        DB      '4'
        DW      4000
        DB      '3'
        DW      3000
        DB      '2'
        DW      2000
        DB      '1'
        DW      1000
        DB      '0'
        DW      100
        DB      '9'
        DW      900
        DB      '8'
        DW      800
        DB      '7'
        DW      700
        DB      '6'
        DW      600
        DB      '5'
        DW      500
        DB      '4'
        DW      400
        DB      '3'
        DW      300
        DB      '2'
        DW      200
        DB      '1'
        DW      100
        DB      '0'
        DW      10
        DB      '9'
        DW      90
        DB      '8'
        DW      80
        DB      '7'
        DW      70
        DB      '6'
        DW      60
        DB      '5'
        DW      50
        DB      '4'
        DW      40
        DB      '3'
        DW      30
        DB      '2'
        DW      20
        DB      '1'
        DW      10
        DB      '0'
        DW      1
        DB      '9'
        DW      9
        DB      '8'
        DW      8
        DB      '7'
        DW      7
        DB      '6'
        DW      6
        DB      '5'
        DW      5
        DB      '4'
        DW      4
        DB      '3'
        DW      3
        DB      '2'
        DW      2
        DB      '1'
        DW      1
        DB      '$'

;
; PRINTF_CONVERT_HEX
;
;   Input:
;       IY = pointer to int
;       IX = buffer to convert into
;   Output:
;       IY = incremented past int
;       IX = incremented past hex version of int
;   Internally:
;       HL = int
;

PRINTF_CONVERT_HEX
        PUSH    HL

        LD      L, (IY + 0)
        LD      H, (IY + 1)
        INC     IY
        INC     IY

        CALL    PHEXWORD

        POP     HL
        RET

;
; PRINTF_CONVERT_STRING
;
;   Input:
;       IY = pointer to string pointer
;       IX = buffer to convert into
;   Output:
;       IY = incremented past pointer
;       IX = incremented past string
;

PRINTF_CONVERT_STRING
        PUSH    HL

        LD      L, (IY + 0)
        LD      H, (IY + 1)
        INC     IY
        INC     IY

        CALL    STRCPY  ; increments IX for us

        POP     HL
        RET