page 66,80 title SAY! generate keyboard input for other programs Comment û SAY!.ASM version 1.5 last updated by Roedy Green 93/06/08 Please report bugs and problems to: Roedy Green Canadian Mind Products #101 - 2536 Wark Street Victoria, BC Canada V8T 4G8 tel:(250) 361-9093 mailto:roedyg@mindprod.com http://mindprod.com This program may be freely copied and used for any purpose except military. Version History: 1.0 1990-11-30 initial version 1.1 1990-11-30 no longer need spaces between quotes and numbers 1.2 1991-03-03 tabs no longer expanded on output because uses handle I/O. 1.3 1993-06-08 embed new phone and address 1.4 1996-10-25 embed POB 707 Quathiaski Cove address 1.5 1998-11-08 embed Barker address. 1.6 2010-01-28 bundled with C version 1.7 2012-11-25 modify to work with ML.exe 11.0 */ Purpose ******* SAY! is very similar to SAY in the Ziff Communications PC Powertools. SAY! creates arbitrary strings of characters for use in redirecting to other programs, files or devices. It is like DOS echo, except that you can easily embed control characters and high bit characters. SAY! has a pickier syntax that SAY. In SAY, if you forget the " marks, SAY treats the text as a comment. SAY! treats it as an error. Examples ******** SAY! "Y" 13 | DEL *.* answers the Are you sure prompt with Y SAY! "Brown" 13 "Yellow" 9 | MyColour.Exe - you may have any number of "enclosed strings" or decimal numbers for the ASCII codes of the keystrokes (not scan codes). - to get the " itself, use code 34. - Any text not in quotes is just ignored. SAY! "Mary had a little lamb" 13 10 "Its fleece .." 173 13 10 > RHYME.DAT - create a file perhaps containing strange characters Say! 27 "&l5257.1058J" 26 > LPT1: - put HP printer into Postscript mode Say! 27 "&l1057.32259J" > LPT1: - put HP printer into native PCL non-Postscript mode Register Conventions ******************** In general, subroutines may trash all registers except those explicity documented as input or output. AL = character in command line being analysed. SI = points to char in command line to analyse next. û ; end of comment .286 .model tiny ; this is a COM file stack segment stack ; keep MS link happy by providing null stack stack ends CODE segment PARA ; start off in code. data segment byte ; provide a separate DATA segment ; actually all come after the code ;============================================================== ; V A R I A B L E S BannerMsg label byte db '°±²Û SAY! 1.5 Û²±°',13,10 db 13,10 db 'Copyright: (c) 1990-2017 Roedy Green, Canadian Mind Products',13,10 db '#101 - 2536 Wark Street, Victoria, BC Canada V8T 4G8',13,10 db 'tel:(250) 361-9093 mailto:roedyg@mindprod.com http://mindprod.com',13,10 db 'May be freely copied and used for any purpose but military.',13,10 BannerEnd label byte UsageMsg db 13,10 db 'try patterns like:',13,10 db 'SAY! "Y" 13 | DEL *.*',13,10 db 'SAY! "Brown" 13 "Yellow" 255 | MyColour.Exe',13,10 db 'SAY! 27 "W" 1 > LPT1:',13,10 db 'SAY! 174 "Yes" 175 > MyFile.Dat',13,10 UsageEnd label byte InsideQuotes db 0 ; 0=outside " .. " -1=inside ; workin on char inside " ..." InsideNumber db 0 ; 0=outside 999 -1=inside ; working on digit forming a number ; gets turned off when the result is emitted Number db 0 ; number as built so far ; note only has values 0 .. 255 outbuff db 0 ; buffer for outputting chars data ends org 100h com group code,data ; force data segment to go at the end! assume CS:com,DS:com,ES:com,SS:com ; seg regs cover everything org 100H ; in Code segment ;========================== _Start proc far ; M A I N L I N E R O U T I N E call Commandline ; DS:DI has command line, cx length ; followed by Cr (not part of cx) jcxz Quit ; nothing to do if null command line ParseLoop: ; main parse loop to examine each char push cx ; save cx just in case it gets wrecked lodsb ; get next char of the command line cmp al,'"' ; is it a " jne notquote IsQuote: call EmitPossNumber ; " possibly terminates a number call handlequote ; it is a " jmp chardone notquote: test InsideQuotes,-1 ; inside " ... "? jz DoParseOutside DoParseInside: ; we are inside quotes mov dl,al call Emit ; treat everything, letters, numbers jmp CharDone ; spaces, control chars as fodder DoParseOutside: ; we are outside " ... " cmp al,32 ; control char 0 .. 32? ja NotControl call EmitPossNumber ; control char ignored, but signals jmp CharDone ; end of number NotControl: cmp al,'0' ; is it a digit? jb NotDigit cmp al,'9' ja NotDigit call handleDigit ; it is a digit jmp chardone NotDigit: IsLetter: ; we treat everything else as a letter call EmitPossNumber ; letter terminates any number. jmp Trouble ; Letters outside quotes are syntax errors. ; To treat letters outside quotes as comments ; just remove this Call Trouble line. chardone: pop cx loop ParseLoop ; go process another char of the command line call EmitPossNumber ; finish off any pending number QUIT: mov AX,4C00H ; EXIT gracefully int 21H ;============================= handlequote proc near ; Rem we just parsed a ". It may be the beginning or end of a string. ; just toggle the state of inside. ; We do not emit the " xor insideQuotes,-1 ret handlequote endp ;============================= handleDigit proc near ; Rem we just parsed a digit in al, not inside quotes sub al,'0' ; convert ascii digit to bin test InsideNumber,-1 ; already inside the number? jnz SecondDigit ; yes, jump FirstDigit: mov Number,al ; initialize number with digit mov InsideNumber,-1 ; mark that we have a number cooking ret SecondDigit: mov dl,al ; save al, al needed for mul mov al,Number ; calc Number := Number * 10 + digit mov bh,10d mul bh ; ax = bh{10} * al add al,dl mov Number,al ret handleDigit endp ;============================= EmitPossNumber proc near ; Rem we just parsed control char that might terminate a number test InsideNumber,-1 ; a number is just completed? jz NoNumberCooking ; There was a number mov dl,Number call Emit mov InsideNumber,0 ; mark number as completely finished NoNumberCooking: ret EmitPossNumber endp ;============================= Trouble proc near ; show banner -- not redirected lea dx,BannerMsg mov cx,BannerEnd-BannerMsg call SayErr ; Problem with command line lea dx,UsageMsg mov cx,UsageEnd-UsageMsg ; length of message call SayErr Abort: mov AX,4C04H ; Die with Errorlevel 04 int 21H Trouble endp ;============================= CommandLine proc near ; Gets command line string into DS:si, cx ; Command line does not include the program name. ; DS already set since we are a COM file ; Sets cx to length of command line. ; Trashes no other registers ; counted string at HEX 80 ; contains command line. ; It has no trailing null. mov si,81h xor CH,CH mov CL,DS:80h ; cx contains length of command ret CommandLine endp ;============================================================== Emit proc near ; emits character in DL -- usually redirected as input to other program ; Cannot use function 02 because that expands spaces. mov outbuff,dl ; save char in RAM for DOS lea dx,outbuff mov cx,1 ; only 1 byte mov bx,1 ; handle 1 - stout will not be redirected mov ax,4000h ; AH=40 signifies write by handle int 21h ; DOS WRITE STRING ret Emit endp ;============================================================== SayErr proc ; display string at ds:dx cx bytes long mov bx,2 ; handle 2 - stderr will not be redirected mov ax,4000h ; AH=40 signifies write by handle int 21h ; DOS WRITE STRING ret SayErr endp ;============================================================== _Start endp ;========================== CODE ends ; end of code segment end _Start