kim@amdahl.uts.amdahl.com (Kim DeVaughn) (04/02/88)
[ "A car is just a big purse on wheels." --Johanna Reynolds ]
In a recent posting concerning ARexx and IPC, as an example of ARexx's
scripting/macro capabilities, I made reference to an amicron initiated,
talking alarm clock program that one of my co-workers whipped off.
I hadn't really thought about posting it, until Peter da Silva catagorized
the REXX language as being "PL/I like". Bunk.
So, in the interest of education, and with Bob Rethemeyer's permission
(he's the author), here's "cuckoo", along with a crontab file.
Also included is a 336-byte, "bare-bones", assembly language substitute
for the "say" command. It's valuable in it's own right as an assembly
language example of using the translator.library and narrator.device.
[ Coincidentally, I just saw a posting requesting assembly language examples
for inclusion into a freely redistributable library, similar in concept to
the Fish Disks. Feel free to include "mumble". ]
Bear in mind that the REXX language has many more features, and power than
are used in "cuckoo". It should, however, give you a "feel" for what the
language is like. Note too, that this example makes implicit use of IPC
to communicate to AmigaDOS (where it invokes the "say" command [line 19],
and also where it invokes whatever is equated to "speak" [lines 8, 59, 60,
and 62], which in this case is "mumble").
Hope some of you find this example useful!
/kim
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# cuckoo.rexx timeto.rexx sample.crontab mumble.asm mumble.uue
echo x - cuckoo.rexx
cat > "cuckoo.rexx" << '//E*O*F cuckoo.rexx//'
/* CUCKOO: ANNOUNCES THE CURRENT TIME. R. Rethemeyer 03-88 *
* Cuckoo is useful with AmiCron to announce times for events. *
* The "MUMBLE" program is used for the voice output. *
* If that is not available, change 'speak' to "sys:system/say".*
* Usage: "RX CUCKOO [ALARM]" *
* The ALARM option produces an obnoxious noise and message. */
/*--------------------------------------------------------------------*/
speak="Mumble" /* PROGRAM FOR VOICE OUTPUT */
nice="Excuse me sir, the time iz now" /* NORMAL MESSAGE */
nasty="All right u bum, get up. Nouw. The time iz" /* ALARM MESSAGE */
dingaling="gggggggggggggggggggggggggggg" /* OBNOXIOUS ALARM NOISE */
/*--------------------------------------------------------------------*/
arg opt
if opt="ALARM" then intro=nasty ; else intro=nice
zhour = time('h')
xmin = time('m')
xmin = xmin-((xmin%60)*60)
xhour = zhour
say "Cuckoo at" zhour||":"||xmin
mnite = " "
if zhour > 12 then xhour = zhour-12
select
when xhour=0 then do; xhour="twelve"; if xmin=0 then mnite="midnite"; end
when xhour=11 then xhour = "elaven"
when xhour=12 then do; xhour="twelve"; if xmin=0 then mnite="noon"; end
otherwise nop
end
smin=" "
if xmin=0 then tmin = "o clock"
else do
tmin=xmin%10
if tmin=1 then do;
select
when xmin=10 then tmin = "ten"
when xmin=11 then tmin = "elaven"
when xmin=12 then tmin = "twelve"
when xmin=13 then tmin = "thirt teen"
when xmin=14 then tmin = "fourt teen"
when xmin=15 then tmin = "fif teen"
when xmin=16 then tmin = "six teen"
when xmin=17 then tmin = "seven teen"
when xmin=18 then tmin = "eight teen"
when xmin=19 then tmin = "nine teen"
end
end
else do
smin=xmin
if xmin>9 then smin=xmin-(tmin*10)
if smin=0 then smin=" "
select
when tmin=0 then tmin = "oh"
when tmin=2 then tmin = "twenty"
when tmin=3 then tmin = "thirty"
when tmin=4 then tmin = "forty"
when tmin=5 then tmin = "fifty"
end
end
end
if opt="ALARM" then address COMMAND speak dingaling
address COMMAND speak intro xhour tmin smin mnite "."
if tmin="o clock" & opt="ALARM" then ,
address COMMAND speak "I repeat," xhour tmin "."
exit
//E*O*F cuckoo.rexx//
echo x - timeto.rexx
cat > "timeto.rexx" << '//E*O*F timeto.rexx//'
/* speak a sentence prefixed by time */
parse arg sentence
cuckoo /* announce the time first */
address command "mumble Its time to " sentence "."
exit
//E*O*F timeto.rexx//
echo x - sample.crontab
cat > "sample.crontab" << '//E*O*F sample.crontab//'
0,15,30,45 * * * * rx cuckoo
0,10,20,30,40,50 7 * * 1-5 rx cuckoo alarm
30 7,18 * * * rx timeto feed the cat
//E*O*F sample.crontab//
echo x - mumble.asm
cat > "mumble.asm" << '//E*O*F mumble.asm//'
PLEN 60
**********************************************************************
* Program: MUMBLE - bare-bones CLI substitute for SAY command *
* Author: Robert Rethemeyer, Sunnyvale, CA (@BBS-HT or BBS-JC) *
* Revision: 0.1 03/30/88 initial release *
* Status: RELEASED TO THE PUBLIC DOMAIN, "AS-IS" *
**********************************************************************
* MUMBLE is a very small CLI program which speaks the text on the *
* command line using the Amiga's translator/narrator voice output. *
* It is convenient for use in CLI or ARexx scripts. *
* Command usage: MUMBLE <text> *
**********************************************************************
* MUMBLE provides a function similar to the command line usage of *
* the SAY command, but takes much less memory and disk space *
* (300 bytes vs. 9K+). It is not, however, as versatile as SAY. *
* There are no -options; the voice parameters are fixed, and the *
* program will not run in window mode or read from a file. *
**********************************************************************
* Mumble return codes:
* 0= success
* 1-9,20-26= narrator IO error
* 11= could not allocate memory
* 12= could not open translator.library
* 13= could not open narrator.device
**********************************************************************
NOLIST
INCLUDE "exec/types.i"
INCLUDE "exec/io.i"
INCLUDE "exec/ports.i"
INCLUDE "exec/memory.i"
INCLUDE "devices/narrator.i"
LIST
XREF _LVOOpenLibrary
XREF _LVOCloseLibrary
XREF _LVOAllocMem
XREF _LVOFreeMem
XREF _LVOOpenDevice
XREF _LVOCloseDevice
XREF _LVOTranslate
XREF _LVOFindTask
XREF _LVOAddPort
XREF _LVORemPort
XREF _LVODoIO
ExecBase EQU 4
execptr EQUR A6
**********************************************************************
* Voice parameters can be changed here, to user's liking:
VSEX EQU MALE MALE/FEMALE/DEFSEX
VMODE EQU NATURALF0 NATURALF0/ROBOTICF0/DEFMODE
VRATE EQU 180 40-400/DEFRATE
VFREQ EQU DEFFREQ 5000-28000/DEFFREQ
VPITCH EQU DEFPITCH 65-320/DEFPITCH
VVOL EQU DEFVOL 0-64/DEFVOL
**********************************************************************
Mumble:
MOVEQ #0,d5 return code if parmlen=0
MOVE.L d0,d4 SAVE EXECUTION PARMS parmlen
BEQ endprog do nothing if parmlen=0
MOVE.L a0,d3 save parmaddr
MOVE.L ExecBase,execptr a6=exec lib pointer
* ALLOCATE SCRATCH MEMOR%
MOVEQ #11,d5 return code if this fails
ASL.L #2,d0 size of TRarea = parmlen*4
ADD.L #scratchlen,d0 add to size of scratch area
MOVE.L d0,d6 remember for deallocate later
MOVE.L #MEMF_PUBLIC+MEMF_CLEAR,d1 mem flags
JSR _LVOAllocMem(execptr) ask for the storage
MOVE.L d0,a3 a3=scratch area ptr
BEQ broke1 abort if request failed
* OPEN TRANSLATOR LIBRARY
MOVEQ #12,d5 return code if this fails
LEA tranlib(pc),a1 addr of library name
MOVEQ #0,d0 any version will do
JSR _LVOOpenLibrary(execptr) get library pointer
MOVE.L d0,a5 a5=tran lib pointer
BEQ broke2 back out if no lib
* TRANSLATE COMMAND LINE PARAMETER
MOVE.L d3,a0 retrieve parmaddr
MOVE.L d4,d0 and parmlen
LEA TRarea(a3),a1 point to xlat out buff
ASL.L #2,d4 figure out buff length again
MOVE.L d4,d1 for func (and save for later)
JSR _LVOTranslate(a5) translate to phonemes
MOVE.L a5,a1 :
JSR _LVOCloseLibrary(execptr) close trans lib
* INIT NARRATOR DEVICE PORT
MOVE.L #0,a1 :
JSR _LVOFindTask(execptr) identify this task
MOVE.L d0,Narport+MP_SIGTASK(a3) let narrator find it
LEA Narport(a3),a1 point to the port area
JSR _LVOAddPort(execptr) install narrator port
* INIT NARRATOR DEVICE
MOVEQ #13,d5 return code if this fails
LEA Narname(pc),a0 point to device name
MOVEQ #0,d0 unit 0
LEA Nardev(a3),a1 point to device structure
MOVEQ #0,d1 no flags
JSR _LVOOpenDevice(execptr) open narrator device
TST.L d0 if device open failed
BNE.S broke3 then back out
* INIT DEVICE STUFF
LEA Nardev(a3),a1 a1=device structure
LEA Narport(a3),a0 a0=port address
MOVE.L a0,MN_REPLYPORT(a1) give port addr to device
LEA audmaps(pc),a0 audio maps
MOVE.L a0,NDI_CHMASKS(a1) addr of maps
MOVE.W #4,NDI_NUMMASKS(a1) number of masks
MOVE.W #VPITCH,NDI_PITCH(a1) init pitch
MOVE.W #VRATE,NDI_RATE(a1) init rate
MOVE.W #VVOL,NDI_VOLUME(a1) init volume
MOVE.W #VFREQ,NDI_SAMPFREQ(a1) init sample freq
MOVE.W #VSEX,NDI_SEX(a1) init sex
MOVE.W #VMODE,NDI_MODE(a1) init mode
MOVE.W #CMD_WRITE,IO_COMMAND(a1) init device command
MOVE.L d4,IO_LENGTH(a1) length of buffer
LEA TRarea(a3),a0 addr of buffer
MOVE.L a0,IO_DATA(a1) :
* THE MOMENT OF TRUTH
JSR _LVODoIO(execptr) do i/o, wait for reply
MOVE.L d0,d5 save return code
NEG.L d5 convert rc to positive
* CLEAN UP AND RETURN
LEA Nardev(a3),a1 point to device structure
JSR _LVOCloseDevice(execptr) release device
broke3 LEA Narport(a3),a1 point to port
JSR _LVORemPort(execptr) delete the port
broke2 MOVE.L d6,d0 length of scratch area
MOVE.L a3,a1 addr of scratch area
JSR _LVOFreeMem(execptr) free scratch mem
broke1 MOVE.L d5,d0 set return code
endprog RTS ALL DONE!
************************************************************
CNOP 0,2
tranlib DC.B 'translator.library',0
CNOP 0,2
narname DC.B 'narrator.device',0
CNOP 0,2
audmaps DC.B 3,5,10,12 Audio device maps
*************************************************************
version DC.B 'MUMBLE V0.1 RAR' Imbedded version info
*************************************************************
STRUCTURE Scratch,0 Format of allocated scratch mem
STRUCT Narport,MP_SIZE Message reply port for narrator
STRUCT Nardev,NDI_SIZE Narrator IO block
LABEL scratchlen Length of scratch area less TRarea
STRUCT TRarea,0 xlat out buff (size computed dynamically)
END and there ain't no more
//E*O*F mumble.asm//
echo x - mumble.uue
cat > "mumble.uue" << '//E*O*F mumble.uue//'
begin 755 mumble
M```#\P`````````!``````````````!+```#Z0```$MZ`"@`9P``ZB8(+'@`^
M!'H+Y8`&@````&@L`"(\``$``4ZN_SHF0&<``,9Z#$/Z`,1P`$ZN_=@J0&<`)
M`*P@0R`$0^L`:.6$(@1.K?_B(DU.KOYB(GP`````3J[^VB=``!!#ZP``3J[^0
MGGH-0?H`FG``0^L`(G(`3J[^1$J`9F!#ZP`B0>L``"-(``Y!^@"*(T@`.#-\&
M``0`/#-\`&X`,C-\`+0`,#-\`$``/C-\5K@`0#-\````-C-\````-#-\``,`&
M'"-$`"1!ZP!H(T@`*$ZN_C@J`$2%0^L`(DZN_CY#ZP``3J[^F"`&(DM.KO\NW
M(`5.=71R86YS;&%T;W(N;&EB<F%R>0``;F%R<F%T;W(N9&5V:6-E``,%"@Q-'
554U"3$4@5C`N,2!205(```````/RT
``
end
size 336
//E*O*F mumble.uue//
echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
63 331 2294 cuckoo.rexx
5 28 152 timeto.rexx
3 25 109 sample.crontab
147 730 8084 mumble.asm
13 15 506 mumble.uue
231 1129 11145 total
!!!
wc cuckoo.rexx timeto.rexx sample.crontab mumble.asm mumble.uue | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
--
UUCP: kim@amdahl.amdahl.com
or: {sun,decwrl,hplabs,pyramid,ihnp4,uunet,oliveb,cbosgd,ames}!amdahl!kim
DDD: 408-746-8462
USPS: Amdahl Corp. M/S 249, 1250 E. Arques Av, Sunnyvale, CA 94086
CIS: 76535,25kim@amdahl.uts.amdahl.com (Kim DeVaughn) (04/05/88)
In article <26223@amdahl.uts.amdahl.com>, I wrote: > > Note too, that this example makes implicit use of IPC > to communicate to AmigaDOS (where it invokes the "say" command [line 19], > and also where it invokes whatever is equated to "speak" [lines 8, 59, 60, > and 62], which in this case is "mumble"). Ooooops! The "say" command on line 19 refers to the *internal* ARexx command "say" ... roughly the REXX equivalent to "printf()". Sorry for any confusion this may have caused ... /kim -- UUCP: kim@amdahl.amdahl.com or: {sun,decwrl,hplabs,pyramid,ihnp4,uunet,oliveb,cbosgd,ames}!amdahl!kim DDD: 408-746-8462 USPS: Amdahl Corp. M/S 249, 1250 E. Arques Av, Sunnyvale, CA 94086 CIS: 76535,25
peter@nuchat.UUCP (Peter da Silva) (04/07/88)
In article ... kim@amdahl.uts.amdahl.com (Kim DeVaughn) writes: > I hadn't really thought about posting it, until Peter da Silva catagorized > the REXX language as being "PL/I like". Bunk. It's PL/1-like. I've extensive experience with another PL/1 derivitive (namely PL/M), and moderate experience with PL/1 itself (despite my best attempts at avoiding it :->). There's nothing inherently wrong with being PL/1-like, so don't be so defensive. I just happen to prefer weird C derivitives. > say "Cuckoo at" zhour||":"||xmin ^ Don't you want a concatenation operator here? > select > when xhour=0 then do; xhour="twelve"; if xmin=0 then mnite="midnite"; end > when xhour=11 then xhour = "elaven" > when xhour=12 then do; xhour="twelve"; if xmin=0 then mnite="noon"; end > otherwise nop > end An improvement over "DO CASE", I must say. > else do > tmin=xmin%10 > if tmin=1 then do; ^ Isn't this redundant? > if opt="ALARM" then address COMMAND speak dingaling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Mind explaining this construct? -- -- a clone of Peter (have you hugged your wolf today) da Silva `-_-' -- normally ...!hoptoad!academ!uhnix1!sugar!peter U -- Disclaimer: These aren't mere opinions... these are *values*.
kim@amdahl.uts.amdahl.com (Kim DeVaughn) (04/10/88)
In article <896@nuchat.UUCP>, peter@nuchat.UUCP (Peter da Silva) writes: > In article ... kim@amdahl.uts.amdahl.com (Kim DeVaughn) writes: > > I hadn't really thought about posting it, until Peter da Silva catagorized > > the REXX language as being "PL/I like". Bunk. > > It's PL/1-like. I've extensive experience with another PL/1 derivitive > (namely PL/M), and moderate experience with PL/1 itself (despite my best > attempts at avoiding it :->). Fine. *I* find it to be closer in flavor to elementary C code, than to PL/I. It's really quite different than either, so this discussion is pretty pointless (and therefore, I won't comment on it further in future postings). I will say that (as with any language), there are a number of things I *don't* like about the REXX language. I'll put up with them though because of all of the flexibility and capability the ARexx *package* gives me. > There's nothing inherently wrong with being PL/1-like, so don't be so > defensive. I just happen to prefer weird C derivitives. Not defensive. I personally find PL/I to be a disgusting language, that I've had to deal with alot more than I would have cared to. BTW, I assume we're talking about the same language ... PL/I. I've never used anything called PL/1 ... :-) > > say "Cuckoo at" zhour||":"||xmin > ^ Don't you want a concatenation operator here? No. The space is a concatation operator, which (logically enough) gives you: the LHS of the expression, a space, and the RHS of the expression. REXX, you see, is a language oriented toward text processing, and has several features that facilitate this. It this instance, you really aren't aware that an operator is being used, unless you think about it. Another point ... the explicit || concatenation operators aren't really needed here at all. Bob just used them for clarity. REXX is flexible enough that there are often several ways to specify the same action. There *are* cases where the || operator is needed, BTW. > > else do > > tmin=xmin%10 > > if tmin=1 then do; > ^ Isn't this redundant? Yeah. Doesn't hurt anything though. Sorta like C lets you code: if (foo) {x++;} where the braces are redundant. > > if opt="ALARM" then address COMMAND speak dingaling > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Mind explaining this > construct? Glad you asked. You are looking at one of the reasons why ARexx and the REXX language is so well suited to serve as a script/macro/IPC processing hub. REXX uses the concept of an "address" to determine who should process a given command. The above form is a very explicit way of telling the interpreter that this command is for the OS (that's what "command" specifies ... the OS). In this example, what gets passed to AmigaDOS for execution is: "Mumble ggggggggggggggggggg" due to the assignments made for "speak" and "dingaling" (mumble is the stripped- down, assembly language version of the AmigaDOS "say" command that was posted along with this example). This is important to note: the commands that are to be passed to an external program (OS, or whatever), can be coded as REXX *expressions*, and not merely specified as a hard-coded string. Flexible. And powerful. Had this been *my* code, I might well have just put the REXX statement "address command" up at the top of the REXX program, and then I could have just written: if opt="ALARM" then speak dingaling and it would've worked in the same way. I.e., the REXX interpreter dosn't understand "Mumble" as a command, it sees that the "address" has been set to "command", and so passes it on to the OS, assuming that it (the OS) will know what to do with it. If the OS *doesn't* know, you get an error back (command not found, etc). Of course the "address" specified could have been any application set up to talk to ARexx, so you could say "address TXED", or "address DME", etc. and passed commands to those applications just as easily. Also, if the REXX program had been invoked *from* such a host (i.e., as a macro), there is no need to use the "address" command at all to issue commands back to that host, as the interpreter keeps track of who the invoking program was. Anything it (the interpreter) doesn't understand gets passed back to the invoking host by default. Or you can change where it goes. Start to see how (from the user's point of view) multiple applications can *communicate* (and not just pass data)? There are a number of other nuances that the "address" statement has, one of which is to allow the address itself to be specified by an expression, and not hard-coded. Another is a "toggle" form, that swaps between the current and previous addresses. Etc. I really suggest you take a look at Cowlishaw's book that defines the REXX language: "The REXX Language" "A Practical Approach to Programming" M. F. Cowlishaw Prentice-Hall, 1985 Should be able to find it in your local university library, or a decent computer book store. Or, better yet, get a copy of ARexx from Bill Hawes, and *find out* what it can and cannot do! Then you might be in a position to offer comments that have some substance to them, and not just your own personal opinions, most of which seem to be based on mere conjecture. BTW, for those of you also running MS-DOS on the Bridge, or a PClone, a REXX interpreter is also available for that system from Mansfield Software. Of course it doesn't have quite the capabilities that ARexx provides, since it's not running on a multi-tasking, message-passing system. If interested, email me, and I'll hunt up their address. /kim -- UUCP: kim@amdahl.amdahl.com or: {sun,decwrl,hplabs,pyramid,ihnp4,uunet,oliveb,cbosgd,ames}!amdahl!kim DDD: 408-746-8462 USPS: Amdahl Corp. M/S 249, 1250 E. Arques Av, Sunnyvale, CA 94086 CIS: 76535,25