;**************************************************************************
; CLIprint.asm   by Jeff Glatt
;
; Some people just starting out in assembly always ask about printing to the
; CLI. This program demonstrates how to do that.
;
; An example of printing to the CLI. Must be linked with a startup code that
; sets up a variable, _stdout. This variable is the result of a call to
; dos lib's Output() function, and is the fileHandle of the CLI window from
; where this program was started. If this program was started from WorkBench
; then _stdout will be 0. The provided startup code called StartUp.o has
; been provided for linking with this example. Using BLink,
;
; Blink  StartUp.o CLIprint.o small.lib TO CLIPrint
;
; This program does the following:
; 1). Does a straight print of a string using dos lib's Write().
; 2). Does a print of a NULL-terminated string using an included function
;     printf. The printf function has the facility to do more than just
;     print a string. By including format specifiers in the string, a "new"
;     string is created with additional information in it. A format specifier
;     is a % character followed immediately by one of the following letters:
;     lc  - for a character (pass the character on the stack)
;     ls  - for a string    (pass the string's starting address on the stack)
;     ld  - for a signed decimal representation (pass the 32 bit value on the stack)
;     lx  - for a hex representation (pass 32 bit value on stack)
; See page B-17 of the Exec ROM Kernal manual for additional options.
; For example, let's say that we have the string
;
;Message dc.b 'The number = %ld',0
;
; printf would not output the '%ld' part of the string to the CLI. Instead,
; it would create a new string where the '%ld' would be replaced by a number
; that you passed on the stack. This number would be represented as an ascii
; string. So, using the above string, we called printf like so:

;  lea    Message,a0   ;the specifier string goes in a0
;  moveq  #50,d0       ;let's pass the number 50 as a 32 bit value to printf
;  move.l d0,-(sp)
;  bsr    printf
;  addq.l #4,sp        ;re-adjust for pushing the 50

; Now printf would print this to the CLI
;
;The number = 50

; 3). Demonstrates calls to printf with imbedded specifiers.

  ;from StartUp.o
  XREF  _stdout,_DOSBase,_SysBase

  ;from small.lib
  XREF  _LVOWrite,_LVORawDoFmt

  SECTION  printfdata,DATA

Message1Len  equ  32  ;don't count terminating NULL
Message1     dc.b 'This is a print using Write().',13,10,0

Message2     dc.b 'This is a print using printf().',13,10,0

Format1      dc.b 'The number passed to printf = %ld',13,10,0

Format2      dc.b 'The string passed to printf is "%ls".',13,10,0
StringInside dc.b 'Hello',0

Format3      dc.b 'The number is %ld, and string = %ls',13,10,0

Format4      dc.b 'The number in hex = $%lx',13,10,0

FinalMsg     dc.b 'Understand, Bozo?',13,10,0

  SECTION printfcode,CODE

  XDEF _main   ;the first routine you want executed must be called _main.
_main:
;---Output a straight string via Write()
   lea      Message1,a0
   move.l   a0,d2
   moveq    #Message1Len,d3
   move.l   _stdout,d1
   movea.l  _DOSBase,a6
   jsr      _LVOWrite(a6)
;---Output a straight string via printf
   lea      Message2,a0
   bsr      printf
;---Output a number inside of the string specifier via printf
   lea      Format1,a0
   moveq    #50,d0
   move.l   d0,-(sp)
   bsr      printf
   addq.l   #4,sp
;---Output that number again (but use the hex specifier)
   lea      Format4,a0
   moveq    #50,d0
   move.l   d0,-(sp)
   bsr      printf
   addq.l   #4,sp
;---Output a string inside of the string specifier via printf
   lea      StringInside,a1
   move.l   a1,-(sp)
   lea      Format2,a0
   bsr      printf
   addq.l   #4,sp
;---Output a string and number inside of the string specifier
;  The order to push the args is reverse to the order that they are inserted
;  into the string (i.e. my %ld came before my %s, so I push StringInside
;  first then the number 50)
   lea      StringInside,a1
   move.l   a1,-(sp)
   moveq    #50,d0
   move.l   d0,-(sp)
   lea      Format3,a0
   bsr      printf
   addq.l   #8,sp      ;note: re-adjust for 2 LONG pushes
;---One last insult
   lea      FinalMsg,a0
   bsr      printf
;---Get out of this program
   rts

;=======================================================================
; Pass string specifier in a0, and any args on the stack.

  XDEF  printf
printf:
   ;---If we're from WorkBench, _stdout = 0. Get out of here.
      move.l   _stdout,d0
      beq.s    noPF
   ;---Get the address of where we pushed any args in a1
      lea      4(sp),a1
   ;---Save some regs
      movem.l  d2/d3/a2/a3/a4/a6,-(sp)
   ;---Save the address of stdout
      movea.l  d0,a4
   ;---Get a buffer on the stack to create a "new" string
   ;   Now we call Exec's RawDoFmt. It does all the work of formatting
   ;   the new string. It needs the address of a function we create. This
   ;   function just throws a char into our "new" buffer. Exec calls this
   ;   for every "new" character it creates.
      moveq    #126,d0
      suba.l   d0,sp
      lea      StoreChar,a2
      movea.l  sp,a3
      ;address of args in a1
      movea.l  _SysBase,a6
      jsr      _LVORawDoFmt(a6)
   ;---Count how many chars are in the string (it will be NULL-terminated)
      moveq    #126-1,d1
      move.l   a3,d2       ;the start of our "new" string
cntC  move.b   (a3)+,d0
      Dbeq     d1,cntC(pc)
   ;---Determine # of chars
      subq.l   #1,a3
      move.l   a3,d3
      sub.l    d2,d3
      beq.s    char0       ;if 0 chars, don't print anything
   ;---Print out to _stdout (which will be the CLI window unless the user
   ;   redirected output on the command line using the '>')
      move.l   a4,d1
      ;address of string in d2
      movea.l  _DOSBase,a6
      jsr      _LVOWrite(a6)
   ;---Get rid of our "new" buffer
char0 moveq    #126,d0
      adda.l   d0,sp
   ;---Restore regs
      movem.l  (sp)+,d2/d3/a2/a3/a4/a6
noPF  rts

;Exec passes a "new" character (in d0) to be stored in our "new" buffer
;(address in a3).
StoreChar move.b   d0,(a3)+
          rts
