TweetFollow Us on Twitter

XCMD Chat
Volume Number:4
Issue Number:11
Column Tag:HyperChat™

XCMD Cookbook

By Donald Koscheka, Apple Computer, Inc.

on HyperChat

A Nice Chat

While doing the Sunday crossword puzzle recently, I was hit with a very interesting thought. One of the clues in this particular puzzle called for a synonym for “small talk”. The answer was “chat”. This struck me as odd because in many ways HyperTalk is very much the preferred object oriented programming language for the Macintosh. One of the earliest object oriented languages, as you know, is called “SmallTalk”. By calling this column “HyperChat”, Fred makes a rather obscure reference to HyperTalk’s philosophical roots. Chat implies a looseness of speech, a vernacular that is easy to master. That is exactly what HyperTalk is - an easy to grasp interface to the Macintosh Toolbox. What HyperTalk lacks in power, it certainly makes up for in ease of understanding!

At the opposite end of the spectrum is Assembly language programming. This much maligned language conjures all sorts of anxieties in the minds of otherwise fearless programmers. Yet most professional programmers will admit that an understanding of assembly language can improve one’s ability to write efficient code in a higher-level language as well as better understand those mysterious looking dumps that one gets when suddenly presented with a memory dump from TMON or MACSBUG.

Worse yet, those of us who choose to work in higher level languages provide a great disservice to the assembly language programmer by not showing them how to interface with our languages. HyperTalk documentation is certainly sparse in describing how an assembly language programmer can write an XCMD in assembly.

Assembly Interface

I started writing this month’s column hoping to be of service to the assembly language programming community. I wasn’t very far along when I realized that this column offers a second service, at no extra charge to the reader.

The process of showing you how to interface to HyperCard in assembly language also introduces the Pascal and C programmer to debugging in Macsbug or TMON (Forth programmers take heart: Jörg Langkowski’s article in the December, 1987 issue of Mactutor provides you with the information you’ll need to write XCMDs; even if you don’t program in Forth-like languages, you should read Jörg’s column; his insights into the Macintosh are often astonishing).

The most important reason to code in Assembly language is that it provides a rich opportunity to improve on the more generic code generated by Pascal and C compilers. Consider the following string comparison in Pascal:

len: INTEGER;
Match : Boolean;
Str1  : Str255;
Str2  : Str255;

Str1 := ‘Hello World’;
Str2 := ‘GoodBye Cruel World’;


IF Length(Str1) <> Length( Str2 ) THEN 
 match := FALSE
ELSE
BEGIN
 Match := True;  { assume they will match}
 k := 1;

 While (k <= Length( Str1)) AND (match) DO
 IF Str1[k] <> Str2[k] THEN 
 match := False
 ELSE
 k := k + 1;
END;

Listing 1. String Comparisons in Pascal

String compares can be made more efficient than this in Pascal. I chose this example for its simplicity. In comparing strings, we must consider the end points, if the strings are not the same length, they are not equal. If you haven’t been programming in Assembly language, you may not see how this routine could be made more efficient. One measure of a program’s efficiency is a count of the number of bytes of instructions needed to execute the program. If you compile listing 1 and dump the object code, you would discover that the while loop requires 82 bytes of instructions to execute. Assembly language programmers know by instinct that 82 bytes is too much code to perform a single string compare.

An assembly language equivalent of the while loop in listing 1 can be shrunk by an order of magnitude as in listing 2. If you’re trying to speed up a particularly slow loop, a few bytes of well-written assembler might be just what the doctor ordered. But optimizing code is just one reason to familiarize yourself with assembly language. A more compelling reason for the high-level language is that an understanding of assembler will help you make sense of your TMON or MacsBug dumps.

 ; A0, A1 point to the 2 strings
 Move.b (A0)+, D1; get the length of string 1
 Move.b (A1)+, D2; get the length of string 2
 Cmp.b  D1,D2  ; are they the same length?
 Beq  CompareChars ; yes, go ahead and compare them
 Move #0, D0; set the result to false
 Bra  Done; and exit

CompareChars     ; compare Str1<->Str2
 Cmp.b  (A0)+,(A1)+; do the characters match?      
 Beq  Done; yes, see if we’re at end of string
 Dbra D1, CompareChars
 Move #1, D0; they match, set the result true

DONE  sne D0       ; Result is true if strings match,          
 false otherwise

Listing 2. String Compare in Assembler

Entering XCMDs

The real trick to writing an XCMD in assembly language is understanding that HyperCard expects to see an XCMD that was generated by the Pascal compiler. This implies that parameters are pushed on the stack from left to right and that subroutines are responsible for removing the pushed parameters from the stack. XCMDs receive only one parameter so the push order isn’t important. What is important is remembering to remove that parameter from the stack before returning to HyperCard. Listing 3 is the assembly language interface for XCMDs. The fields in the paramBlock record are identical to the Paramblock record in Pascal or C.

On entry to a subroutine in assembly language, the last item on the stack is the return address of the calling routine. Normally, when we are done with the subroutine, an rts (return from subroutine) instruction will pop this return address off the stack and into the Program Counter resuming execution at the instruction pointed to by that location. This won’t do for Pascal routines since convention dictates that we also remove the parameters from the stack. One way to do this would be to pop the return address into a temporary register, say D0, remove the parameters from the stack by adding the size of the parameters to the stack pointer and then pushing the contents of D0 onto the stack and executing an RTS. A more efficient method exists: Pop the return address into an address register, say A0. Unbias the stack parameters by adding 4 to the stack (the size in bytes of the parameter block pointer). Finally, since register A0 contains the return address, execute the Jmp Indirect instruction on A0: Jmp (A0).

Globals in XCMDs

If your XCMD requires local variables, you’re going to need a place to store them. Assembly language XCMDs bear the same restriction placed on high-level languages: you don’t have access to the globals. A simple solution would be to allocate a handle large enough to store all your globals and keep that handle available in an address register. This works fine if the data is very static but not very well otherwise since you could easily run out of registers while manipulating even a small number of handles. Pascal and C use a more efficient approach, one that you’ve already seen if you’ve done any debugging in TMON or Macsbug.

On entry to the subroutine, we know that the stack pointer, register A7, already points to the next available space on the stack (the bottom of the stack). Why don’t we allocate our data on the stack by pointing an address register, say A6, to the bottom of the stack, subtracting the number of bytes that we need for our locals from A7 effectively growing the stack by the amount we need (the stack grows downward). Now A6 points to our local variables and A7 continues its role as stack pointer below our local stack frame (figure 1 ).

The process of creating a stack frame can be performed with one assembly language instruction: link. Used judiciously, the link instruction buys us a whole lot more: it saves the old value of the address register and gives a reference point to the parameters passed by the caller.

By executing link A6,#LocalSize, before doing anything else, we set up up a stack frame at the stack bottom. If stacksize were set to zero, we wouldn’t actually allocate any stack space for globals, but the instruction would still provide a payback. Because nothing else was put on the stack between the call to this routine and our link instruction, A6 also doubles as a pointer to our parameters! First, 0(A6) contains the previous value of A6 (can you think of anything this might be useful for?), next 4(A6) is the return address, and 8(A6) is the paramblock pointer passed to us by HyperCard. By putting 8(A6) into A3, we save the pointer to our parameters in an address register.

The offsets defined in the parameter block equates now become offsets off A3 for each field. The first field in the record is paramCount(A3) the count of the number of parameters in the params array. Accessing the handles in the params array poses another question. The first handle in the array is at params(A3). Getting to the other handles in the params array requires a straightforward application of arithmetic. By definition, handles occupy 4 bytes in the Mac. Any array element, i, is at offset (i-1)*4 byte in the array. We subtract 1 from the element number because array indices count from 0 not from 1. If we put the array index into D0, subtract 1 and multiply by 4 we have the offset from the beginning of the array. All we need to do is add this number to params[A3] and we have the handle. The 68000 has just an instruction: indexed addressing with offset. Here is how this instruction can be used to get the third parameter in the list:

Moveq     #3, D0   ; Access the third parameter in the list 
Sub.w     #1, D0   ; arrays count from 0, not 1
Asl.w     #2, D0   ; shift left by 2 is same as multiply by 4! 
Move.l    params(A3,D0.w), A0 ; A0 now holds the third handle 

Since stack frames are oriented from the highest memory location they occupy to the lowest, local variables are always referred to by negative offsets. In Pascal, the following declaration

VAR
myInt   : INTEGER;
myLong  : LongINt;
myRect  : Rect;

would have the following counterpart in assembly language:

myInt        EQU -4        ; locals start at offset -4 in the frame
myLong  EQU myInt-2; Integers are 2 bytes
myRect      EQU myLong-4  ; longs are 4 bytes
LocalSize EQU myRect-8  ; rectangles are 8 bytes.
LocalSize is equal to 14 bytes.  The stack frame is set up to read:
LINK    A6,#LocalSize; create the local variable pool.
Move.w  (A0),D0

If you’re having a bit of difficulty with this material, try writing a simple XCMD in Pascal or C and disassembling it in TMON or Macsbug. You can do this by invoking the Debugger call as the first statement in you XCMD.

Exiting the XCMD

Leaving the XCMD requires a little house cleaning. First, we restore the registers that were saved onto the stack. Next, we execute an unlink (UNLK) instruction to undo the last Link instruction. At this point, the stack looks just like it did when we entered the XCMD. More importantly (A7) is the return address of the calling routine. Popping this value into A0 allows us to save the return address in a safe place so that we can remove the parameters from the stack. We know how much space the parameters take. The stack contains only one parameter, XCmdBlkPtr, whose length is four bytes. Adding 4 to A7 shrinks the stack to the right size. Now all that’s left is to Jmp to the location pointed to in A0 and we’re done!

Conclusion

If you’re already programming in assembly language, this article is enough to get you started with XCMDs, especially if you’re not familiar with Pascal calling conventions. If you’re a Pascal or C programmer, the above discussion should help you to debug your code by explaining some of the assembly code you may have been looking at in TMON or Macsbug. In any case, understanding how compilers take your statements and convert them into machine executable code is a great way to learn more about the inner-workings of your programs and those seemingly mysterious bugs that just are not obvious in your Pascal or C source code. Hopefully, I’ve been able to fill some gaps in your debugging skills with this information. If the information in this article was useful, please let me know, I’ll be happy to offer more information on assembly language and debugging techniques.


;******************************
;* File: SimpleXCMD.a*
;* *
;* A simple XCMD written in *
;* Assembly language to show*
;* how XCMDs are written in *
;* assembler...  *
;* -------------------------- *
;* By:  Donald Koscheka   *
;* Date:16 July, 1988*
;******************************

;******************************
;* Build Sequence
;*
;* asm -w SimpleXCMD.a
;* link  -rt XCMD=1200 -sn Main=SimpleXCMD
;* SimpleXCMD.a.o
;* -o “YourStackName”
;******************************

;***  ParamBlock structure***
paramCountEQU    0   
params  EQU paramCount+2
returnValue EQU  params+( 16 * 4 )     
passFlagEQU returnValue + 4 
entryPointEQU    passFlag + 2   
request EQU entryPoint + 4  
result  EQU request + 2
inArgs  EQU result + 2
outArgs EQU inArgs + ( 8 * 4 )
pBlkSizeEQU outArgs + (4 * 4 )

;*** ------------------ ***
;*** THE LOCAL VARIABLES  ***
;*** (WILL GO ON STACK)   ***
;*** Note that the stack frame***
;*** counts backwards from 0***
;*** so that the value of ***
;*** LocalSiz will always be  ***
;*** negative    ***

LOCALS  EQU 0
LASTLOCAL EQU    LOCALS
LOCALSIZEQU LASTLOCAL
;*** ------------------ ***


SimpleXCMDMAIN   EXPORT
;******************************
;* In:
;*
;* 0(A7) == Return Address
;* 4(A7) == ParamBlockPtr
;* 
;* Link A6 to create a stackframe
;* that points to these vars.
;******************************

 ;*** Set Up Stack Frame
 LINK   A6,#LOCALSIZ ; Size of the local frame
 MOVEM.LD5-D7/A3/A4,-(SP) ; save some registers
 
 ;*** Get Pointer to paramblock
 MOVE.L 8(A6), A3; Point to parameters
 CLR.L  returnValue(A3) ; set to “empty”
 TST.W  paramCount(A3)  ; Any Parameters?
 BEQ    DONE; no, just return
 
   ;*** Insert your code here.  If your XCMD doesn’t take any 
 ;*** parameters eliminate the atest on paramcount ...
 
DONE    ;*** Prepare for Return to HyperCard
 MOVEM.L(SP)+,D5-D7/A3/A4 ; restore registers
 UNLK   A6; wipe out stack frame
 MOVE.L (A7)+, A0; get the return address
 ADD.L  #4, A7   ; unbias the stack
 JMP    (A0); return to HyperCard
 END

Listing 3. SimpleXCMD in Assembly Language

end HyperChat
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Arq 5.9 - Online backup to Google Drive,...
Arq is super-easy online backup for Mac and Windows computers. Back up to your own cloud account (Amazon Cloud Drive, Google Drive, Dropbox, OneDrive, Google Cloud Storage, any S3-compatible server... Read more
LibreOffice 5.4.0.3 - Free, open-source...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
NeoFinder 7.1.2 - Catalog your external...
NeoFinder (formerly CDFinder) rapidly organizes your data, either on external or internal disks, or any other volumes. It catalogs all your data, so you stay in control of your data archive or disk... Read more
Numi 3.17 - Menu-bar calculator supports...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
VOX 2.8.28 - Music player that supports...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more
Chromium 60.0.3112.78 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 60.0.3112.78: Security Fixes High CVE-2017-... Read more
Safari Technology Preview 11.0 - The new...
Safari Technology Preview contains the most recent additions and improvements to WebKit and the latest advances in Safari web technologies. And once installed, you will receive notifications of... Read more
Geekbench 4.1.1 - Measure processor and...
Geekbench provides a comprehensive set of benchmarks engineered to quickly and accurately measure processor and memory performance. Designed to make benchmarks easy to run and easy to understand,... Read more
iMazing 2.3.3 - Complete iOS device mana...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
TeamViewer 12.0.81279 - Establish remote...
TeamViewer gives you remote control of any computer or Mac over the Internet within seconds, or can be used for online meetings. Find out why more than 200 million users trust TeamViewer! Free for... Read more

Latest Forum Discussions

See All

Knight Fever Beginner’s Guide - how to h...
Knight Fever is a thrilling new mobile RPG from Webzen, featuring colorful pixelated sprites and some of the most innovative combat we’ve seen in modern RPGs to date. It’s full of swords and sorcery and other fantastical feats. Given the game’s... | Read more »
MU Origin celebrates its first birthday...
A year ago, Seoul-based publisher Webzen added to its MU franchise with an MMO for the mobile platform. MU Origin features elements from its 2003 predecessor, updated to fit in amongst other modern freemium fantasy-action apps. You loot dungeons,... | Read more »
Galaxy of Pen & Paper has landed on...
If you enjoyed Knights of Pen & Paper and its sequel, you're in for a real treat, as Galaxy of Pen & Paper has officially launched on the App Store. Galaxy takes us far from the irreverent fantasy setting of Knights of Pen & Paper to a... | Read more »
Big changes are coming to Fire Emblem He...
Nintendo just revealed what players can expect from Fire Emblem Heroes in the coming weeks, with a good deal of fresh goodies arriving just in time for the game's six-month anniversary. The announcements arrived in a 15-minute FEH Channel stream... | Read more »
Aero Effect (Games)
Aero Effect 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: Race an aerodynamic thingy through a maze of animated, geometric peril and plunge into a mysterious grid of pixels, all while... | Read more »
Linelight (Games)
Linelight 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Linelight is an elegant, minimalist puzzle game set in a universe of lines. Its puzzles will awake your mind as the music flows... | Read more »
Fighting Fantasy Legends (Games)
Fighting Fantasy Legends 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Create your own adventures in a dangerous land of monsters, treasures and traps. From renowned authors Steve Jackson... | Read more »
Knight Fever is a new take on the classi...
Knight Fever lands on the Google Play, and you won’t want to miss it if you’re a devoted RPG fan. Developed by indie creatives Buff Studios, the game looks like a retro RPG with some exciting new twists. | Read more »
Steam Panic (Games)
Steam Panic 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: Steampunk puzzle in which you spin the playfield to put yourself back together. Dear Reader,Enclosed is a copy of my bizarre 1935... | Read more »
Time Crash (Games)
Time Crash 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: Time is about to Crash! It’s up to you to save the city! Time Crash is a 3D first person runner which lets you play as a powerful... | Read more »

Price Scanner via MacPrices.net

Retrofit Your MacBook Air 13.3 For Touchscree...
Apple famously refuses to make touchscreen support available in its MacBook family of clamshell notebooks. There is no indication that Apple is going to relent anytime soon, but you can now still... Read more
Apple Stores Are Retail’s Most Profitable Squ...
A Marketnewsupdates.com (MNU) News Commentary release distributed by PRNewswire says that according to research provided by CoStar, sales per square foot at all but a few public retailers have... Read more
Sale! 15-inch 2.8GHz MacBook Pros for $100 of...
B&H Photo has the new 2017 15″ 2.8GHz Touch Bar MacBook Pros on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only: – 15″ 2.8GHz MacBook Pro Space Gray: $... Read more
2016 13-inch MacBook Airs available starting...
B&H Photo has clearance 2016 13″ MacBook Airs available for up to $150 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 13″ 1.6GHz/128GB MacBook Air (MMGF2LL... Read more
Clearance iMacs available for up to $400 off...
B&H Photo has clearance 21″ and 27″ Apple iMacs available for up to $400 off original MSRP, each including free shipping plus NY & NJ sales tax only: – 27″ 3.3GHz iMac 5K: $1899 $400 off... Read more
27-inch 3.5GHz iMac on sale for $100 off MSRP...
Adorama has the new 27″ 3.5GHz iMac (MNEA2LL/A) on sale for $1899 including free shipping. Their price is $100 off MSRP. Adorama charges sales tax for purchases in NY & NJ only. Read more
Seven Cities, One Phone: OtterBox Sends iPhon...
Plenty of people have trekked around the world, but what about a globe-trotting iPhone? OtterBox is sending an iPhone around the world to capture the adventures of a diverse set of global ambassadors... Read more
L-Card Pro App May Spell End For Paper Busine...
OrangeTreeApps, LLC has announced the release of L-Card Pro 1.1, an update to their business app for iOS and Android devices that introduces eco-friendly, affordable, electronic business cards on the... Read more
Clearance previous generation iMacs, Apple re...
Apple has previous-generation Certified Refurbished 2015 21″ & 27″ iMacs available starting at $849. Apple’s one-year warranty is standard, and shipping is free. The following models are... Read more
27-inch 3.4GHz iMac on sale for $1699, save $...
MacMall has the new 2017 27″ 3.4GHz iMac (MNE92LL/A) in stock and on sale for $1699 including free shipping. Their price is $100 off MSRP. Read more

Jobs Board

*Apple* Solutions Consultant - Apple Inc. (U...
Job Summary As an Apple Solutions Consultant, you'll be the link between our future customers and our products. You'll showcase your entrepreneurial spirit as you Read more
Senior Software Engineer, *Apple* Online St...
Job Summary The Apple Online Store is looking for an experienced, self-driven, detail-oriented software engineer who can join our team to help build highly scalable Read more
Frameworks Engineering Manager, *Apple* Wat...
Frameworks Engineering Manager, Apple Watch Job Number: 41632321 Santa Clara Valley, California, United States Posted: Jun. 15, 2017 Weekly Hours: 40.00 Job Summary Read more
Senior Software Engineer, *Apple* Online St...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more Read more
Frameworks Engineering Manager, *Apple* Wat...
Frameworks Engineering Manager, Apple Watch Job Number: 41632321 Santa Clara Valley, California, United States Posted: Jun. 15, 2017 Weekly Hours: 40.00 Job Summary Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.