# ip65 technical reference

# File : ip65/printf.s

## functions

function | description
--- | ---
console_printf |
    print a string to console, with (some) standard printf % codes converted
    inputs: AX = pointer to 'argument list'
     first word in argument list is the string to be printed
     subsequent words in argument list are interpolated in to the string as
     it is displayed. (argument list is automagically created by the 'printf' macro
     defined in inc/printf.i)
    outputs: none
    supported % codes:
       %s: string (argument interpreted as pointer to null terminated string)
       %d: decimal number (arguement interpreted as 16 bit number)
       %x: hex number (arguement interpreted as 16 bit number)
       %c: char (arguement interpreted as pointer to single ASCII char)
    "field width" modifiers are also supported, e.g "%02x" to print 2 hex digits

## implementation

    .include "../inc/common.i"

      .export console_printf

      .import console_out
      .import console_strout

      .segment "IP65ZP" : zeropage

    strptr:    .res 2
    argptr:    .res 2
    valptr:    .res 2

      .bss

    ysave:    .res 1
    arg:    .res 1
    fieldwidth:  .res 1
    fieldwcnt:  .res 1
    leadzero:  .res 1
    argtemp:  .res 1
    int:    .res 2
    num:    .res 5
    ext:    .res 2

      .code

    ;print a string to console, with (some) standard printf % codes converted
    ;inputs: AX = pointer to 'argument list'
    ; first word in argument list is the string to be printed
    ; subsequent words in argument list are interpolated in to the string as
    ; it is displayed. (argument list is automagically created by the 'printf' macro
    ; defined in inc/printf.i)
    ;outputs: none
    ;supported % codes:
    ;   %s: string (argument interpreted as pointer to null terminated string)
    ;   %d: decimal number (arguement interpreted as 16 bit number)
    ;   %x: hex number (arguement interpreted as 16 bit number)
    ;   %c: char (arguement interpreted as pointer to single ASCII char)
    ;"field width" modifiers are also supported, e.g "%02x" to print 2 hex digits
    console_printf:
      stax argptr
      ldy #0
      lda (argptr),y
      sta strptr
      iny
      lda (argptr),y
      sta strptr + 1
      iny
      sty arg

      ldy #0
    @nextchar:
      lda (strptr),y
      bne :+
      rts
    :
      cmp #'%'
      beq @printarg

      cmp #'\'
      beq @printescape

      jsr console_out

    @next:
      iny
      bne @nextchar

      inc strptr + 1
      jmp @nextchar

    @printescape:
      iny
      bne :+
      inc strptr + 1
    :  lda (strptr),y
      ldx #esc_count - 1
    :  cmp esc_code,x
      beq @escmatch
      dex
      bpl :-
      bmi @next
    @escmatch:
      lda esc_char,x
      jsr console_out
      jmp @next

    @printarg:
      lda #0
      sta fieldwidth
      sta leadzero
      lda #$ff
      sta fieldwcnt
    @argnext:
      iny
      bne :+
      inc strptr + 1
    :
      tya
      pha

      lda (strptr),y

      cmp #'0'    ; check for field width
      bcc @notdigit
      cmp #'9'+1
      bcs @notdigit
      and #$0f
      bne :+      ; check for leading 0
      inc fieldwcnt
      bne :+
      lda #$80
      sta leadzero
      pla
      tay
      jmp @argnext
    :
      pha      ; multiply old value by 10
      asl fieldwidth
      lda fieldwidth
      asl
      asl
      clc
      adc fieldwidth
      sta fieldwidth
      pla
      clc      ; add new value
      adc fieldwidth
      sta fieldwidth
      pla
      tay
      jmp @argnext

    @notdigit:
      cmp #'s'
      beq @argstr

      cmp #'d'
      beq @argint

      cmp #'x'
      beq @arghex

      cmp #'c'
      beq @argchar

    @argdone:
      pla
      tay
      jmp @next

    @argstr:
      jsr @argax
      jsr console_strout

      jmp @argdone

    @argint:
      jsr @argax
      stax valptr
      jsr @valax
      jsr printint

      jmp @argdone

    @arghex:
      jsr @argax
      stax valptr
      jsr @valax
      jsr printhex

      jmp @argdone

    @argchar:
      jsr @argax
      stax valptr
      ldy #0
      lda (valptr),y
      jsr console_out

      jmp @argdone

    @argax:
      ldy arg
      lda (argptr),y
      pha
      iny
      lda (argptr),y
      tax
      iny
      sty arg
      pla
      rts

    @valax:
      ldy #0
      lda (valptr),y
      pha
      iny
      lda (valptr),y
      tax
      pla
      rts

    @printx:
      txa
      lsr
      lsr
      lsr
      lsr
      tay
      lda hex2asc,y
      jsr console_out
      txa
      and #$0f
      tay
      lda hex2asc,y
      jmp console_out

    ; print 16-bit hexadecimal number
    printhex:
      tay
      and #$0f
      sta num + 3
      tya
      lsr
      lsr
      lsr
      lsr
      sta num + 2

      txa
      and #$0f
      sta num + 1
      txa
      lsr
      lsr
      lsr
      lsr
      sta num

      lda #4
      sec
      sbc fieldwidth
      tax
      bpl :+
      jsr printlong
    :
      cpx #4
      beq @nowidth

    @printlead:
      lda num,x
      bne @printrest
      lda #' '
      bit leadzero
      bpl :+
      lda #'0'
    :  jsr console_out
      inx
      cpx #3
      bne @printlead

    @nowidth:
      ldx #0
    :  lda num,x
      bne @printrest
      inx
      cpx #4
      bne :-
      lda #'0'
      jsr console_out
      rts

    @printrest:
      lda num,x
      tay
      lda hex2asc,y
      jsr console_out
      inx
      cpx #4
      bne @printrest
      rts

    printlong:
      lda #' '
      bit leadzero
      bpl :+
      lda #'0'
    :  jsr console_out
      inx
      bne :-
      rts

    ; print a 16-bit integer
    printint:
      stax int

      ldx #4
    @next:
      lda #0
      sta num,x
      jsr div10
      lda ext
      sta num,x
      dex
      bpl @next

      lda fieldwidth
      beq @nowidth
      lda #5
      sec
      sbc fieldwidth
      tax
      bpl :+
      jsr printlong
    :
    @printlead:
      lda num,x
      bne @print

      lda #' '
      bit leadzero
      bpl :+
      lda #'0'
    :  jsr console_out
      inx
      cpx #5
      bne @printlead
      beq @printzero

    @nowidth:
      inx
      cpx #5
      beq @printzero
      lda num,x
      beq @nowidth

    @print:
      clc
      adc #'0'
      jsr console_out
      inx
      cpx #5
      beq @done
    @printall:
      lda num,x
      jmp @print

    @done:
      rts

    @printzero:
      lda #'0'
      jmp console_out

    ; 16/16-bit division, from the fridge
    ; int/aux -> int, remainder in ext
    div10:
      lda #0
      sta ext+1
      ldy #$10
    @dloop:
      asl int
      rol int+1
      rol
      rol ext+1
      pha
      cmp #10
      lda ext+1
      sbc #0    ; is this a nop?
      bcc @div2
      sta ext+1
      pla
      sbc #10
      pha
      inc int
    @div2:
      pla
      dey
      bne @dloop
      sta ext
      rts

      .rodata

    msg_unimplemented:
      .byte "",0

    hex2asc:
      .byte "0123456789abcdef"

    esc_code:
      .byte "eabfnrt", '\'
    esc_count  = * - esc_code
    esc_char:
      .byte 27, 7, 8, 12, 10, 13, 9, '\'

    ;-- LICENSE FOR printf.s --
    ; The contents of this file are subject to the Mozilla Public License
    ; Version 1.1 (the "License"); you may not use this file except in
    ; compliance with the License. You may obtain a copy of the License at
    ; http://www.mozilla.org/MPL/
    ;
    ; Software distributed under the License is distributed on an "AS IS"
    ; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    ; License for the specific language governing rights and limitations
    ; under the License.
    ;
    ; The Original Code is ip65.
    ;
    ; The Initial Developer of the Original Code is Per Olofsson,
    ; MagerValp@gmail.com.
    ; Portions created by the Initial Developer are Copyright (C) 2009
    ; Per Olofsson. All Rights Reserved.
    ; -- LICENSE END --