Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

XCMD and XFCN: Hypercard's Software Slots


SEP88: XCMD AND XFCN: HYPERCARD'S SOFTWARE SLOTS


Stan Krute has been a Macintosh software developer since 1984. He is currently commuting to Redmond WA where he is writing programmers documentation for the MS OS/2 Presentation Manager. He can be reached on CompuServe (User ID# 73137, 2121), or at 18617 Camp Creek Rd., Hornbrook, CA 96044.

XCMDs and XFCNs are code resources that act as software slots for HyperCard. You can use them to create efficient extensions to the HyperTalk language. When HyperTalk can't (or won't) perform as you desire, XCMDs and XFCNs give you a powerful out. For instance, if HypetTalk lacks a particular command, you can write the necessary code in C, Pascal, or assembly; transform the code into an XCMD or XFCN; append the XCMD or XFCN to a stack or to HyperCard itself; then use the new command as if it were native HyperTalk.

Both XCMD and XFCN code resources begin execution at the first byte. When HyperCard transfers control to an XCMD or XFCN, it passes a single parameter via the stack: a pointer to an XCmdBlock data structure (see Example 1, page 91, for a C rendition). When control returns to HyperCard, it pays attention to a field in the XCmdBlock where XFCN's are supposed to place a handle to a return value, and ignores the same field for XCMDs. So one is a function, and the other is a procedure, as Pascalians would say. In HyperTalk, the analogous distinction is between function handlers and on handlers.

Example 1: C rendition of an XCmdBlock data structure

typedef struct XCmdBlcok {
     short paramCount;        /* # of entry parameters                     */
     Handle params[16];       /* array of handles to entry parameters      */
     Handle returnValue;      /* handle to a return value                  */
     Boolean passFlag;        /* boolean: if true, HC passes message on    */

     void (*entryPoint) ();   /* pointer to HCard callback function (cbf)  */
     short request;           /* a cbf command code                        */
     short result;            /* HC's answer to a cbf command              */
     long inArg[8];           /* array of arguments in to a cbf command    */
     long outArgs[4];         /* array of arguments out from a cbf command */
     } XCmdBlock, *XCmdBlockPtr;

# make instructions for testing the isAlpha XFCN via hctest

So far, most of the material from Apple and APDA that documents XFCN's and XCMDs has had examples written in C and Pascal. This article shows you the details of implementing an XFCN in 680x0 assembly language. The development environment is Apple's Macintosh Programmer's Workshop (MPW), Version 2.02. Listing One , page 108, shows the file XCMDandXFCN.a. It contains MPW assembler definitions of an XCmdBlock. I put the file in the MPW directoryAlncludes. The file uses the MPW RECORD/ENDR pseudo-ops, which lets you define assembly language equates in ways quite analogous to C or Pascal. If you create such a file, and want to put it somewhere other than AIncludes, be sure to augment the AIncludes equate in the MPW Startup file.

My XFCN example (see Listing Two is algorithmically trivial, simply implementing the useful C library function isAlpha, which controls the MPW build process. A few notes on the make listing in Listing Two: lines beginning with # are comments; the f symbol (Macintosh character code $c4) can be read as "depends upon" and the dependencies it indicates guide the make operation. The MPW tools mentioned are Asm, the MPW 680x0 assembler, and Link, the linker. Command lines were put together with Commando, the MPW CCLC generator.

The assembler takes the source code file, isAlpha.a (see Listing Three, page 108), and produces isAlpha.a.o, an object code file, and isAlpha.a.lst, a listing of the assembly. The linker takes the object file and produces isAlpha, a resource file containing my code compiled into an XFCN resource. I've set isAlpha up as a ResEdit document, with appropriate type and creator longwords and an icon, but that's just one of my completeness shticks and not necessary to the project.

Ignoring the source code's algorithm (a simple bounds test), the important thing to note about Listing Three is how I use the XCmdBlock to get information to and from HyperCard. Note how transparent the MPW record pseudo-ops used in XCMDandXFCN.a make my XCmd-Block interactions. Results sent back to HyperCard are in real live 0-terminated string format, stored in a memory block, and indicated via a handle. HyperCard takes care of memory block disposal details.

After the make procedure is completed, the code resides in isAlpha as an XFCN. You can then paste it into a stack using ResEdit or MPW. Example 2, below, shows the file isAlpha.r, which can be fed into the MPW resource compiler Rez. Example 3 and 4, below, show MPW make files that grab a HyperCard stack and then use Rez to append the isAlpha XFCN. The code in Example 3 will append the stack hctest, which is just a little XFCN to the one-button test stack that lives in the isAlpha folder. The code in Example 4 does the appendation for a stack outside the local world. As with Listing Two, the comments should make the cryptic commands understandable.

Example 2: The file alpha.r is used by the MPW tool Rez to append the is Alpha XFCN to a HyperCard stack

     include "isAlpha" 'XCFN' (990);

Example 3: The file hctest.make guides MPW as it appends an XFCN to a test stack located within a default directory

     # make instructions for testing the isAlpha XFCN via hctest

     #the hypercard test stack depends on the resource file and the rez file
     hctest    f    isAlpha        isAlpha.r
     # to create the hypercard test stack, merge it with the XFCN
          # the rez file is isAlpha.r
          # the output file is the hypercard stack hctest
          # the XFCN will be merged into the existing stack
          Rez isAlpha.r -o hctest -a

Example 4: The file people.make guides MPW as it appends an XFCN to a stack located within an arbitrary directory

# make instructions fo radding the isAlpha XFCN to Zippy:hypercard:people

# the stack depends on the resource file and the rez file
people    f    isAlpha        isAlpha.r
# to create the people stack, merge it with the XFCN
     # the rez file isAlpha.r
     # the output file is the hypercard stack
     # the XFCN will be merged into the existing stack
     Rez isAlpha.r -o Zippy:hypercard:people -a

That's it. Linkup accomplished. You can now augment HyperCard with any kind of code you like such as the standard C and Lisp library functions I've recently added.

_XCMD and XFCN_ by Stan Krute

[LISTING ONE]

<a name="01a3_0009">


*--------------------------------------- file information
*   XCMDandXFCN.a
*   MPW record definitions useful in XCMD and XFCN work
*   Edited with MPW 2.02
*   Compiled under MPW 2.02
*   Written and (C)1988 by Stan Krute. All rights reserved.
*-----------------------------------------------------------------------

; general array structures

Array4L      RECORD   0   ; an array of 4 longs
zero      DS.L   1
one      DS.L   1
two      DS.L   1
three      DS.L   1
      ENDR

Array8L      RECORD   0   ; an array of 8 longs
zero      DS.L   1
one      DS.L   1
two      DS.L   1
three      DS.L   1
four      DS.L   1
five      DS.L   1
six      DS.L   1
seven      DS.L   1
      ENDR

Array16L   RECORD   0   ; an array of 16 longs
zero      DS.L   1
one      DS.L   1
two      DS.L   1
three      DS.L   1
four      DS.L   1
five      DS.L   1
six      DS.L   1
seven      DS.L   1
eight      DS.L   1
nine      DS.L   1
ten      DS.L   1
eleven      DS.L   1
twelve      DS.L   1
thirteen   DS.L   1
fourteen   DS.L   1
fifteen      DS.L   1
      ENDR

; an XCmdBlock record

XCmdBlock   RECORD 0
paramCount   DS.W   1      ; # of entry parameters
params      DS   Array16L   ; array of handles to entry parameters
returnValue   DS.L   1      ; handle to a return value
passFlag   DS.W   2      ; boolean: if true, HC passes message on

entryPoint   DS.L   1      ; pointer to HyperCard callback function (cbf)
request      DS.W   1      ; a cbf command code
result      DS.W     1      ; HC's answer to a cbf command
inArgs      DS   Array8L      ; array of arguments in to a cbf command
outArgs      DS   Array4L      ; array of arguments out from a cbf command
      ENDR





<a name="01a3_000a"><a name="01a3_000a">
<a name="01a3_000b">
[LISTING TWO]
<a name="01a3_000b">


# make instructions for the isAlpha XFCN

# the object file depends on the assembly language source code file
isAlpha.a.o   f   isAlpha.a
# to create the object file, assemble the assembly language source code file
   # the source code file is isAlpha.a
   # we'll send a listing to the file isAlpha.a.lst
   Asm isAlpha.a -l

# the resource file depends on the object file
isAlpha      f   isAlpha.a.o
# to create the resource file, link the object file
   # the output file is isAlpha
   # the output file's type is RSRC
   # the output file's creator is RSED
   # the code will be an XFCN resource with ID #990
   # the segment name will be isAlpha, same as the new HyperCard command name
   # the input file is the object file isAlpha.a.o
   Link -o isAlpha -t RSRC -c RSED -rt XFCN=990 -sn Main=isAlpha isAlpha.a.o



<a name="01a3_000c"><a name="01a3_000c">
<a name="01a3_000d">
[LISTING THREE]
<a name="01a3_000d">

*--------------------------------------- file information
*   isAlpha.a
*   Assembly language source code for the HyperCard XFCN isAlpha
*   Given a character, isAlpha returns the string 'true'
*       if the character is an uppercase or lowercase letter, the
*       string 'false' if it is not.
*       A c function prototype might look like this:
*      BOOLEAN_STRING isAlpha(CHAR chSomeChar) ;
*   Edited with MPW 2.02
*   Compiled under MPW 2.02
*   Written and )1988 by Stan Krute. All rights reserved.
*       No part of this file other files in the project it belongs to,
*       or the object code the file(s) lead(s) to, may be reproduced,
*       in any form or by any means, without the express written
*       permission of the author and copyright holder.
*-------------------------------------------------------------------


*-------------------------------------- include files

; standard Mac definitions
   INCLUDE    'QuickEqu.a'      ; quickdraw
   INCLUDE    'ToolEqu.a'      ; toolbox
   INCLUDE    'SysEqu.a'      ; system
   INCLUDE    'Traps.a'      ; traps

; our stuff
   INCLUDE    'XCMDandXFCN.a'      ; XCMD and XFCN definitions


*------------------------------------------ isAlpha -----------------

; set local constants
smallBlockSize      SET   6   ; size of a small result block we'll allocate
XCmdPtr         SET   8   ; where we'll find the XCmdPtr after register saving
bytesOfEntryParams   SET   4   ; bytes of entry parameters

isAlpha   MAIN
   ; save a register
   MOVE.L      A2,-(SP)

   ; get a small result block, filled with zeroes for cheap 0-terminated strings
   MOVE.L      #smallBlockSize,D0
   _NewHandle    ,CLEAR

   ; store the result block's handle as a return value
   MOVE.L      XCmdPtr(SP),A2             ; A2 gets pointer to XCmdBlock
   MOVE.L      A0,XCmdBlock.returnValue(A2)    ; handle to result block

   ; point to the result block
   MOVE.L      (A0),A0

   ; get the single entry parameter
   MOVE.L      XCmdBlock.params.zero(A2),A1      ; handle to first entry parameter
   MOVE.L       (A1),A1            ; pointer to first entry parameter

   ; clear a data register, and move entry parameter, a character, into it
   CLR.L   D0
   MOVE.B   (A1),D0

   ; now run a series of boundary tests
   ; check against the low bound of upper-casedness
   CMP.B   #'A',D0
   BLT.S   notAlpha   ; less than this, and we're not alphabetical

   ; check against the high bound of upper-casedness
   CMP.B   #'Z',D0
   BLE.S   yesAlpha   ; less than or equal to this, and we're upper-case,
            ; thus alphabetical

   ; check against the low bound of lower-casedness
   CMP.B   #'a',D0
   BLT.S   notAlpha   ; less than this, and we're not alphabetical

   ; check against the high bound of lower-casedness
   CMP.B   #'z',D0
   BGT.S   notAlpha   ; greater than this, and we're not alphabetical

yesAlpha
   ; we have an upper- or lower-case letter
   ; set result into result block
   MOVE.L   #'true',(A0)
   BRA.S   bye

notAlpha
   ; we have neither an upper- nor a lower-case letter
   ; set result into result block
   MOVE.L   #'fals',(A0)+
   MOVE.B   #'e',(A0)

bye   ; tell HyperCard we've handled things
   CLR.W   XCmdBlock.passFlag(A2)

   ; restore a register
   MOVE.L   (SP)+,A2

   ; move the return address into A0
   MOVE.L    (A7)+,A0

   ; jump over entry parameters
   ADD.W   #bytesOfEntryParams,A7

   ; puffasmoke and we be gone
   JMP    (A0)

   ; end of the procedure
   ENDPROC

   ; end of the assembly language source file



Example 1: C rendition of an XCmdBlock data structure

#include "Types.r"
resource 'SIZE' (-1, purgeable) {
    dontSaveScreen,
    ignoreSuspendResumeEvents,
    disableOptionSwitch,
    cannotBackground,
    notMultiFinderAware,
    150*1024, /* maximum size 150K */
    150*1024  /* minimum size 150K */
    };










Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.