Segments
Volume Number: | | 4
|
Issue Number: | | 11
|
Column Tag: | | Forth Forum
|
Code Segments & Linker
By Jörg Langowski, MacTutor Editorial Board
Code segments and a Mach 2 linker
This month Id like to report on some recent Mach2 improvements: the new 2.14 version and a linker for kernel-independent applications that has recently appeared on the GEnie Mach2 roundtable. Since many of you dont have access to that BBS, Ill document the linker here, with some review of code segment structure, and put the code on the source code disk.
Single-segment linker
We have seen several examples - DAs, XCMDs, MDEFs - of Mach2 code that runs independent of the Forth multitasking kernel. Writing such code requires that the programmer write the setup code that is usually provided by the Mach2 system. For the examples that I gave in my column, the standard glue code for making a routine callable from outside Mach2 looked similar to:
CODE prelude
LINK A6,#-Nstack
\ Nstack bytes of local Forth stack
MOVEM.L A0-A5/D0-D7,-(A7)\ save registers
MOVE.L A6,A3 \ setup local loop return stack
SUBA.L #Nlocal,A3 \ in the low Nlocal stack bytes
MOVE.L 8(A6),D0 \ pointer to parameter block
MOVE.L D0,-(A6)
RTS \ just to indicate the MACHro stops here
END-CODE MACH
CODE epilogue
MOVEM.L (A7)+,A0-A5/D0-D7\ restore registers
UNLK A6
MOVE.L (A7)+,A0 \ return address
ADD.W #4,A7 \ pop off 4 bytes of parameters
JMP (A0)
RTS
END-CODE MACH
: my-forth-code
( code to be called externally )
;
: ext.routine
prelude my-forth-code epilogue
;
After these definitions, the routine ext.routine may be called from the outside as if defined in Pascal as:
procedure ext.routine (parameter:longint);
begin
( code to be called externally )
end;
All we need in this case before calling our Forth code is to set up a local Forth stack maintained by the A6 register, save all the registers for safety, and create a loop return stack maintained by A3.
In principle, a complete application can be created this way; however, some more setup is required. A simple one-segment application consists of two CODE resources: the jump table in CODE 0 and the actual code in CODE 1. The structure of the jump table (JT) as given in IM II-60 looks like the following (in the case that the first entry in the JT corresponds to a routine in segment 1):
0: longintAbove A5 size ( 32 + length of JT )
4: longintBelow A5 size (appl. and QD globals)
8: longintLength of jump table in bytes
12:longint Offset from A5 to jump table (32 )
16:Jump table:
------ Jump table entry #1 ------
word offset of routine #1 from beginning of
segment
longintMOVE.W #1,-(A7)
( push segment # of routine on stack)
word _LoadSeg
------ following jump table entries ----
------ for routine #2...n, if necessary ------
When an application is launched, the CODE 0 resource will be loaded and the first JT entry executed. This will load the appropriate segment into memory and jump to the routine to which the first entry is pointing. Thus, a simple one-segment application would consist of a CODE 0 resource like above, with one single JT entry:
$nnnn ( entry address in CODE 1 segment )
( attention: segment starts at )
( beginning of resource + 4)
$3F3C0001( MOVE.W #1,-(A7) )
$A9F0 ( _LoadSeg )
CODE 1 would contain the actual code, written in Mach2.
What are the advantages of creating single-segment Mach2 programs? First of all, we can create very small applications. The smallest conceivable application, which does absolutely nothing but return, would comprise only 30 bytes:
CODE 0:
$00000028( always $20 + length(JT) )
$00000200( arbitrary )
$00000008( length of JT; one entry )
$00000020( always )
$0000 ( entry address in CODE 1 segment )
$3F3C0001( MOVE.W #1,-(A7) )
$A9F0 ( _LoadSeg )
CODE 1:
$0000 ( first routine is at beginning of JT )
$0001 ( one entry in this segment )
$4E75 ( RTS )
This program is enclosed on the source code disk as a curiosity. The file actually is 364 bytes long (Resource map etc.), which is still pretty small.
A second advantage is that we have complete control over the way the application sets itself up. In particular, we could pass a routine pointer to _InitDialogs to activate the Resume button of the system bomb box, or we might want to control the amount of calls to _MoreMasters.
The disadvantage of compiling applications under Mach2: Obviously we have to care about all the things that the kernel normally does for us, like basic event handling, menu and menu bar setup, screen input/output, etc. Particularly, there are quite a few Forth words that may not be used anymore; regular readers of this column should be familiar with the rules for creating kernel-independent code that Ive laid out a few times already.
The Forth words that can be used include:
! + +! ^ +> - -> 0< 0= 0> 1+ 1- 2* 2+ 2- 2/ 2DROP
2DUP 2OVER < <> = > >BODY >R ?DUP @ ABS AND ASCII C! C@
DROP DUP EXIT I I J LEAVE L_EXT NEGATE NOT OR OVER PAD
PICK R> R@ SWAP U< W! W@ XOR { (its OK to use local variables)
The following control and branching structures may also be used:
IF ELSE THEN BEGIN WHILE REPEAT UNTIL AGAIN CASE ENDCASE OF
ENDOF DO LOOP +LOOP
Assembler, of course, may be used freely.
Waymen Askey, of Palo Alto Shipping, has created a linker utility that compiles single-segment application using the strategy given above. We reprint his program in listing 1 with his permission. This linker operates on a program which has the following structure:
PROGRAM programname;
( definitions not to be included in the final application
such as constants, compiling words, etc. )
VAR
( global variable declarations which will be offset from A5 )
PROCEDURES
( Forth words called by the top level word )
MAIN
( top level word which is called on startup. )
( This word should call the setup procedures )
( MachSetUp and MacintoshSetUp )
( which are provided with the Linker utility. )
END
The linker computes the below A5 size from the variables defined after the VAR statement, adding space for the Forth stacks, Quickdraw globals and various other things. The offset of the MAIN entry point into the code segment is calculated and the jump table set up. MakeJumpTable and MakeMain are the words that create the jump table and code segment 1.
MachSetUp initializes the registers for Mach2 usage. Floating point (D7), parameter (A6) and return (A3) stacks are created above the current stack base in the application globals area. The A7 stack, starting at CurStackBase, remains unaffected. The application globals area is then cleared.
MacintoshSetUp does the standard initialization calls to _MoreMasters, _InitGraf, _InitFonts, _InitWindows, _InitMenus, _TEInit, _InitDialogs, _FlushEvents and _InitCursor.
After these initialization calls, the main program may be entered. An example of a short program which creates a window and beeps is given in the listing. This program, too, is only 858 bytes long (!!!).
Mach 2.14 upgrade
For those of you who havent yet upgraded to Mach2.14, Ill briefly review the latest changes.
1. CASE optimization: redundant instruction sequences of the type
MOVE.L D0,-(A6)
MOVE.L (A6)+,D0
are no longer generated.
2. Local variable handling: the new release offers access to the local variable compiler with the words LALLOT and LP@. For example, a word might define a local 16-byte buffer in the following way:
: EXAMPLE { | [ 12 LALLOT ] myBuffer -- }
CR . Please enter your name
^ myBuffer 16 EXPECT
CR . Hello ^ myBuffer SPAN @ TYPE ;
The local variable compiler can be further enhanced through local variable compiling words; examples on how to do this are given on the 2.14 release disk.
3. Disassembler: References to USER and global variables are now given with their Forth names. Disassembly speed has been greatly improved, which is particularly evident when executing IL on a Mac Plus or SE. 68881 opcodes are now supported, however, 68020-specific instructions not yet.
4. New words: ASCII now takes up to four characters, for easy definition of resource types. 4+, 4-, 4*, 4/ have been added. a n SHIFT will shift a 32-bit word a by n bits.
5. The trap list has been updated.
Feedback dept.
Dear Jörg,
I saw a discussion of accented character problems in the July MacTutor and thought I would throw in a few digressions on that matter.
First, you and your readers might be interested to know that Apple has removed the scaron and zcaron characters from the new NTX PROMs against the recommendation of Adobe. These characters were not accessible from the keyboard, because they are uncoded, meaning that no ASCII value is assigned. The only way to access them is via Postscript character names. The good news is that several new characters were added making the NTX almost compliant with the ISO 8859 character set that Adobe routinely supplies with all new fonts. (Apple removed the ´y and ´Y, too).
For those of you who want to see the unencoded characters, you can get at them with the following Postscript code and a download utility, if you have one of the new unprotected Adobe fonts or a late model Laserwriter Plus with v.3 PROMs:
/Garamond-Light findfont dup length dict
/newdict exch def
{1 index /FID
ne{ newdict 3 1 roll put }{ pop pop }ifelse
} forall
/Encoding 256 array def
Encoding 0 /Garamond-Light findfont
/Encoding get 0 256 getinterval putinterval
Encoding 127 /DEL put
Encoding 129 /lslash put
Encoding 130 /Lslash put
Encoding 131 /eth put
Encoding 132 /Eth put
Encoding 133 /thorn put
Encoding 134 /Thorn put
Encoding 135 /onehalf put
Encoding 136 /onequarter put
Encoding 137 /threequarters put
Encoding 138 /brokenbar put
Encoding 139 /onesuperior put
Encoding 140 /twosuperior put
Encoding 141 /threesuperior put
Encoding 142 /scaron put
Encoding 143 /Scaron put
Encoding 144 /zcaron put
Encoding 145 /Zcaron put
Encoding 146 /yacute put
Encoding 147 /Yacute put
newdict /Encoding Encoding put
/IsoGaramond newdict definefont pop
/IsoGaramond findfont 18 scalefont setfont
75 250 moveto
(ÄÅÇÉÑÖÜáàâäãåçéèêëíì Garamond) show
showpage
Unfortunately there is no way, at present, to get at these with templates in Fontographer, so you have to make your own composites, if you want to add these characters to PostScript fonts.
Best regards, Tim Ryan
SourceNet
P.O.Box 6767
Santa Barbara, CA 93160
PS: The standalone caron () is frequently found as ASCII character 255, one of the last four untypeable characters. It can be accessed using QUED, ... and MS Word if you enter the character using its ASCII value.
By the way, I did get a Greek + Hebrew System from Apple-France via persistent phone calls.
Ive enclosed the first draft of an article that will appear in my forthcoming book The Macintosh Book of Fonts. If youre interested in reprinting the final draft when its available, let me know.
Tim
Thanks, Tim, for that interesting letter (Ive enclosed your Postscript code on the source code disk). Now if all these characters were defined somewhere in the standard fonts, wouldnt that be nice? I always wondered why there were so many empty places in the font definition tables, seems like a waste of space to me
In the next issue well introduce - with other contributions from this side of the Atlantic - a very nice and powerful utility for changing keyboard definitions, so at least that problem can be overcome. Till then.
Listing 1: Mach 2.14 single-segment linker
\ © Waymen Askey c/o Palo Alto Shipping
\ Reprinted with permission. -- JL
\ Guidelines for use of the single-segment linker.
\ This utility is NOT meant to replace the
\ standard Mach TURNKEY process. Its use (at present)
\ is limited to creating small (one-segment, less than
\ 32K) programs which do NOT require the multi-tasking,
\ I/O, and auto event-handling support which the normal
\ turnkey process supplies.
\ Also, since this utility is being supplied free to
\ Mach users, Palo Alto Shipping will NOT assume
\ responsibility for support of the utility, nor
\ will we be held responsible for any errors (bugs)
\ which it may produce.
\ It should, however, point the way for other
\ compiler enhancements by users. The bottom line is
\ that Mach can be used to create any type (and size) of
\ Macintosh application, DA, driver, INIT, etc.
\ Waymen @ PASC
(
\ The following words MAY be used freely within the
\ stand-alone, linked application.
\
! + +! ^ +> - -> 0< 0= 0> 1+ 1- 2* 2+ 2-
2/ 2DROP 2DUP 2OVER < <> = > >BODY >R ?DUP @
ABS AND ASCII C! C@ DROP DUP EXIT I I J LEAVE
L_EXT NEGATE NOT OR OVER PAD PICK R> R@ SWAP
U< W! W@ XOR { (its OK to use local variables)
\ The following control and branching structures MAY
\ also be used.
\
IF ELSE THEN BEGIN WHILE REPEAT UNTIL AGAIN
CASE ENDCASE OF ENDOF DO LOOP +LOOP
\ All assembler words may be used.
\ The following compilation words MAY be used to
\ create your application (but dont attempt to
\ compile them, they cant be executed during the
\ run-time of your finished application).
\
: ; VARIABLE CONSTANT USER CREATE DOES>
;CODE CODE END-CODE ALLOT VALLOT , W, C,
HERE COMPILE [COMPILE] IMMEDIATE SMUDGE LITERAL
LAST MACH RECURSIVE [ ]
\ Note, global variables may ONLY be used if you
\ declare a VAR block.
\ [] should be used with caution.
\ Dont use it on words defined outside of your
\ program block.
\ If you wish to use EXECUTE, it may be redefined as
\
CODE EXECUTE ( a -- )
MOVE.L (A6)+,A0
JSR (A0)
RTS
END-CODE MACH
\ ONLY the following MAC vocabulary words MAY be used.
\ Remember to use (CALL) instead of CALL.
\
(CALL) All CONSTANTS used for the creation of user
interface structures (CLOSEBOX, VISIBLE, etc.)
If you define a VAR block, EVENT-RECORD (and all
other system global variables) may be
used as a storage area only -- events (and other
information) will NOT automatically be posted
there).
\ These utilities may also be used.
TRAP# TRAPLIST TRAPNAME
\ ====================================
\ ========== Cant Use These ============
\ Words which may NOT be used!!! This is NOT a
\ complete list, just some of the more common words.
\ You must NOT compile any word which is referenced
\ through Machs own jump table (words which compile
\ a JSR d(A5) instruction.
\
CALL GLOBAL TERMINAL TURNKEY NEW.WINDOW ADD (etc.)
BUILD TASK TASK-> BYE EVENT-TABLE PAUSE
All I/O such as KEY EXPECT EMIT . TYPE (etc.)
<# # #S #> DEPTH 2SWAP CMOVE * / /MOD */MOD */
(if you are using a Mac II exclusively, you may substitute the new 32-bit
math routines which came with the last Mach upgrades.) NO SANE words,
NO TALKing words,
NO I/O words. None of the high-level FILE words in
the MAC vocabulary. NO words which reference the
multi-tasking kernel, NO I/O task words (i.e. events
MUST be handled explicitly, you must create your own
event-loop).
A space for USER variables is reserved for your
program, but all of them (except for the TIB value,
S0, and RETURN_STK) are initialized to zero.
Consider USER variables as just another global storage
area. Words like BASE and (ABORT) may be used; however, they will have
NO effect on your program unless you specifically design the words to
use them.
)
\ ----------------------------------------------------------------------
\ A simple, one-segment linker which may
\ be used (with restrictions) to create
\ small applications in high-level Forth.
\ Also allows you to create very small assembly
\ language programs (mininum size about 40 bytes)
\ With slight modifications to the linker and the
\ proper SetUp word, could also be used
\ to create DAs, FKEYs, XCMDs, and INITs.
\ -- Waymen
\ @ Palo Alto Shipping Company
ONLY MAC ALSO FORTH DEFINITIONS
DECIMAL
$908 CONSTANT CurStackBase
$434F4445 CONSTANT CODE
$4150504C CONSTANT APPL
$3F3F3F3F CONSTANT ????
%1 CONSTANT MainErr
%10CONSTANT EndErr
%100 CONSTANT ProcErr
$12344320 CONSTANT GoodStart
$1234432F CONSTANT StartFlag
$12344328 CONSTANT GoodEnd
\ The default stack and USER variable sizes
\ to be used in building the jump table.
\ Ive made the USER size larger to allow
\ for a 256 byte PAD
572CONSTANT USERSize ( USER variables)
74 CONSTANT TIBSize( plus STATUS)
600CONSTANT ParameterSize ( A6 & A3 stacks)
200CONSTANT FPSize ( FP stack)
206CONSTANT GrafSize ( QD globals)
$20CONSTANT BL
-1 CONSTANT TRUE
0CONSTANT FALSE
VARIABLE VarEntry
VARIABLE SegmentEntry
VARIABLE MainEntry
VARIABLE SegmentEnd
VARIABLE ProgramFlag
VARIABLE ProgramName 28 VALLOT
VARIABLE JumpTable 20 VALLOT
: -Leading { addr cnt | whiteSpace -- addr cnt }
\ Adjusts addr and cnt to strip leading spaces from a string.
\ Addr is the starting character address,
\ cnt is the original length.
0 -> whiteSpace
BEGIN
addr whiteSpace + C@ BL =
whiteSpace cnt < AND
WHILE
1 +> whiteSpace
REPEAT
addr whiteSpace +
cnt whiteSpace - ;
: RemoveSpaces { addr | cnt -- }
\ Given counted string at addr, remove trailing and leading
\ spaces and repack string.
addr COUNT -TRAILING addr C! DROP
addr COUNT -Leading -> cnt
( addr) addr 1+ cnt CMOVE cnt addr C! ;
: Scan { addr num delimiter | cnt char -- flag }
\ Scans input stream, placing characters into string at addr until
\ num characters are received or delimiter is found.
\ If delimiter is NOT found prior to num, return FALSE
\ else return TRUE.
num 0>
IF
0 -> cnt
BEGIN
0 WORD 1+ C@ -> char
char delimiter = NOT
num cnt > AND
WHILE
1 +> cnt char addr cnt + C!
REPEAT
cnt addr C! char delimiter =
ELSE
0 addr ! FALSE
THEN ;
: PROGRAM { | cnt scanFlag -- }
\ Gets program name and inits linker variables.
ProgramName 31 ASCII ; Scan -> ScanFlag
ProgramName RemoveSpaces
ProgramName C@ 0= scanFlag 0= OR
ABORT Must use ; to delimit program name!
0 MainEntry ! 0 SegmentEnd ! 0 VarEntry !
StartFlag ProgramFlag ! ;
: ClearErr ( errNum -- )
ProgramFlag @ XOR ProgramFlag ! ;
: VAR ( -- )
\ Ensure that current VP offset from A5 is
\ even, then save it.
VP @ 1 AND
IF
1 VALLOT
THEN VP @ ABS VarEntry ! ;
: Globals? ( -- )
\ Checks to see if a VAR statement was made.
VarEntry @ 0=
IF
10 CALL SysBeep
CR . WARNING: No global variables were declared!
THEN ;
: ?HERE ( -- a )
\ Ensures that HERE pointer is even, then
\ returns HERE.
HERE 1 AND
IF
1 ALLOT
THEN HERE ;
: PROCEDURES ( -- )
ProcErr ClearErr
?HERE SegmentEntry ! 4 ALLOT ;
: MAIN ( -- )
MainErr ClearErr
?HERE MainEntry ! ;
: END ( -- )
EndErr ClearErr
?HERE SegmentEnd ! ;
: ZeroFlags ( -- )
0 ProgramFlag ! 0 VarEntry !
0 MainEntry ! 0 SegmentEnd ! ;
: BelowA5 ( -- n )
\ Calculates the Below A5 space for
\ the jump table.
VarEntry @ DUP 0=
IF
DROP GrafSize
THEN
USERSize + TIBSize +
ParameterSize + FPSize + ;
: MakeJumpTable ( -- handle f )
\ handle is to a generic, one-entry jump table.
$00000028JumpTable! \ Above A5 size
BelowA5JumpTable 4 +!
\ Global variable space
$00000008JumpTable 8 + !
\ Jump table length
$00000020JumpTable 12 + !
\ Jump table A5 offset
\ Calculate segment entry point
MainEntry @ SegmentEntry @ 4 + -
( entry) JumpTable 16 + W!
$3F3C0001JumpTable 18 + !
\ MOVE.W #1,-(A7)
$0001A9F0JumpTable 22 + W!
\ _LoadSeg
JumpTable 24 CALL PtrToHand ;
: MakeMain ( -- handle f )
\ Offset to first jump-table entry
0 SegmentEntry @ W!
\ Only one jump-table entry
1 SegmentEntry @ 2+ W!
SegmentEntry @ ( start of segment )
SegmentEnd @ SegmentEntry @ - ( length of segment )
CALL PtrToHand ;
: Link { refNum | JumpHandle MainHandle -- }
\ Creates, then adds CODE segments 0 and 1
\ to file refNum
refNum
IF
MakeJumpTable
IF
ZeroFlags
refNum CALL CloseResFile
CR . MakeJumpTable error! ABORT
THEN
-> JumpHandle
JumpHandle CODE 0 Jump Table
CALL AddResource
CALL ResError
IF
ZeroFlags
refNum CALL CloseResFile
JumpHandle CALL DisposHandle DROP
CR . Link (0): AddResource error! ABORT
THEN
MakeMain
IF
ZeroFlags
refNum CALL CloseResFile
JumpHandle CALL DisposHandle DROP
CR . MakeMain error! ABORT
THEN
-> MainHandle
MainHandle CODE 1 Main
CALL AddResource
CALL ResError
IF
ZeroFlags
refNum CALL CloseResFile
JumpHandle CALL DisposHandle DROP
MainHandle CALL DisposHandle DROP
CR . Link (1): AddResource error! ABORT
THEN
THEN ;
: CreateApplFile { | refNum -- refNum or zero }
\ Remember to delete previously made files!
0 -> refNum
???? APPL ProgramName 0 CreateFile
DISK 4 + W@ 0=
IF
ProgramName CALL CreateResFile
ProgramName CALL OpenResFile
\ This logic returns either a valid refNum or zero,
\ as OpenResFile returns a -1 if it cant open the file.
DUP -1 = NOT AND -> refNum
THEN refNum ;
: ?Error { errFlag -- }
\ Checks for proper program headings
errFlag GoodEnd XOR
IF
CR . Missing:
errFlag $FFFFFFF0 AND GoodStart =
IF
errFlag %111 AND
CASE
MainErr OF. MAIN ENDOF
EndErr OF . END ENDOF
ProcErr OF. PROCEDURES
ENDOF
( else)
. MAIN, END and/or PROCEDURES
ENDCASE
ELSE
. PROGRAM
THEN
. Statement(s)! ZeroFlags ABORT
THEN ;
: MakeApplication { | refNum -- }
ProgramFlag @ ?Error
CreateApplFile -> refNum
refNum
IF
refNum Link Globals?
refNum CALL CLoseResFile
ZeroFlags
ELSE
CR . CreateFile error # DISK 4 + W@ L_EXT .
ZeroFlags ABORT
THEN ;
\ ============================================
\ All of the code previous to here will NOT be included
\ in the linked program. Thus, the above utilities may be
\ workspaced, used and/or enhanced at will.
\ MachSetUp and MacintoshSetUp should appear as the
\ first statements in your MAIN or LAUNCH word.
CODE MachSetUp ( -- )
\ Sets up stacks for high-level Forth.
\ Not needed if you work only in assembly language.
MOVE.L CurStackBase,D0
MOVE.L D0,D1
ADD.L #FPSize,D0
MOVE.L D0,D7 \ FP stack
MOVEA.L D0,A3 \ loop stack
ADD.L #ParameterSize,D0
MOVEA.L D0,A6 \ parameter stack
ADD.L #TIBSize,D0
MOVEA.L D0,A4 \ USER variables
MOVEA.L D1,A0
MOVE.L A5,D0
SUB.L D1,D0\ below A5 bytes to clear
DIVU.W #16,D0
MOVE.W D0,D2 \ blocks to clear
SWAP.W D0\ bytes to clear
\ Init all globals, USER
\ vars and stack area to zeros
BRA.S @20
@10CLR.L (A0)+
CLR.L (A0)+
CLR.L (A0)+
CLR.L (A0)+
@20DBF D2,@10
BRA.S @40
@30CLR.B (A0)+
@40DBF D0,@30
\ Although it cant really be used,
\ here I set-up the (TIB) USER var
MOVE.L A6,24(A4)
MOVE.L A6,4(A4) \ S0 USER var
MOVE.L A3,12(A4)\ RETURN_STK USER var
RTS
END-CODE MACH
CODE MacintoshSetUp ( -- )
_MoreMasters
_MoreMasters
PEA -4(A5)
_InitGraf
_InitFonts
_InitWindows
_InitMenus
_TEInit
CLR.L -(A7)
_InitDialogs
MOVE.L #$0000FFFF,D0
_FlushEvents
_InitCursor
RTS
END-CODE MACH
\ =============================================
\ ============= An Example =====================
\ From this point on (between the PROCEDURES and END
\ statement) is where you place your application code.
PROGRAM My Example;
\ The required and beginning statement in your program.
\ The application will be titled as whatever appears
\ between the PROGRAM statement and the delimiting
\ colon (up to 31 characters).
\ Words defined here
\ will NOT be included in your application
\ The redefintion of CALL is just a reminder.
: CALL
CR . Dont use CALL here, use (CALL) instead.
ABORT ;
0 CONSTANT NIL
-1 CONSTANT InFront
10 CONSTANT TenTicks
30 CONSTANT HalfSecond
VAR
\ All global variables used within the program MUST follow
\ the VAR statement. If you dont include the VAR
\ statement, a warning will be given during program link.
\ If you dont use global variables (or Mach system
\ globals), you may ignore the warning.
VARIABLE DelayTicks
VARIABLE BoundsRect 4 VALLOT
PROCEDURES
\ All subroutines must appear between PROCEDURES and
\ MAIN. Only that code appearing between the
\ PROCEDURES and END statements will appear in your
\ finished application.
: SetDelay ( n -- ) DelayTicks ! ;
: Beeper { beepTime -- }
HalfSecond TenTicks
DO
beepTime (CALL) SysBeep
I SetDelay
DelayTicks @ (CALL) Delay DROP
TenTicks +LOOP ;
: MakeWindow ( -- a | returns a window pointer)
BoundsRect 20 72 492 322 (CALL) SetRect
NIL BoundsRect Beeper Window VISIBLE
NOGROW InFront NOCLOSEBOX NIL
(CALL) NewWindow ;
: ProgramLoop { | windowPointer -- }
NIL -> windowPointer
MakeWindow -> windowPointer
10 Beeper
windowPointer 0= NOT
IF
10 (CALL) SysBeep
windowPointer BYE (CALL) SetWTitle
60 (CALL) Delay DROP
windowPointer (CALL) DisposWindow
THEN ;
MAIN
\ The programs entry point must appear immediately
\ after MAIN
: LAUNCH ( -- )
\ Dont attempt to use local variables in the
\ LAUNCH word. The stacks arent created until
\ after MachSetUp.
MachSetUp
MacintoshSetUp
ProgramLoop ;
END ( of program My Example)
\ This statement does error checking
\ and creates the application.
MakeApplication
CR .( An application called My Example has been created.)