TweetFollow Us on Twitter

Forth DA
Volume Number:3
Issue Number:2
Column Tag:Threaded Languages

Building Desk Accessories with Forth

By Jörg Langowski, MacTutor Editorial Board, Grenoble, France

A template desk accessory in Mach2

Any of our readers who has taken a close look at Mach2 code will have noticed that the output generated by the Forth compiler resembles very much that of 'classical' compiled languages like Pascal. Disassembly shows that much of the code is inline-generated machine code, and references to kernel routines are not too frequent.

This is one of the strong points of Mach2; retaining most of the ease of programming and debugging with a Forth interpreter, you can not only generate stand-alone applications, but also things that are much more dependent on interacting directly with the operating system such as VBLtasks that run independently of a runtime Forth kernel (see my article in V2#6 ); one could also imagine to create INIT resources, MDEFs or WDEFs and... desk accessories.

The problem with Forth code in general is that a 'stand-alone-application' generated by any Forth system available for the Macintosh contains - and is dependent on - at least part of the run time Forth kernel. Whether the kernel contains the interpreter (as in MacForth) or the task of interpreting is taken over by the CPU itself in subroutine threaded code such as Mach2 - the standard I/O routines, window handling, controls, menus etc. all come in a pre-written package that will form part of the stand-alone application. To write something like a desk accessory in Forth, this would imply that the runtime package had to be part of the DA. This is (a) not very practical because space-consuming and (b) not an available option in any Forth for the Macintosh that I'm aware of.

Nevertheless, Mach2 allows us to create a functioning desk accessory without too much effort and allows me to simultaneously illustrate some principles of DA programming to you at the same time.

DA strategy for implementation in Mach2

How would we proceed to build a desk accessory using Mach2? The DA is a DRVR resource. We would have to create this resource in memory first, then write it out to a resource file. There are resource manager routines that allow us to do this; AddResource will add a new resource to the current resource file, given a handle to a data structure in memory, and UpdateResFile saves these changes to the resource file. So all we have to do is to create a data structure of the format of a desk accessory, get a handle to it and call AddResource with the type DRVR to create the new driver, then update the resource file.

The general format of a desk accessory is known from Inside Macintosh. The first couple of bytes are a header containing flags that tell the system whether the DA needs to be called periodically, and what type of calls it can respond to; a delay setting that determines the time between periodic actions; and event mask that determines what events the desk accessory will respond to; a menu ID (negative) if the DA has a menu associated with it; and offsets to the Open, Prime, Control, Status and Close routines. These latter offsets are measured from the beginning of the desk accessory to the beginning of each of the routines. The last portion of the header is a string containing the driver name.

The remaining portion of the desk accessory may be executable code and can be written in Mach2 Forth if we make sure that references to kernel routines are avoided. Before we discuss the example (listing 1), however, let me briefly summarize what happens when a desk accessory is opened.

Fig. 1 Our simple DA, written in Forth!

Opening the desk accessory

When the DA (or any driver) is opened for the first time, a device control entry is created by the system, a data structure which contains information about the driver; it is described in Inside Macintosh (II-190). It will, for instance, contain the driver flags, the delay setting, the event mask and the menu ID from the desk accessory header. Also a window pointer to a window associated with the driver may be stored here or a handle to a memory block if the DA needs to store large amounts of data.

When a driver routine (open, control, close, status, prime) is called, a pointer the driver's device control entry is passed in register A1. The other parameter, passed in A0, is a pointer to the parameter block of the call. For desk accessories, this parameter block is important for Control calls since we'll be able to tell from the parameter block what has happened that the desk accessory has to respond to, such as menu selections, periodic actions, editing commands, etc.

Glue routines

Since the parameters to the driver routines are passed in registers, we'll have to write some assembler code to make them 'digestible' for Forth, which is stack-oriented. Also, we'll save A0-A1 and restore them after the call; as IM mentions, no other registers have to be saved. One important thing to remember is that we have to setup a Forth stack before using any actual Forth code; we make A6 point to a data block sufficiently large (100 bytes in the example, but you may easily change this to have more stack space).

The glue routines then in turn call the driver routines which have been written in Forth. The routines referenced in the DA header are the glue routines, of course. The final part of listing 1 contains the stack setup, the glue routines, and the part that initializes the desk accessory header and writes the driver code to a resource file.

The code written is contained between the 'markers' startDA and endDA. For adding the resource to the file, the word make-res is provided, which gets a handle to a data structure in memory and calls AddResource with the handle, the resource type (DRVR in our case) and a resource file ID as parameters.

init-DA will initialize the desk accessory's header. This includes setting the driver flags, the driver name, an event mask, the delay time between periodic actions, and calculating and storing the offsets between the start of the DA and the beginning of the 'glue' routines. Also, the ID of the DA's own menu is stored in the header; we'll come back to that later.

make-DA calls init-DA and then writes the newly created DRVR resource with ID=12 into the file "Mach2 DA.rsrc". This file can then be used by RMaker to create a Font-DA Mover compatible file that contains the DA and any resources owned by it.

DA-owned resources

Listing 2 shows the RMaker input file. It will include the DRVR code from "Mach2 DA.rsrc" and two more resources, a window template and a menu. Both these resources have an ID of -16000, which later indicates to the Font/DA Mover that they are 'owned' by the desk accessory whose ID is 12; they will therefore be moved together with the DA. If the DA's ID is changed during the move, their IDs will be changed accordingly so that they always correspond to that of the DA.

The format of the ID number of an owned resource is given in IM (I-109); I'll briefly review it here. The ID number is always negative and bits 15 and 14 are always 1. Bits 11-13 specify the owning resource type, and are zero for a DRVR. Bits 5-10 contain the ID number of the owning resource, which therefore must be between 0 and 63. Bits 0-4 may contain a number which identifies the individual resource. Therefore, the allowed number range for owned resources is between -16384 and -1.

If the DRVR resource has an ID=12, the IDs of the owned resources start with -16000 ( if bits 0-4 are zero) and go up to -15969 ( bits 0-4 = 31). Since -16000 is a simple number to remember, the DRVR is given an ID of 12 when it is created by the program. Both the MENU and WIND resources owned by the DRVR in the example will have IDs of -16000 (which correspond to 'local' IDs of 0).

Two Forth words are provided to easily convert local IDs to owned resource IDs. getDrvrID will calculate the driver ID from its reference number, which is kept in the device control entry; and ownResID will calculate the owned resource ID from the driver ID and the local resource ID.

The desk accessory

We can now take a look at the desk accessory's main code. The Open routine is called by the DAOpen glue routine. It will do nothing if the desk accessory's window is already open, which can be checked by looking at the dCtlWindow field in the device control entry. If the DA has not been opened yet, or has been opened and then closed again, it will create a new window from the WIND resource with local ID=0 and store its pointer in the device control entry; furthermore, it stores the driver reference number in the windowKind field of the window record. By this means, the desk manager will know that a window belongs to the DA, how to find it and to send the appropriate messages to the DA when the window is activated, deactivated, the mouse clicked in its content region or when it is closed.

Open will in addition calculate the ID of the MENU resource (local ID=0) that is owned by the DA. This number is also stored in the DA header, but you cannot reliably assume that it is correct. Font/DA Mover will change the DA's ID and the IDs of its own resources correctly, but it doesn't go into the DA header and sets the correct menu ID. However, some negative menu ID has to be present in the DA header in order to tell the desk manager that the DA has to respond to menu selections. The device control entry, however, has to contain the correct menu ID; otherwise the DA won't respond to its own menu.

Close will store zero in the dCtlWindow field so that a new Open will re-create the window; it deletes the DA's menu and disposes of the heap space occupied by window and menu, then it redraws the menu bar. Close is called automatically by the Desk Manager when the close box of the DA window is clicked, or Close is selected from the File menu of an application where the DA was called.

The Prime and DrStatus routines are not needed here, and will do nothing at all.

Sending messages to the DA

The heart of the desk accessory is the Ctl routine. This routine will receive a message from the desk manager to indicate which action should be taken - a very simple implementation of object-oriented behavior, in fact. I have written a shell routine that handles some of the actions of a desk accessory; all other actions simply do nothing, but since they are included in the case statement, you can very easily add your own routines.

The message code is contained in the csCode parameter, which is in the parameter block whose address was passed in A0 when Ctl was called. Ten messages are possible:

-1 : 'good bye kiss' that will be given just before an application exits to the finder;

64 : an event should be handled;

65 : take the periodic action that has to be performed by the DA. This message is sent every time the number of ticks in the drvrDelay field of the DA header has expired;

66 : The cursor shape should be checked and changed if appropriate. This message is sent each time SystemTask is called by the application, as long as the DA is active;

67 : A menu selection should be handled. csParam contains the menu ID and csParam+2 the menu item number;

68 : handle Undo command from the Edit menu;

70 : handle Cut command;

71 : handle Copy command;

72 : handle Paste command;

73 : handle Clear command.

The example implements handlers for the first five actions; no Edit menu selections are handled. The periodic action simply consists of a short beep once every second. (You might want to change this to save your sanity if you really want to do something useful with this desk accessory). The goodBye action is also a beep, but 50 ticks long. When the desk accessory is active and you close an application, it will sound almost as if the system reboots. Don't let yourself be bothered by this.

The accCursor message will call update-cursor, which checks whether the mouse is inside the DA window and changes the cursor to the standard NNW arrow, if necessary.

The menu and event handlers are a little more complicated. First, since both will output text to the DA window, we have to write some rudimentary output routines; the Mach2 output routines won't work without the kernel. tp will type a string in the current grafPort, and crd acts the same way as cr in the Forth kernel. I've also included some numeric output routines, which you might find convenient to use; they are not needed for the example, although I used them in debugging.

The event handler(s)

The DA's response to the accEvent message has to be subdivided according to the event that has happened. Therefore, we check the what field of the event record and set up another case statement that contains the handlers for each type of event. The behavior that we'll give to our DA window is that of a document window with zoom box and size box that responds to mouse down and key down events by displaying appropriate messages in the window. The DA's own menu should be displayed when the window is activated and removed when it is deactivated.

The activate handler, therefore, first checks whether the window is activated or deactivated, gets the menu from the resource file in the first case and attaches it to the menu bar, or deletes it in the latter case.

The update handler will clear the update region and redraw the grow icon. Key down events will clear the window and display a message in the first line.

Mouse down events cannot be handled as easily as with application windows. If you call findWindow when the mouse is clicked in a desk accessory window, the code returned is always 2 (= in system window), no matter what part of the system window was clicked. The drag region and close box are handled by the desk manager, so no problem there; but we have to check ourselves for clicks in the size or zoom box. This is especially annoying for the zoom box, because we also have to keep a record of the current zoom state of the window. With application windows, the window manager takes care of this task, changing the part code that is returned by FindWindow depending on whether we have to zoom in or out.

I defined the words ?inGrow and ?inZoom that return true when the mouse click (in local coordinates) was in the size box or in the zoom box of the active window. The zoom state has to be maintained by a flag. The mouse down event handler will check for size box and zoom box clicks and change the window accordingly. The region that comprises the grow icon - which is part of the content region - will have to be added to the update region after the window has been made smaller or before it is enlarged. The word invalsize has been defined for this purpose; for resizing with the size box, we just call it before and after the resizing since we don't know the new window size. For zooming, we call invalsize before zooming in and after zooming out.

If the mouse is clicked in the content region, the window responds simply by typing a message, followed by a new line.

This takes care of mouse down events; now menus are the last thing we have to include. If the accMenu message is received by the DA, the menu item number is extracted from the parameter block and a message displayed according to the item number. Add any of your own routines here if you like.

Getting the DA started

To add the desk accessory to your system file, just load the Forth program and type make-DA. This will create a file "Mach2 DA.rsrc" on the default disk. Then run RMaker with the input given in listing 2, which will give you a small briefcase called "Mach2 DA". It contains the DRVR, MENU and WIND resources and may be used as an input to the Font/DA Mover to install the desk accessory in the system file. Good luck (you don't really need it, though).

Using the template to write 'useful' DAs

In the unlikely case that you would want to beef up the DA example to do something really useful, you should be careful about a couple of things.

First, make sure that no Forth word you use in yout routines makes part of the Mach2 kernel. This can easily be checked by including the word in a simple definition and disassembling it. If you see a JSR to a jump table entry or a JSR xxx(A5) at the position of the word, you can't use it in a DA. JSRs are only legal if they point to routines that you defined yourself. The only Mach2 words you may use are those that directly compile 68000 code; fortunately, there are quite a few of them. Some others you have to redefine: the multiply and divide operators, for example.

Second, A6 stack space may become a problem if your routines get more complicated. This is easily taken care of, but equally easy to overlook.

Third, if you redefine any general purpose routines that create kernel-independent code and could be useful to others in writing their DAs, don't hesitate to drop me a line. Nothing is more frustrating than having to reinvent the wheel...

{1}
Listing 1: Desk Accessory written in Mach2
( Mach2 desk accessory with owned resources )
( J. Langowski / MacTutor Nov. 86 )

only forth also assembler also mac

HEX
44525652 CONSTANT "drvr

BINARY
0000110111101010 CONSTANT DAEmask

( *** System globals *** )
HEX
8FC CONSTANT JioDone 

DECIMAL
( windowrecord fields, starting with grafport )
16 CONSTANT portRect ( Grafport rectangle )

( fields of WindowPeek )
108 CONSTANT windowKind 
110 CONSTANT wVisible
111 CONSTANT wHiLited
112 CONSTANT goAwayFlag
113 CONSTANT spareFlag
130 CONSTANT dataHandle
140 CONSTANT controlList
152 CONSTANT refCon

( fields of device control entry )
 4 CONSTANT dCtlFlags
 6 CONSTANT dCtlQHdr
16 CONSTANT dCtlPosition
20 CONSTANT dCtlStorage
24 CONSTANT dCtlRefNum
26 CONSTANT dCtlCurTicks
30 CONSTANT dCtlWindow
34 CONSTANT dCtlDelay
36 CONSTANT dCtlEMask
38 CONSTANT dCtlMenu

( csCodes for Ctl calls )
-1 CONSTANT goodBye
64 CONSTANT accEvent
65 CONSTANT accRun
66 CONSTANT accCursor
67 CONSTANT accMenu
68 CONSTANT accUndo
70 CONSTANT accCut
71 CONSTANT accCopy
72 CONSTANT accPaste
73 CONSTANT accClear

( *** standard parameter block data structure *** )
0   CONSTANT  qLink( pointer to next queue entry )
4   CONSTANT  qType( queue type )
6   CONSTANT  ioTrap ( routine trap )
8   CONSTANT  ioCmdAddr ( routine address )
12  CONSTANT  ioCompletion( addr of completion routine )
16  CONSTANT  ioResult  ( result code returned here )
18  CONSTANT  ioNamePtr ( pointer to file name string)
22  CONSTANT  ioVRefNum ( volume reference number )
24  CONSTANT  ioRefNum
26  CONSTANT  csCode ( type of control call )
28  CONSTANT  csParam( control call parameters )

( *** eventrecord data structure *** )
0  CONSTANT what
2  CONSTANT message
6  CONSTANT when
10 CONSTANT where
14 CONSTANT modifiers

( *** event codes *** )
0  CONSTANT null-evt
1  CONSTANT mousedn-evt
2  CONSTANT mouseup-evt
3  CONSTANT keydn-evt
4  CONSTANT keyup-evt
5  CONSTANT autokey-evt
6  CONSTANT update-evt
7  CONSTANT disk-evt
8  CONSTANT activate-evt
10 CONSTANT network-evt
11 CONSTANT driver-evt

CODE shl ( data #bits )
 MOVE.L (A6)+,D0
 MOVE.L (A6),D1
 LSL.L  D0,D1
 MOVE.L D1,(A6)
 RTS
END-CODEMACH

CODE shr ( data #bits )
 MOVE.L (A6)+,D0
 MOVE.L (A6),D1
 LSR.L  D0,D1
 MOVE.L D1,(A6)
 RTS
END-CODEMACH

( *** start of desk accessory main code *** )

header testDA ( marker for writing to DRVR resource )
 header drvrFlags  2 allot
 header drvrdelay  2 allot
 header drvrEMask  2 allot 
 header drvrMenu   2 allot
 header drvrOpen   2 allot
 header drvrPrime  2 allot
 header drvrCtl  2 allot
 header drvrStatus 2 allot 
 header drvrClose  2 allot
 header drvrname  32 allot

( *** main desk accessory routines *** )
header oldPort 4 allot    ( for storage of old grafPtr )
header temprect 8 allot
header SizeRect 8 allot   ( grow size limits )
header mouseLoc 4 allot   ( mouse location )
header NewSize 4 allot    ( for SizeWindow )
header penLoc 4 allot( pen location )
header tempString 256 allot   ( for numeric conversion etc. )
header zoomState 4 allot  ( zoomed in or out )

: whereMouse ['] mouseLoc call getMouse ['] mouseLoc @ ;

: cl  ( WPtr -- ) portrect + call eraserect ;

: tp  call drawstring ;

: crd ['] penLoc call getpen
 10 ( horizontal boundary )
 ['] penLoc w@ 12 +
 call moveto
;

CODE NumToString
 MOVE.L (A6)+,A0
 MOVE.L (A6),D0
 MOVE.W #0,-(A7)
 _Pack7
 MOVE.L A0,(A6)
 RTS
END-CODE

CODE StringToNum
 MOVE.L (A6),A0
 MOVE.W #1,-(A7)
 _Pack7
 MOVE.L D0,(A6)
 RTS
END-CODE

CODE unpack
 MOVE.L (A6),D0
 CLR.L   D1
 MOVE.W D0,D1
 CLR.W  D0
 SWAP.W D0
 MOVE.L D0,(A6)
 MOVE.L D1,-(A6)
 RTS
END-CODE

CODE pack
 MOVE.L (A6)+,D1
 MOVE.L (A6),D0
 SWAP.W D0
 MOVE.W D1,D0
 MOVE.L D0,(A6)
 RTS
END-CODE

: .d ( num -- )  ['] tempstring NumToString  tp  ;
 
( *** event-handling routines *** )

: activate-handler { menuID DAWind event-rec | -- }
 event-rec modifiers + w@ 1 and
 IF ( window activated )
 menuID call getRMenu 0 call InsertMenu  
 call drawMenuBar
 ELSE ( window deactivated )
 menuID call deleteMenu
 menuID call getRMenu call DisposMenu
 call drawMenuBar
 THEN
;
 
: update-handler { DAWind event-rec | -- }
 ['] penLoc call GetPen
 DAWind CALL BeginUpdate
    DAWind cl
    DAWind CALL DrawGrowIcon
 DAWind CALL EndUpdate
 ['] penLoc 2+ w@ ['] penLoc w@ 
 call moveto ( restore pen position )
;

: ?inGrow { localPt WPtr | b r -- flag }
 WPtr portRect + 4 +
 dup w@ -> b 2+ w@ -> r
 ['] temprect r 14 - b 14 - r b call setrect
 localPt ['] tempRect call PtInRect 
;

: ?inZoom { localPt WPtr | r -- flag }
 WPtr portRect + 6 + w@ -> r
 ['] temprect r 20 -   -16   r 8 -   -4 call setrect
 localPt ['] tempRect call PtInRect 
;

: invalSize { gPort | b r -- }
 gPort 4 + w@ -> b
 gPort 6 + w@ -> r
 ['] temprect r 16 - 0 r b call setrect
 ['] temprect call invalrect
 ['] temprect 0 b 16 - r b call setrect
 ['] temprect call invalrect
;

: mousedn-handler { DAWind event-rec | whereM DAPort -- }
 DAWind portrect + -> DAPort
 event-rec where + @ -> whereM
 whereM ['] mouseLoc !
 ['] mouseloc call GlobalToLocal
 ['] mouseloc @ dup 
 DAWind ?inGrow  
 IFDAPort invalSize
 DAWind whereM ['] SizeRect call GrowWindow 
 DAWind swap unpack swap -1 call sizewindow
 DAPort invalSize
 ELSE 
   DAWind ?inZoom
   IF   ['] zoomState @
 IF 0 ['] zoomState !
    DAWind whereM 7 call TrackBox
 IF DAPort invalSize
    DAWind 7 0 call ZoomWindow THEN
 ELSE 1 ['] zoomState !   
    DAWind whereM 8 call TrackBox
 IF DAWind 8 0 call ZoomWindow 
    DAPort invalSize THEN
 THEN
   ELSE ( in content region )
 " Mouse down" tp crd
   THEN
 THEN
;

: update-cursor  { DAWind | -- }
 whereMouse DAWind portrect + call PtInRect
 IF call InitCursor THEN
;

: getDrvrID { dCtlEntry | -- num }
 dCtlEntry dCtlRefNum + w@ l_ext
 1+ negate
;

: ownResID ( resID drvrID )
 5 shl + -16384 +
;

: Open { DCtlEntry ParamBlockRec | DAWind -- }
 ['] oldPort call GetPort
 dCtlEntry dCtlWindow + @
 0= IF ( not open already )
 0 dCtlEntry getDrvrID ownResID 
 dup dCtlEntry DCtlMenu + w! 
 ( menu ref has to be updated )
 0 0 call getNewWindow -> DAWind
 DAWind  dCtlEntry dCtlWindow + !  
 ( store window pointer )
 DAWind  dCtlEntry dCtlRefNum + w@  
 swap windowKind + w!
 DAWind  call setport
 0 ['] zoomState !
 ['] sizerect 50 50 500 320 call setrect
 10 10   call moveto
 ['] oldPort @ call setPort
 THEN
;

: Close { DCtlEntry ParamBlockRec | -- }
 dCtlEntry dCtlWindow + 
 dup @ call DisposWindow  0 swap ! 
 ( so that Open will work again )
 DCtlEntry DCtlMenu + w@ ( get menu ID )
 dup call deletemenu
 call getRMenu call disposMenu call drawMenuBar
;

: Ctl 
  { DCtlEntry ParamBlockRec | DAWind event-rec menuItem -- }

 ['] oldPort call GetPort
 dCtlEntry dCtlWindow + @ dup -> DAWind call setport
 ParamBlockRec csCode + w@ l_ext 
 CASE
 goodByeOF 50 call sysbeep ENDOF
 accEvent OF 
 ParamBlockRec csParam + @ -> event-rec
 event-rec what + w@ 
 CASE
 mousedn-evtOF   
 DAWind event-rec mousedn-handler ENDOF

 keydn-evt OF DAWind cl
 DAWind call DrawGrowIcon
 10 10 call moveto  " Key down." tp crd
 ENDOF

 autokey-evtOF ENDOF

 update-evt OF
 DAWind event-rec update-handler ENDOF

 disk-evt OFENDOF

 activate-evt  OF
  DCtlEntry DCtlMenu + w@ ( get menu ID )
  DAWind event-rec activate-handler  ENDOF

 network-evtOF ENDOF
 driver-evt OF ENDOF

 ENDCASE

 ENDOF

 accRun OF    1 call sysbeep  ENDOF
 accCursor  OF DAWind update-cursor ENDOF
 accMenuOF
 ParamBlockRec csParam + 2+ w@ l_ext
 CASE 1 OF " Item1!" tp crd ENDOF
 2 OF " Item2!" tp crd ENDOF
 3 OF " Item3!" tp crd ENDOF
 4 OF " Item4!" tp crd ENDOF
 6 OF " Item6!" tp crd ENDOF
 ENDCASE
 0 call HiLiteMenu
 ENDOF
 accUndoOFENDOF
 accCut OFENDOF
 accCopyOFENDOF
 accPaste OFENDOF
 accClear OFENDOF
 ENDCASE
 ['] oldPort @ call setPort
;

: DrStatus { DCtlEntry ParamBlockRec | -- }
;

: Prime { DCtlEntry ParamBlockRec | -- }
;

( *** glue routines *** )
header local.stack 200 allot

CODE setup.local.stack
    LEA -8(PC),A6   ( local stack grows downward from here )
    RTS
END-CODE

CODE DAOpen 
 MOVEM.L A0-A1,-(A7)
 setup.local.stack
 MOVE.L  A1,-(A6) 
 MOVE.L  A0,-(A6)
 Open
 CLR.L  D0
 MOVEM.L (A7)+,A0-A1 
RTS END-CODE

CODE DAClose  
 MOVEM.L A0-A1,-(A7)
 setup.local.stack
 MOVE.L  A1,-(A6) 
 MOVE.L  A0,-(A6)
 Close
 CLR.L   D0
 MOVEM.L (A7)+,A0-A1 
RTS END-CODE

CODE DACtl 
 MOVEM.L A0-A1,-(A7)
 setup.local.stack
 MOVE.L  A1,-(A6) 
 MOVE.L  A0,-(A6)
 Ctl
 CLR.L   D0
 MOVEM.L (A7)+,A0-A1
 MOVE.L  JioDone,-(A7) 
RTS END-CODE

CODE DAStatus 
 MOVEM.L A0-A1,-(A7)
 setup.local.stack
 MOVE.L  A1,-(A6) 
 MOVE.L  A0,-(A6)
 DrStatus
 CLR.L   D0
 MOVEM.L (A7)+,A0-A1 
RTS END-CODE

CODE DAPrime 
 MOVEM.L A0-A1,-(A7)
 setup.local.stack
 MOVE.L  A1,-(A6) 
 MOVE.L  A0,-(A6)
 Prime
 CLR.L   D0
 MOVEM.L (A7)+,A0-A1 
RTS END-CODE

header endDA
 ( *** code written to DRVR resource ends here *** )

( *** initialization routines *** ) 

: setFlags  ['] drvrFlags w! ;
: setDelay  ['] drvrDelay w! ;
: setEMask  ['] drvrEMask w! ;
: setMenuID ['] drvrMenu  w! ;

: setOpen ['] drvrOpen  w! ;
: setPrime['] drvrPrime w! ;
: setCtl['] drvrCtlw! ;
: setStatus ['] drvrStatusw! ;
: setClose['] drvrClose w! ;

: setName { addr len | target -- }
 ['] drvrName -> target
 len target c!
 addr target 1+
 len 31 > if  31 else len then
 cmove
;
 
( write resource to file ) 
: $create-res ( str-addr - errcode )
 call CreateResFile
 call ResError L_ext
;

: $open-res { addr | refNum - refNum or errcode }
 addr call OpenResFile -> refNum
 call ResError L_ext
 ?dup IF ELSE refNum THEN
; 

: close-res ( refNum - errcode )
 call CloseResFile
 call ResError L_ext
;

: make-res { addr len rtype ID name | -- }
 addr len call PtrToHand 
 abort" Could not create resource handle"
 rtype ID name call AddResource
;

: write-out { filename | refnum -- } 
 filename $create-res 
 abort" That resource file already exists"
 filename $open-res
 dup 0< abort" Open resource file failed"
 -> refnum
 refnum call UseResFile
 ['] testDA ['] endDA over - 
 "drvr 12 " Mach 2 DA" make-res
 refnum close-res abort" Could not close resource file"
;

: install-system { | refnum -- }
 " System" $open-res
 dup 0< abort" Open resource file failed"
 -> refnum
 refnum call UseResFile
 "drvr 25 call getresource call rmveresource
 ['] testDA ['] endDA over - 
 "drvr 25 " Mach 2 DA" make-res
 refnum call UpdateResFile
;

: init-DA
( initialize offsets )
 ['] DAOpen ['] testDA -  setOpen 
 ['] DAPrime   ['] testDA -   setPrime
 ['] DACtl     ['] testDA -   setCtl 
 ['] DAStatus  ['] testDA -   setStatus
 ['] DAClose   ['] testDA -   setClose
( initialize driver name )
 " Mach 2 DA" count setname
( initialize driver flags, NeedTime, NeedGoodBye, CtlEnable )
 [ hex ] 3400 setFlags [ decimal ]
( initialize delay time )
 60 setDelay
( initialize event mask, events recommended in IM )
 DAEMask setEMask 
( initialize menu ID, local ID=0 for DRVR ID=12 )
 -16000 setMenuID 
 ( careful! this field will NOT be changed
 by the DA Mover when ID is changed )
;
 
: make-DA
 init-DA
 " Mach2 DA.rsrc" write-out
;

: install-DA init-DA install-system bye ;
{2}
Listing 2: RMaker input
file for the DA example

*   Resources for MACH 2 
desk accessory J. Langowski 1986

Mach2 DA
DFILDMOV

INCLUDE Mach2 DA.rsrc

Type MENU
     ,-16000
My DA
Item 1
Item 2
Item 3
Item 4
(-
Item 6


Type WIND
     ,-16000
Mach2 Desk Accessory
100 131 300 381
Visible GoAway
8
0
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Posterino 4.4 - Create posters, collages...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more
Chromium 119.0.6044.0 - 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. List of changes available here. Version for Apple... Read more
Spotify 1.2.21.1104 - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
Tor Browser 12.5.5 - Anonymize Web brows...
Using Tor Browser you can protect yourself against tracking, surveillance, and censorship. Tor was originally designed, implemented, and deployed as a third-generation onion-routing project of the U.... Read more
Malwarebytes 4.21.9.5141 - Adware remova...
Malwarebytes (was AdwareMedic) helps you get your Mac experience back. Malwarebytes scans for and removes code that degrades system performance or attacks your system. Making your Mac once again your... Read more
TinkerTool 9.5 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Paragon NTFS 15.11.839 - Provides full r...
Paragon NTFS breaks down the barriers between Windows and macOS. Paragon NTFS effectively solves the communication problems between the Mac system and NTFS. Write, edit, copy, move, delete files on... Read more
Apple Safari 17 - Apple's Web brows...
Apple Safari is Apple's web browser that comes bundled with the most recent macOS. Safari is faster and more energy efficient than other browsers, so sites are more responsive and your notebook... Read more
Firefox 118.0 - Fast, safe Web browser.
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more
ClamXAV 3.6.1 - Virus checker based on C...
ClamXAV is a popular virus checker for OS X. Time to take control ClamXAV keeps threats at bay and puts you firmly in charge of your Mac’s security. Scan a specific file or your entire hard drive.... Read more

Latest Forum Discussions

See All

‘Monster Hunter Now’ October Events Incl...
Niantic and Capcom have just announced this month’s plans for the real world hunting action RPG Monster Hunter Now (Free) for iOS and Android. If you’ve not played it yet, read my launch week review of it here. | Read more »
Listener Emails and the iPhone 15! – The...
In this week’s episode of The TouchArcade Show we finally get to a backlog of emails that have been hanging out in our inbox for, oh, about a month or so. We love getting emails as they always lead to interesting discussion about a variety of topics... | Read more »
TouchArcade Game of the Week: ‘Cypher 00...
This doesn’t happen too often, but occasionally there will be an Apple Arcade game that I adore so much I just have to pick it as the Game of the Week. Well, here we are, and Cypher 007 is one of those games. The big key point here is that Cypher... | Read more »
SwitchArcade Round-Up: ‘EA Sports FC 24’...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for September 29th, 2023. In today’s article, we’ve got a ton of news to go over. Just a lot going on today, I suppose. After that, there are quite a few new releases to look at... | Read more »
‘Storyteller’ Mobile Review – Perfect fo...
I first played Daniel Benmergui’s Storyteller (Free) through its Nintendo Switch and Steam releases. Read my original review of it here. Since then, a lot of friends who played the game enjoyed it, but thought it was overpriced given the short... | Read more »
An Interview with the Legendary Yu Suzuk...
One of the cool things about my job is that every once in a while, I get to talk to the people behind the games. It’s always a pleasure. Well, today we have a really special one for you, dear friends. Mr. Yu Suzuki of Ys Net, the force behind such... | Read more »
New ‘Marvel Snap’ Update Has Balance Adj...
As we wait for the information on the new season to drop, we shall have to content ourselves with looking at the latest update to Marvel Snap (Free). It’s just a balance update, but it makes some very big changes that combined with the arrival of... | Read more »
‘Honkai Star Rail’ Version 1.4 Update Re...
At Sony’s recently-aired presentation, HoYoverse announced the Honkai Star Rail (Free) PS5 release date. Most people speculated that the next major update would arrive alongside the PS5 release. | Read more »
‘Omniheroes’ Major Update “Tide’s Cadenc...
What secrets do the depths of the sea hold? Omniheroes is revealing the mysteries of the deep with its latest “Tide’s Cadence" update, where you can look forward to scoring a free Valkyrie and limited skin among other login rewards like the 2nd... | Read more »
Recruit yourself some run-and-gun royalt...
It is always nice to see the return of a series that has lost a bit of its global staying power, and thanks to Lilith Games' latest collaboration, Warpath will be playing host the the run-and-gun legend that is Metal Slug 3. [Read more] | Read more »

Price Scanner via MacPrices.net

Clearance M1 Max Mac Studio available today a...
Apple has clearance M1 Max Mac Studios available in their Certified Refurbished store for $270 off original MSRP. Each Mac Studio comes with Apple’s one-year warranty, and shipping is free: – Mac... Read more
Apple continues to offer 24-inch iMacs for up...
Apple has a full range of 24-inch M1 iMacs available today in their Certified Refurbished store. Models are available starting at only $1099 and range up to $260 off original MSRP. Each iMac is in... Read more
Final weekend for Apple’s 2023 Back to School...
This is the final weekend for Apple’s Back to School Promotion 2023. It remains active until Monday, October 2nd. Education customers receive a free $150 Apple Gift Card with the purchase of a new... Read more
Apple drops prices on refurbished 13-inch M2...
Apple has dropped prices on standard-configuration 13″ M2 MacBook Pros, Certified Refurbished, to as low as $1099 and ranging up to $230 off MSRP. These are the cheapest 13″ M2 MacBook Pros for sale... Read more
14-inch M2 Max MacBook Pro on sale for $300 o...
B&H Photo has the Space Gray 14″ 30-Core GPU M2 Max MacBook Pro in stock and on sale today for $2799 including free 1-2 day shipping. Their price is $300 off Apple’s MSRP, and it’s the lowest... Read more
Apple is now selling Certified Refurbished M2...
Apple has added a full line of standard-configuration M2 Max and M2 Ultra Mac Studios available in their Certified Refurbished section starting at only $1699 and ranging up to $600 off MSRP. Each Mac... Read more
New sale: 13-inch M2 MacBook Airs starting at...
B&H Photo has 13″ MacBook Airs with M2 CPUs in stock today and on sale for $200 off Apple’s MSRP with prices available starting at only $899. Free 1-2 day delivery is available to most US... Read more
Apple has all 15-inch M2 MacBook Airs in stoc...
Apple has Certified Refurbished 15″ M2 MacBook Airs in stock today starting at only $1099 and ranging up to $230 off MSRP. These are the cheapest M2-powered 15″ MacBook Airs for sale today at Apple.... Read more
In stock: Clearance M1 Ultra Mac Studios for...
Apple has clearance M1 Ultra Mac Studios available in their Certified Refurbished store for $540 off original MSRP. Each Mac Studio comes with Apple’s one-year warranty, and shipping is free: – Mac... Read more
Back on sale: Apple’s M2 Mac minis for $100 o...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $100 –... Read more

Jobs Board

Licensed Dental Hygienist - *Apple* River -...
Park Dental Apple River in Somerset, WI is seeking a compassionate, professional Dental Hygienist to join our team-oriented practice. COMPETITIVE PAY AND SIGN-ON Read more
Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Sep 30, 2023 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
*Apple* / Mac Administrator - JAMF - Amentum...
Amentum is seeking an ** Apple / Mac Administrator - JAMF** to provide support with the Apple Ecosystem to include hardware and software to join our team and Read more
Child Care Teacher - Glenda Drive/ *Apple* V...
Child Care Teacher - Glenda Drive/ Apple ValleyTeacher Share by Email Share on LinkedIn Share on Twitter Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.