;************************************************************************** ; 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