[comp.sys.amiga] A small ARexx example, and other useful tidbits . . .

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,25

kim@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*.

gary@eddie.MIT.EDU (Gary Samad) (04/08/88)

In article <896@nuchat.UUCP> peter@nuchat.UUCP (Peter da Silva) writes:
}
}> say "Cuckoo at" zhour||":"||xmin
}                 ^ Don't you want a concatenation operator here?

Nope.  REXX automatically concatenates a space, then the object, if no
concatenation operator is used.

}> if opt="ALARM" then address COMMAND speak dingaling
}                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Mind explaining this
}                                                      construct?

The ADDRESS keyword specifies that the upcoming statement is not to be
executed by REXX, but is to be send to the HOST.  COMMAND is a builtin
hostname which executes programs.  It could have been something like:

address TXED insert 'foo'

or something like that to send the statement to TexEd instead or

address MFF_SERVER get_data_def dd

to query Microfiche Filer about it's currently loaded database.

There are shortcuts, by the way.  You needn't always specify the address.
You can specify the default address for all future unrecognized REXX
constructs by the expression:

address MFF_SERVER

then

get_data_def dd

works as expected.


	cu,
	Gary

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