-not a Macintosh disk-FbGYa4GYa;0a:a4BH@%qΞ ,ןspv(  )MW 0Pp 0Pp "0$P&p(*,.204P6p8:<>@JLNPR0Xbphjlnpr0tPvpxz~0PpЎ  0 P p О  0 P p  Ю  0 P p о  0 P p  0 P p  0P0Pp1Qq  1Qq "1$Q&q(*,0214Q6q8:<>@B1DQFqHJLNPR1TQVqXZ\^`b1dQfqhjlnpr1tPFNDRERIK@swvJDeskTopTEXTMACAffܚ iocorev2.textTEXTMACAp0DHf۟f* kbdv1.textTEXTMACABf\flink/grafsys.textTEXTMACACkff:link/miscsys.textTEXTMACApDTf_flink/opsys.textTEXTMACAEf֟f<link/toolsys.textTEXTMACA(Fefhfload/mwdisk.textTEXTMACA(p G5ff mac/comp.textTEXTMACA( HVf fmac/download.textTEXTMACA< I( ,f|f޲macromsound.textTEXTMACA<p Tfޟf& make/all.textTEXTMACA< UfBNDLJMACAVICN#bFREF\$$$%3$ %@jI$$$%8pP$%<$0%$$%%  $% ,u$$MW ; File: IOCore.Text ; MACWORKS copy ;_______________________________________________________________________ ; ; Macintosh Operating System I/O Core Routines ; ; This module contains the core routines that form the kernel of the ; I/O system. These include OPEN, CLOSE, READ, CONTROL, STATUS, KILLIO, ; DRVRINSTALL, and DRVRREMOVE. They receive control from the core routine ; dispatcher and pass control to the file system or I/O drivers. ; ; Written by Andy Hertzfeld 09-Jul-81 ; ; Modification History: ; ; 17 Jul 81 AJH Added asynchronous I/O stuff ; 15 Sep 81 AJH Changed structure of FETCH/NEXTFETCH ; 18 sep 81 bmw fetch bugfix ; 29 sep 81 bmw make goprime clear d0 ; 1 oct 81 bmw make control, status give user's a0 to driver ; 2 oct 81 bmw close bugfix ; 5 oct 81 bmw add deferred completion routines ; 11 oct 81 bmw separate synchronous from asnychronous symbolic offsets, ; doasynch, dowasynch bugfix; control, status bugfix, ; consolidated dispatching to driver routines ; 14 Oct 81 AJH changed fetch,stash,read,write,aread,awrite to ; for the new way of doing things; added IODONE ; 15 oct 81 bmw random bugfixes ; 20 oct 81 bmw fetch advances pointer after getting byte ; 02 Nov 81 AJH moved UNITABLE to heap ; 15 Nov 81 AJH made READ and WRITE look at async bit ; 16 Nov 81 AJH integrated file system ; 19 Aug 82 LAK removed ring buffer stuff and PEEK ; modified Fetch and STASH to be simpler ; changed Status, Control to be asynchronous ; changed to support new Unit Table and Device Control ; Entry formats ; position parameters for read and write calls are now ; evaluated when the prime routine is called (instead ; of when the command is received) ; made string-compare case insensitive (pretty much) ; 05 Sep 82 LAK added InstallDriver, RemoveDriver; Included Object ; Manager; debugged this and previous changes ; 15 Nov 82 LAK now calls completion routines at IODone ; 02 Dec 82 LAK changed IO positioning to be same as file systems; ; removed references to FileControl, FileStatus ; (needs corresponding file system changes) ; 16 Dec 82 LAK when calling completion routine, param blk ptr is ; now passed in A0, D0 result code is tested. ; changed IOResult to be positive when "in progress", ; negative or zero when done. ; 17 Dec 82 LAK integrated with Andy's IOCore (resources!) ; 03 Jan 83 AJH changed over to new resource manager ; 12 Jan 83 LAK modified to use OS string compare proc ; 21 Jan 83 LAK modified for new I/O parameter blocks ; 22 Feb 83 LAK moved CmpString from here to SysUtil; made ; all routines here preserve regs as per Pascal ; regsave conventions. ; 10 Mar 83 LAK fixed bug in close (wasn't saving registers it restored). ; 26 Apr 83 AJH optimized IODone path for ROM drivers ; intercepted status call 1 to return DCE handle ; implemented KillIO ; 27 Apr 83 LAK modified changes. added fix for GoDriver out-of-mem error. ; 25 May 83 LAK changed interface to new string compare. ; 15 Jun 83 AJH made it use symbolic deep shit alert ; 16 Jun 83 LAK changed Open and DrvrInstall to use UnitNtryCnt lomem ; var instead of UnitEntries equate. ; 17 Jun 83 AJH made it compact heap before locking down opened driver ; 23 Jun 83 AJH added "NoQueue" bit to IO traps; made open install drivers ; simplified DrvrInstall ; 24 Jun 83 AJH made "noQueue" control calls enter at DoImedIO instead of GoDriver ; 26 Jun 83 AJH made it save trap in low memory for drivers use ; 28 Jun 83 AJH fixed bug in string compare for ROM-based driver open ; 29 Jun 83 AJH made ram-based driver ID mapping NOT instead of NEG ; 15 Jul 83 LAK changed to store trap in IOCmd instead of code. ; 08 Aug 83 LAK made changes (mostly for code savings) as per 17 July ; walkthrough. ; 12 Aug 83 LAK Open search thru ROM-based drivers only searches thru unit ; 9 now (last ROM-based driver). ; 15 Aug 83 LAK Added FSIODneTbl for initialization. ; 17 Aug 83 LAK Removed set up of lomem var CurIOTrap; IODone now is smart ; enough to look at IOTrap and do the RTS itself. ; 23 Aug 83 LAK RWCS now checks for driver open before it will dispatch. ; 26 Aug 83 LAK Changed to save only A1 over completion routine call to ; speed up async IODone's: completion routine is responsible ; for preserving A4-A6 and D4-D7 . . . the other registers ; are saved at the interrupt level. ; 05 Sep 83 LAK Changed IODone to run completion routines at level it is ; called at. ; 10 Sep 83 AJH/LAK Fixed IODone bug -- immediately called drivers weren't unlocked ; replaced recoverHandle calls with in-line code ; 5 Feb 85 KWK Gross hack in IORWCS to fix bug in Finder/other apps when ; sony disk driver is incorrectly assumed. ; 11 Feb 85 KWK Fix in IOCore (Open), after BSR GoDriver, check for errors. ; 19 Mar 85 KWK Fix in KillIO, check for driver opened and enabled for control call. ; ;_______________________________________________________________________ .NOLIST .INCLUDE Tlasm-SysMacs.Text ; macros for OS calls .INCLUDE Tlasm-SysEqu.Text ; general system equates .INCLUDE Tlasm-SysErr.Text .INCLUDE Tlasm-ToolMacs.Text ; for resources .INCLUDE Tlasm-ToolEqu.Text ; ibid . . . .INCLUDE Tlasm-ResEqu.Text .LIST .PROC IOCore,0 .DEF Read,Write .DEF Control,Status,Open,Close,KillIO .DEF Fetch,Stash,IODone,FSIODneTbl .DEF DrvrInstall,DrvrRemove .REF Enqueue,Dequeue,InitQueue .REF FileRead,FileWrite .REF FileOpen,FileClose FSIODneTbl .WORD Fetch-FSIODneTbl .WORD Stash-FSIODneTbl .WORD IODone-FSIODneTbl ;______________________________________________________________________ ; ; Routine: Fetch ; Arguments: A1 (input) -- pointer to device control entry ; D0 (output) -- requested byte- bit 15 = 1 if last byte ; all other registers are preserved ; Function: Used to fetch the next byte from user buffer for ; output I/O requests. The last character is returned with ; bit 15 set; the driver should output the last byte ; and then jump to IODone. ;______________________________________________________________________ Fetch BCLR #15,D0 ; set Fetch flag FetchStash MOVEM.L D1/A2-A3,-(SP) ; save relevant registers MOVE.L DCtlQHead(A1),D1 ; get pointer to request BEQ.S IOCoreErr ; trap if there is no request MOVE.L D1,A3 MOVE.L IONumDone(A3),D1 ; get buffer pointer MOVE.L IOBuffer(A3),A2 ; get pointer to buffer BCLR #15,D0 ; fetch or stash? BNE.S StashOne ; branch for Stash MOVE.B 0(A2,D1.L),D0 ; fetch it! BRA.S FinishFS StashOne MOVE.B D0,0(A2,D1.L) ; stash it! FinishFS ADDQ.L #1,D1 ; compute new count MOVE.L D1,IONumDone(A3) ; update the count CMP.L IOByteCount(A3),D1 ; if this doesn't finish the request BLT.S FSDone ; then continue on BSET #15,D0 ; hi bit on indicates no more FSDone TST.W D0 ; set condition codes MOVEM.L (SP)+,D1/A2-A3 RTS IOCoreErr MOVEQ #DSIOCoreErr,D0 ; deep shit IOCore error _SysError ; invoke deep shit ;______________________________________________________________________ ; ; Routine: Stash ; Arguments: A1 (input) -- pointer to device control entry ; D0 (input) -- byte to stash ; D0 (output) -- bit 15 = 1 if I/O request finished ; all other registers are preserved ; Function: Used to store the next input byte into user buffer (for ; input I/O requests). The driver should jump to ; IODone after the last byte. ;______________________________________________________________________ Stash BSET #15,D0 ; set Stash flag BRA.S FetchStash ; go share code with Fetch ;______________________________________________________________________ ; ; Routine: IODone ; Arguments: A1 (input) -- pointer to device control entry ; D0 (input) -- I/O result code ; Function: IODone is a utility routine that is JMPed to by drivers when ; they have processed all the info in an IO Control Block (for ; read, write, control, and status calls). ; Condition codes are preserved (why?). ; ; Modification History: ; 16 Dec 82 LAK When calling completion routine, now passes A0=param blk ptr, ; D0=result code (CCR set on this) ;______________________________________________________________________ IOOK MOVEQ #0,D0 ; no error IODone MOVE.L DCtlQHead(A1),A0 ; A0 -> current request BTST #NoQueueBit-8,IOTrap(A0) ; no queue bit set? BNE.S DeActImed ; if so, unlock and return MOVE SR,-(SP) ; save interrupt state MOVE SR,D1 ; remember SR state ORI #$0300,SR BSR.S DeActDrvr ; deactivate driver ; delete the current request from the queue and post any completion routines BSR.S PostCompletion ; dequeue and post the completion routine TST.L DCtlQHead(A1) ; any more pending requests? BNE.S ToGoIO ; if so, go relaunch the driver RTE ; otherwise, re-enable interrupts and return ; routine to mark a driver inactive and then ; unlock the driver and its DCE if we're allowed to DeActDrvr BCLR #DrvrActive,DCtlFlags+1(A1) ; mark as inactive DeActImed BTST #DNeedLock,DCtlFlags(A1) ;does it have to stay locked? BNE.S IORTS7 ; exit if so (note that all rom-based ; drivers have this bit set . . . MOVE.L DCtlDriver(A1),A0 ; get handle to the driver BCLR #Lock,(A0) ; unlock it MOVE.L SysZone,A0 ; get the system zone base ADD.L -4(A1),A0 ; recover handle BCLR #Lock,(A0) ; unlock it IORTS7 RTS ; PostCompletion dequeues the current request, posts the result code as specified ; by A0 and runs the completion routine if neccessary. It should be called ; with interrupts disabled. D0 contains the error code. PostCompletion MOVE.L DCtlQHead(A1),A0 ; A0 -> current request CMP.W #IOQType,QType(A0) ; it better be an I/O queue element BNE.S IOCoreErr ; or else die MOVE.L QLink(A0),DCtlQHead(A1) ; get next request, if any BNE.S @0 ; branch if queue not empty CLR.L DCtlQTail(A1) ; clear tail ptr, too @0 MOVE D1,SR ; restore interrupt state except for KillIO MOVE.W D0,IOResult(A0) ; post error code BLE.S @1 ; make sure it's negative or zero NEG.W IOResult(A0) @1 MOVE.L IOCompletion(A0),D1 ; is there a completion routine? BEQ.S @2 ; skip if there's not MOVE.L A1,-(SP) ; save A1 over call MOVE.L D1,A1 ; get the completion routine address TST.W D0 ; test result code JSR (A1) ; call it MOVE.L (SP)+,A1 ; restore A1 @2 RTS ;______________________________________________________________________ ; ; Routine: DoAsyncIO ; Arguments: A0 (input) -- pointer to I/O command parameter block ; A1 (input) -- pointer to Device Control Entry for driver ; D0 (output) -- error code ; pascal regsave conventions are observed ; Function: This core routine allows asynchronous calls to drivers. ; It does this by building an I/O queue element (in the ; caller's memory space), queueing it up and calling ; the driver. ; ;______________________________________________________________________ DoAsyncIO MOVE.L A1,D1 ; save device control entry pointer LEA DCtlQueue(A1),A1 ; get pointer to driver's queue JSR Enqueue ; queue up the request MOVE.L D1,A1 ; restore A1 MOVE SR,-(SP) ; only allow level 3 ORI #$0300,SR ; interrupts for debugging ToGoIO BSET #DrvrActive,DCtlFlags+1(A1) ; is it active? BEQ.S GoIO ; if not, just call it ; The driver is busy so we're done for now. We'll get to it when the driver ; calls the IODone routine. MOVEQ #0,D0 ; no errors RTE ; re-enable interrupts and return ; The driver was inactive so set things up and kick it off GoIO MOVE (SP)+,SR ; restore interrupt state MOVE.L DCtlQHead(A1),A0 ; get pointer to request DoImedIO MOVEQ #DrvrCtl,D1 ; control routine offset within driver CMP.B #ACtlCmd,IOTrap+1(A0) ; is it control, tho? BEQ.S GoDriver ; do it, if so MOVEQ #DrvrStatus,D1 ; status routine offset within driver CMP.B #AStsCmd,IOTrap+1(A0) ; is it status, tho? BEQ.S GoDriver ; it must be read or write . . . GoPrime TST.L IOByteCount(A0) ; if nothing to do, don't bother BEQ.S IOOK ; and just say its done (IO drivers ; usually assume a non-zero count) MOVE.W IOPosMode(A0),D0 ; get positioning mode MOVE.L IOPosOffset(A0),D1 ; get position offset ROXR.B #2,D0 ; get pos bits into carry, sign BPL.S GoPrime1 ; 0,2 do nothing BCC.S @1 ; 1 is absolute (rel to beginning) ADD.L DCtlPosition(A1),D1 ; mode 3: relative to current position @1 MOVE.L D1,DCtlPosition(A1) ; mode 1: relative to beginning ; posmode0 and 2 are no-ops GoPrime1 MOVEQ #DrvrPrime,D1 ; we want to call the prime routine ; general method to call a driver's routine...enter with A1 pointing to the ; Device Control Entry and D1 containing the offset to the driver's ; routine table (as a word)...calls the driver's routine with D0 clear ; and A0 and A1 containing what they contained on entry... GoDriver MOVE.L DCtlDriver(A1),D0 ; if the unit doesn't have a driver, BNE.S @0 ; then complain ; If the entry in the unit table is nil, the requested driver doesn't exist. ; Flag the error and return to user. MOVEQ #UnitEmptyErr,D0 MOVE.W D0,IOResult(A0) ; post the error RTS @0 MOVEM.L D3-D7/A1-A6,-(SP) ; save regs over driver call MOVE.L DCtlDriver(A1),A2 ; get the handle or ptr BTST #DRAMBased,DCtlFlags+1(A1) ; ptr or handle? BEQ.S @2 ; if ROMBased, we have it MOVE.L SysZone,A3 ; get the system zone base ADD.L -4(A1),A3 ; recover handle BSET #Lock,(A3) ; lock down the DCE TST.L (A2) ; driver been purged? BNE.S @1 ; br if not MOVE.L A2,-(SP) ; push driver handle _LoadResource ; load the driver (preserves A0 A1 D1) MOVE.W ResErr,D0 ; did we get it? BNE.S GoError ; if not, we're in trouble @1 BSET #Lock,(A2) ; lock it down before calling MOVE.L (A2),-(SP) ; push ptr to driver CLR.B (SP) ; make sure high byte is clear MOVE.L (SP)+,A2 ; recover driver base address @2 MOVE.W 0(A2,D1.W),D1 ; to the desired routine MOVEQ #0,D0 ; assume no errors JSR 0(A2,D1.W) ; call it with A0=param ptr, A1=DCE ptr BRA.S RegExit ; and return, successful ; The loadResource wasn't successful, so set IOResult to the error code ; and return to user. GoError MOVE.W D0,IOResult(A0) ; post the error RegExit MOVEM.L (SP)+,D3-D7/A1-A6 ; restore regs RTS ; ;______________________________________________________________________ ; ; Routine: MapUnit ; Arguments: D2 (input) -- unit RefNum ; A1 (output) -- pointer to Device Control Entry ; A2 (output) -- handle of DCE memory block ; Function: Maps an I/O RefNum to a Device Control Entry pointer; only ; I/O driver RefNums are mapped. Z-bit = 1 if purged. ;______________________________________________________________________ MapUnit NOT.W D2 ; bitwise complement to get unitnum BMI.S BadUnit CMP.W UnitNtryCnt,D2 ; is it in range? BGE.S BadUnit ; skip if its not ASL.W #2,D2 ; multiply by four MOVE.L UTableBase,A1 ; get address of unit table MOVE.L 0(A1,D2.W),D2 ; add in the offset BEQ.S BadUnit ; branch if there is no driver installed MOVE.L D2,A2 ; DCE handle MOVE.L (A2),D2 ; dereference handle BEQ.S IOCoreErr ; syserror if purged (DCE should never be) MOVE.L D2,A1 ; pointer to Device Control Entry RTS BadUnit MOVEM.L (SP)+,D0/D3-D5/A2-A3 ; restore regs and pop return address ; into D0!! MOVEQ #BadUnitErr,D0 ; flag the error RTS ;______________________________________________________________________ ; ; Routine: Open ; Arguments: A0 (input) -- pointer to Open parameter block ; D1 (input) -- OS Call trap word ; D0 (output) -- error code ; Function: We look up the input filename in the object manager data ; structure (for ram-based drivers) and indirect thru the ; Unit Table (for rom-based drivers); if an I/O driver is ; installed, we make a synchronous call to that I/O driver, ; and return the proper unit number as the refnum; ; otherwise we let the file system take care of it. ; ; ;______________________________________________________________________ Open MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVE.L A0,D5 ; save parameter block pointer MOVE.W D1,IOTrap(A0) ; and D1 TST.W SysMap ; are resources opened yet? BMI.S SrchROMBased ; if not, don't bother SUBQ #4,SP ; make room for result MOVE.L #DrvrRType,-(SP) ; push resource class DRVR MOVE.L IOFileName(A0),A3 ; get pointer to name MOVE.L A3,-(SP) ; push name pointer _GetNamedResource ; search resource manager first MOVE.L (SP)+,D3 ; did it find one? BEQ.S SrchROMBased ; if not,go search ROM-based drivers ; it's a RAM-based driver, so lock everything down if ; it needs it, and then call the driver MOVE.W ResErr,D0 ; sucessful load? BNE.S GoOpenEnd ; if not, return error ; figure out the driver's ID by using GetResInfo LINK A6,#-270 ; get some room MOVE.L D3,-(SP) ; push driver handle PEA -4(A6) ; push place to stick ID PEA -8(A6) ; push place to stick type PEA -270(A6) ; push place to stick name _GetResInfo ; get the info MOVE.W -4(A6),D4 ; get ID number in D2 NOT.W D4 ; map into refNumber UNLK A6 ; deallocate temp storage ; make sure the driver is installed MOVE.W D4,D0 ; get refNum in D0 BSR MpUnitNum ; is it installed? BNE.S SkipInstall ; if so, we're cool MOVE.W D4,D0 ; get refNum in D0 _DrvrInstall ; install it GoOpenEnd BNE OpenEnd ; if bad, give up SkipInstall MOVE D4,D2 ; make sure D2 has the refNum BSR MapUnit ; map it into DCE pointer and handle MOVE.L D3,DCtlDriver(A1) ; handle to driver code -> DCE MOVE.L D3,A0 ; get handle in A-reg MOVE.L (A0),A0 ; dereference driver handle MOVE.W (A0)+,DCtlFlags(A1) ; flags from driver -> DCE MOVE.L (A0)+,DCtlDelay(A1) ; copy DrvrDelay,DrvrEMask MOVE.W (A0)+,DCtlMenu(A1) ; and DrvrMenu BCLR #DrvrActive,DCtlFlags+1(A1) ; mark driver inactive to start BSET #DRAMBased,DCtlFlags+1(A1) ; driver is RAM-based BRA.S DrvrLock ; lock it and call it ; it's not a defined RAM-based-driver, so look it up in the unitTable list SrchROMBased MOVE.L D5,A0 ; restore parameter block pointer MOVE.L UTableBase,A3 ; get address of the unit I/O table MOVEQ #9,D2 ; number of units to check (only thru ; async b-port driver) MOVEQ #-1,D4 ; init refnum index DOpenLoop MOVE.L (A3)+,D0 ; get next Device Control Entry handle BEQ.S NextEntry ; branch if no entry installed MOVE.L D0,A2 ; DCE handle MOVE.L (A2),D0 ; dereference handle BEQ.S NextEntry ; branch if null MOVE.L D0,A1 ; A1 -> Device Control Entry BTST #DRAMBased,DCtlFlags+1(A1) ; only check ROM-based drivers BNE.S NextEntry MOVEM.L A0-A1,-(SP) ; preserve A0-A1 over compare call MOVE.L IOFileName(A0),A0 ; point A0 to input filename MOVE.L DCtlDriver(A1),A1 ; point to the driver LEA DrvrName(A1),A1 ; point A1 to driver name MOVEQ #0,D0 ; clear high bytes MOVE.B (A0)+,D0 ; string 1 length SWAP D0 MOVE.B (A1)+,D0 ; string 2 length _CmpString MOVEM.L (SP)+,A0-A1 BEQ.S DrvrLock ; branch if they matched NextEntry SUBQ #1,D4 ; bump refnum index SUBQ.W #1,D2 ; next unit table entry BGT.S DOpenLoop ; continue searching till done ; it wasn't found in the device table so let the file system deal with it MOVE.W IOTrap(A0),D1 ; restore D1 MOVEM.L (SP)+,D3-D5/A2-A3 ; restore regs JMP FileOpen ; needs D1 and A0 unharmed ; lock down the driver if it needs locking DrvrLock BTST #DNeedLock,DCtlFlags(A1) ; need to be locked? BEQ.S FoundUnit ; if not, don't lock it ; before locking, force a heap compact MOVEQ #8,D0 ; use a BIG number SWAP D0 ; get it in high part .WORD $A04C ; compact the heap, preserve A0 ; lock down the DCE and the driver code MOVE.L DCtlDriver(A1),A0 ; get driver code handle BSET #Lock,(A0) ; lock it down BSET #Lock,(A2) ; lock down DCE ; we found the unit so call it ; A1 = DCE pointer and D4 = refnum FoundUnit MOVE.L D5,A0 ; restore parameter block pointer MOVE.W D4,IORefNum(A0) ; return the refnum MOVEQ #OpenErr,D0 ; assume permissions error MOVE.B DCtlFlags(A1),D1 ; get the driver's permission bits MOVE D1,D2 ; save it OR.B IOPermssn(A0),D1 ; 'or' in open request's permissions EOR.B D2,D1 ; if anything changed it's an error AND.B #$0003,D1 ; only worry about rd, write BNE.S OpenEnd ; branch if permissions error BSET #DOpened,DCtlFlags+1(A1) ; exit if already opened, flag it BNE.S OpenOK MOVEQ #DrvrOpen,D1 ; open routine offset within driver BSR GoDriver ; call the driver's open routine ; (preserves A1 and A2) TST.W D0 ; any errors returned from the open? (kwk) BNE.S CloseFini ; yes, mark driver as closed and unlock it BTST #DNeedLock,DCtlFlags(A1) ;does it have to stay locked? BNE.S OpenOK ; exit if so UnLkStuff MOVE.L DCtlDriver(A1),A0 ; get handle to the driver BCLR #Lock,(A0) ; unlock it BCLR #Lock,(A2) ; unlock the DCE ; everything was cool so just return OpenEnd MOVEM.L (SP)+,D3-D5/A2-A3 ; restore regs RTS OpenOK MOVEQ #0,D0 BRA.S OpenEnd ;______________________________________________________________________ ; ; Routine: Close ; Arguments: A0 (input) -- pointer to Close parameter block ; D1 (input) -- OS Call trap word ; D0 (output) -- error code ; Function: If the RefNum is positive, the file system does all the work; ; otherwise, we make a synchronous call to the IO Driver's ; Close routine, after waiting for the driver to finish any ; pending requests. If the driver is a RAM-based driver, its ; code block is unlocked; the Device Control Entry for the ; driver is always unlocked. ;______________________________________________________________________ Close MOVE.W IORefNum(A0),D2 ; get the refnum BMI.S IOClose ; branch if it's for us JMP FileClose ; let the file system handle it IOClose MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions BSR MapUnit ; get pointer to DCE in A1, handle in A2 BTST #DOpened,DCtlFlags+1(A1) ; is it open? BEQ.S OpenOK ; if not, exit ok WaitUntilDone BTST #DrvrActive,DCtlFlags+1(A1) BNE.S WaitUntilDone ; wait until the driver has finished MOVEQ #DrvrClose,D1 ; close routine offset within driver BSR GoDriver ; call the driver's close routine ; (preserves A1 and A2) CloseFini BCLR #DOpened,DCtlFlags+1(A1) ;flag it as closed BRA.S UnLkStuff ; unlock everything (including ROM ; code) ;______________________________________________________________________ ; ; Routine: Read ; Arguments: A0 (input) -- pointer to Read parameter block ; D1 (input) -- OS Call trap word ; D0 (output) -- error code ; Function: If the read call has the asynchronous flag bit set, it vectors ; to the driver to do the dirty work; otherwise it performs an ; asynchronous read call and polls the I/O control block until ; the I/O has completed. ; ;______________________________________________________________________ Read MOVE.W IORefNum(A0),D2 ; get the refnum BMI.S IORead ; if <0, its a device call JMP FileRead ; let the file system handle it IORead MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVEQ #ReadErr,D0 ; not-read-enabled error MOVEQ #DReadEnable,D4 ; read enable flag bit number ;______________________________________________________________________ ; ; Routine: IORWCS ; Arguments: A0 (input) -- pointer to command parameter block ; D0 (input) -- operation-specific error code ; D1 (input) -- OS Call trap word ; D2 (input) -- the driver's RefNum ; D4 (input) -- flag enable bit for this command ; stack has D3-D4, A2 saved on it ; Function: shares code for Read, Write, Control, and Status calls ; to IO Drivers; it maps the RefNum into a Device Control Entry ; pointer in A1, checks that the driver is enabled for such a ; call, and then handles the synchronous or asynchronous calling ; of the driver. If the NoQueue bit in the trap is set, it ; calls the driver directly without any queuing ; ;______________________________________________________________________ ; kwk -- at the start we do the nasty thing to jam the MacWorks hard disk driver refnum if whoever ; is calling the IO core incorrectly set the refnum (but at least got the drive number right) ; Problem is that lots of code assumes use of the Sony disk drive (always uses FFFB for refnum) IORWCS CMPI.W #4,IODrvNum(A0) ; was it drive number 4? BNE.S @0 ; no, skip problem check MOVE.W #$FFFE,D2 ; force driver refnum to be hard disk driver MOVE.W D2,IORefNum(A0) ; and move back into DCE @0 BSR MapUnit ; first, map RefNum into DCE addr in A1 CMP.B #AWrCmd,D1 ; read or write? BGT.S RWCSInit ; branch if status or control CLR.L IONumDone(A0) ; nothing done so far RWCSInit MOVE.W #1,IOResult(A0) ; set IOResult to "in progress" MOVE.W D1,IOTrap(A0) ; set up IOCB: mark I/O command type MOVE.W #IOQType,QType(A0) ; say its an I/O queueing element BTST D4,DCtlFlags(A1) ; is driver enabled for this command? MOVEM.L (SP)+,D3-D5/A2-A3 ; (clean up the stack) BEQ.S RWCSDone ; give up if not MOVEQ #NotOpenErr,D0 ; assume it's not open BTST #DOpened,DCtlFlags+1(A1) ; is it open? BEQ.S RWCSDone ; if not, exit ok BTST #NoQueueBit,D1 ; no queue bit set? BNE DoImedIO ; if so, go right to the driver BTST #AsynTrpBit,D1 ; async bit on? BNE DoAsyncIO ; if so, don't wait for completion CLR.L IOCompletion(A0) ; no completion routines for sync calls MOVE.L A0,-(SP) BSR DoAsyncIO MOVE.L (SP)+,A0 SyncWait MOVE.W IOResult(A0),D0 ; test the sync completion flag BGT.S SyncWait RWCSDone RTS ;______________________________________________________________________ ; ; Routine: Write ; Arguments: A0 (input) -- pointer to Write parameter block ; D1 (input) -- OS Call trap word ; D0 (output) -- error code ; Function: If the write call has the asynchronous flag bit set, it vectors ; to the driver to do the dirty work; otherwise it performs an ; asynchronous write call and polls the I/O control block until ; the I/O has completed. ; ;______________________________________________________________________ Write MOVE.W IORefNum(A0),D2 ; get the refnum BMI.S IOWrite ; if <0, it's a device call JMP FileWrite ; let the file system handle it IOWrite MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVEQ #WritErr,D0 ; not-write-enabled error MOVEQ #DWritEnable,D4 ; write enable flag bit number BRA.S IORWCS ;______________________________________________________________________ ; ; Routine: Control ; Arguments: A0 (input) -- pointer to Control parameter block ; Function: Call driver if control is enabled. ; ;______________________________________________________________________ Control MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVE.W IORefNum(A0),D2 ; get the refnum MOVEQ #ControlErr,D0 ; not-control-enabled error MOVEQ #DCtlEnable,D4 ; control enable flag bit number BRA.S IORWCS ;______________________________________________________________________ ; ; Routine: Status ; Arguments: A0 (input) -- pointer to Status parameter block ; Function: Call driver if status is enabled. ; ; 26-Apr-83 AJH implemented status call 1 -- return DCE handle ; ;______________________________________________________________________ Status MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVE.W IORefNum(A0),D2 ; test the sign of the refnum CMP #1,CSCode(A0) ; is it status call 1? BEQ.S RetDCEHandle ; if so, special case it MOVEQ #StatusErr,D0 ; not-status-enabled error MOVEQ #DStatEnable,D4 ; status enable flag bit number BRA.S IORWCS RetDCEHandle BSR MapUnit ; figure out the DCE handle MOVE.L A2,CSParam(A0) ; return it as the result BRA OpenOK ; restore registers and return ;____________________________________________________________________ ; ; Routine: KillIO ; Arguments: A0 (input) -- pointer to param block w/refNum ; D0 (output) -- result code ; ; Function: KillIO terminates the current request in progress ; for an I/O driver and empties its queue, posting ; error codes and running completion routines as ; appropriate. ;______________________________________________________________________ KillIO MOVEM.L D3-D5/A2-A3,-(SP) ; observe pascal regsave conventions MOVE.W IORefNum(A0),D2 ; get the refNum BSR MapUnit ; derive the DCE pointer MOVE.W #KillCode,CSCode(A0) ; KillIO maps to control call 1 MOVE SR,-(SP) ; preserve current status ORI #$0300,SR ; interrupts off for queue manipulation MOVEQ #DrvrCtl,D1 ; control routine offset within driver MOVEQ #ControlErr,D0 ; not-control-enabled error (kwk) MOVEQ #DCtlEnable,D4 ; control enable flag bit number BTST D4,DCtlFlags(A1) ; is driver enabled for this command? BEQ.S KillFailed ; give up if not MOVEQ #NotOpenErr,D0 ; assume it's not open BTST #DOpened,DCtlFlags+1(A1) ; is it open? BEQ.S KillFailed ; if not, give up (kwk) BSR GoDriver ; call the driver control routine BNE.S KillFailed ; if that failed, punt KillLoop TST.L DCtlQHead(A1) ; is there a request? BEQ.S DoneKillIO ; if not, we're done MOVEQ #AbortErr,D0 ; get the "abort" error code MOVE #$2300,D1 ; make PostCompletion stay at level 3 BSR PostCompletion ; post error code and dequeue BRA.S KillLoop ; loop until done DoneKillIO BSR DeActDrvr ; mark it inactive and unlock DCE, driver MOVEQ #0,D0 ; no error KillFailed MOVE (SP)+,SR ; interrupts back on BRA OpenEnd ; restore regs and return to caller ;______________________________________________________________________ ; ; Routine: DrvrInstall ; Arguments: D0 (input) -- Driver Refnum (-1 thru -32) ; D0 (output) -- result code, 0=driver installed ; Function: Used to install a driver. ; A Device Control Entry for the driver is created and its ; handle entered into the specified Unit Table position (-1 ; thru -32). ; ; If the unit number is -4 thru -9, the corresponding ; ROM-based driver will be replaced. ; ;______________________________________________________________________ DrvrInstall MOVEM.L A2-A3/D3-D5,-(SP) ; observe Pascal regsave conventions MOVE.L A0,A3 ; save name pointer MOVE.L D0,D3 ; save refnum BSR.S MpUnitNum ; make it a unit num (don't return if bad) BNE.S ClearDCE ; if not nil, reuse it MOVEQ #DCtlEntrySize,D0 ; get size of device control entry _NewHandle ,SYS ; allocate it on system heap BNE.S DInstRTS ; exit on mem full errors ; clear out the DCE entry ClearDCE MOVEQ #DCtlEntrySize-1,D0 ; get size in bytes MOVE.L (A0),A1 ; DCE pointer @1 CLR.B (A1)+ ; clear a byte DBRA D0,@1 ; loop till cleared ; install it in the table MOVE.L A0,(A2) ; DCE handle -> Unit I/O Table MOVE.L (A0),A3 ; DCE pointer BSET #DRamBased,DCtlFlags+1(A3) ; just set RAM-based until we load it MOVE.W D3,DCtlRefNum(A3) ; set up the refNum (queue inited to 0s) DInstOKExit MOVEQ #0,D0 ; no error DInstRTS MOVEM.L (SP)+,A2-A3/D3-D5 ; restore regs RTS ; routine to map the refNum into a DCE handle/pointer MpUnitNum NOT.W D0 ; bitwise complement to get unitnum BMI.S @1 ; br if it was positive CMP.W UnitNtryCnt,D0 ; is it in range? BGE.S @1 ; exit if not ASL.W #2,D0 ; multiply by four MOVE.L UTableBase,A2 ; get address of unit table ADD D0,A2 ; add in the offset MOVE.L (A2),A0 ; get the old DCE handle MOVE.L A0,D0 ; was it nil? RTS ; return with A2=entry pointer, A0=handle @1 BRA BadUnit ;______________________________________________________________________ ; ; Routine: DrvrRemove ; Arguments: D0 (input) -- Driver Refnum (-1 thru -24) ; D0 (output) -- result code, 0=driver removed ; -1=driver not found ; Function: Used to remove a driver. A RAM-based driver is purged from ; the system heap using a ReleaseResource call. Memory for ; the Device Control Entry for the driver is disposed and the ; driver's close routine is called. ;________________________________________________________________________ DrvrRemove MOVEM.L A2-A3/D3-D5,-(SP) ; observe Pascal regsave conventions BSR.S MpUnitNum BEQ.S DInstOKExit ; exit if entry is empty (already removed) MOVE.L A2,A3 ; save entry pointer MOVE.L D0,A2 ; DCE handle MOVE.L (A2),A1 ; DCE pointer ; A3 = ptr to UnitTable Entry ; A2 = DCE handle ; A1 = DCE ptr BTST #DOpened,DCtlFlags+1(A1) ; check if it's Opened BNE.S RemovErr ; don't remove an opened driver BTST #DRAMBased,DCtlFlags+1(A1) ; check if it's RAM-based BEQ.S @1 ; branch for ROM-based drivers MOVE.L DCtlDriver(A1),-(SP) ; driver object handle _ReleaseResource ; go remove it @1 MOVE.L A2,A0 ; deallocate DCE handle _DisposHandle ,SYS ; dispose it on system heap CLR.L (A3) ; clear Unit Table entry BRA.S DInstOKExit ; and exit successfully RemovErr MOVEQ #DRemovErr,D0 ; BRA.S DInstRTS ; restore regs and exit .END ;FILE KBD.TEXT ;MACWORKS Source ;_______________________________________________________________________ ; ; ; written by Jeff Parrish April 11, 1984 ; ; Here is the official MacWorks keyboard driver. It is responsible ; for turning the keycode information provided by the keyboard into ASCII. ; ; MODIFICATION HISTORY: ; ; 30-Aug-82 LAK modified for 512-dot machine (took out clock routines) ; 11-Oct-82 LAK updated to 0.5D 384-dot machine OS version. ; 05-Jan-83 LAK fixed bug: now reports enter key keystrokes . . . ; 12 Jan 83 LAK changed VIA PCR initialization for one-second-interrupt ; 04 Feb 83 LAK fixed bug in screen dump . . . (now uses long count). ; 15 Feb 83 LAK removed mapping procs - they are now resources . . . ; 01 Jun 83 LAK resets keyboard-intf part of VIA on errors, expands keypadmap ; to 8 bytes, raw keycodes translated to 0-63 for keyboard, ; 64-127 for keypad (high bit zeroed). ; 09 Jun 83 LAK fixed bug in keylast upkeep (stop auto repeats). ; 13 Jul 83 LAK changed RSetKMap to clear 9 words instead of 5. ; 11 Apr 84 JWP end of extensive modifications ; 18 Jun 84 KWK added termination proc check in PowerOff ; 24 Oct 84 KWK added warm-restart check in PowerOff ; 8 Nov 84 KWK added flush of all volumes in PowerOff ; 10 Jan 85 JWP changed check of VCBQHdr in PowerOff to include uninit'ed case ; 11 Jan 85 JWP saved state of Option key for Option-Poweroff reboot ; <18Apr85> RDC extend poweroff delay to 4 seconds to ensure Widget has time to park heads ; <24Apr85> RDC remove poweroff delay, move to driver poweroff routine since Finder calls that directly ; <29Apr85> RDC added change to properly handle old keyboard's (id = 00) ; <30Apr85> RDC added software "debounce" for mouse button changes ;_______________________________________________________________________ .NOLIST .INCLUDE Tlasm-SysEqu.Text .INCLUDE Tlasm-FSEqu.Text .INCLUDE Tlasm-SysMacs.Text .INCLUDE Tlasm-SonyEqu.Text .LIST .DEF InitKbd,KeyHandler,PuntDisk ; Keyboard Driver Variables KbdTalked .EQU KbdVars ; $FF after keyboard shifts something in ; $00 set every 1/2 second by keyboard vbl task Numeric .EQU KbdTalked+1 ; $FF after numeric ESC char received ; $00 after next code is processed ModNumSent .EQU Numeric+1 ; $FF after sending a model number token ; $00 after receiving model number KeyExtra .EQU ModNumSent+1 ; extra byte for the future Token .EQU $10 ; Normal Token IToken .EQU $14 ; Instant Token ModelToken .EQU $16 ; Model Number Token .PROC KbdMngr,0 ;-------------------------------------------------------------------------- InitKbd ; initialize state variables CLR.B KbdType ; assume we have no keyboard to start CLR.L KbdVars ; zero KbdTalked, Numeric, ModNumSent BSR RSetKMap ; reset the keyboard map, etc. ST KbdTalked ; assume keyboard has just talked CLR.L Key1Trans ; no mapping procs until resources read in CLR.L Key2Trans ; RTS ; ;-------------------------------------------------------------------------- ; ; Lisa/Mac keyboard interrupt handler ; Routine: KeyHandler (user routine) ; Arguments: D0 (input) -- keycode (integer) ; D1 (input) -- $00=up, $80=down transition (integer) ; Mac convention is: bit 7 of keycode is set for a key-up transition ; All registers are saved by Rick prior to calling KeyHandler. KeyHandler ST KbdTalked ; note that the keyboard has talked EORI.B #$80,D1 ; Change Lisa up/down to Mac convention ; ; Check and see if the key came from the numeric keypad ; CMPI.B #$4D,D0 ; special case for Lisa keypad "1" BEQ @0 CMPI.B #$49,D0 ; special case for Lisa keypad "0" BEQ @0 CMPI.B #$30,D0 ; other keypad keys are in range $20-$2F BPL ConvertKey CMPI.B #$20,D0 BMI Buttons ; mouse button, on/off button etc. < $20 @0 ST Numeric ; it was a numeric keypad key ; Convert the Lisa keycode to a Mac keycode ConvertKey MOVE.W D0,D2 ; keycode SUBI.W #$20,D2 ; keycode ($20,$7F) => (0,$5F) LEA usChart,A0 ; assume US keyCode table TRAPTO _Keyboard ; what kind of keyboard do we have? ANDI.B #$3F,D0 ; we don't care about the manufacturer BEQ.S @0 ; skip if really old US (id = 00) <29Apr85> CMP.B #$0F,D0 ; is it old US? <29Apr85> BEQ.S @0 ; skip if yes <29Apr85> EORI.B #$3F,D0 ; is it final US? BEQ.S @0 ; branch if it is LEA euroChart,A0 ; address of european keyCode table @0 MOVE.B 0(A0,D2),D0 ; Get Mac keycode OR.B D1,D0 ; set "up/down" bit ; Map the Mac keycode into ASCII - Event MOVE.B D0,D3 ; save in D3 MOVEQ #$7F,D2 ; byte mask (note high bytes are 0 4 later) AND.B D0,D2 ; strip off the high bit of the keycode LSR.B #1,D2 ; map into code from 0 to 63 TST.B Numeric ; if in numeric mode BEQ.S @1 ADD.W #64,D2 ; offset by 8 bytes to get to keypadmap @1 TST.B D3 ; key down or up? (+ or -) BPL.S @2 ; br if down MOVE.W KeyLast,D1 ; last [raw code][ascii] LSR.W #8,D1 ; get code into low byte CMP.B D1,D2 ; same key? BNE.S @2 ; CLR.W KeyLast ; then stop the repeat @2 MOVE.L D2,-(SP) ; preserve D2 BSR.S MapKey ; map key into ASCII in D0, set bit map MOVE.L (SP)+,D2 LSL.W #8,D2 ; shift raw code over MOVE.B D0,D2 ; got any ASCII? BNE GoodKey RTS ; no event if we didn't ; we got a good key so generate an event, etc. GoodKey MOVEQ #KeyUpEvt,D0 ; get event number (guess it's key up) TST.B D3 ; original key code BMI.S @3 ; branch if key up MOVEQ #KeyDwnEvt,D0 ; get event number MOVE.L Ticks,KeyTime ; mark the time for auto repeating MOVE.L Ticks,KeyRepTime MOVE.W D2,KeyLast ; save its keycode and ASCII ; generate a keyboard event. On entry, D0 contains the event number, ; D2 contains the ASCII for the key and the raw keycode. @3 MOVE.L D0,A0 ; event number MOVE.L D2,D0 ; [raw code][ASCII code] _PostEvent ; post the event RTS ;_______________________________________________________________________ ; ; MAPKEY maintains the keyboard bitmap and translates raw keystrokes into ASCII ; on entry, the keycode is in D0; ;_______________________________________________________________________ ; first map keycode in D0/D3 into an index into the keyboard/keypad bitmap where A0 ; contains an address, D1 contains an offset, and D2 contains a bit index. MapKey LEA KeyMap,A0 MOVE.W D2,D1 ; 0-127 code LSR.W #3,D1 ; divide by 8 for byte offset ADD.W D1,A0 ; point to the byte BSET D2,(A0) ; set the bit in the bitmap TST.B D3 ; going up or down? BPL.S @0 ; if its down, bit should be set BCLR D2,(A0) ; clear the bit in the bitmap for keydowns ; now translate into ASCII. D2 has the keycode from 0 to 63 @0 MOVE.L Key1Trans,-(SP) ; use the specified translation procedure TST.B Numeric ; Numeric keypad? BEQ.S @1 MOVE.L Key2Trans,(SP) ; use Numeric mapping proc if so CLR.B Numeric ; note we processed Numeric key code @1 MOVE.W KeyMap+6,D1 ; get the meta-key part of the keymap TST.L (SP) ; mapping proc installed? BNE.S @2 ; br if so MOVE.L (SP)+,D0 ; return 0 if no map proc ; D1=meta map, D2=0-127 code on entry ; D3=original key code @2 RTS ; D0 contains ASCII or 0 upon ; return from mapping routine ; D2-D3 preserved ; ; zero key-map, keypad-map, and keylast ; RSetKMap LEA KeyMap,A0 MOVEQ #8,D0 @1 CLR.W (A0)+ DBRA D0,@1 CLR.B KbdTalked RTS ;----------------------------------------------------------------------- ; ; Buttons translates disk, poweroff or mouse buttons into events ; ;----------------------------------------------------------------------- ; Buttons CMPI.B #$8,D0 ; was it the power button? BEQ PowerOff ; if it was, turn ourselves off! CMPI.B #$6,D0 ; was it the mouse button? BEQ MouseButton CMPI.B #5,D0 ; was a disk button pushed? BMI DiskEvent RTS PowerOff ;kwk -- RamDisk (or any other driver) termination routine check MOVE.L TermProc,A0 ; get proc BEQ.S Flush4PowerDown ; no proc installed, normal power down JSR (A0) ; execute the termination proc ; assume all regs (ex. D0) are saved TST.B D0 ; TRUE => turn off BNE.S Flush4PowerDown ; yes, keep turning off... RTS ; otherwise do nothing and return ;kwk -- Flush all on-line volumes. Flush4PowerDown MOVEQ #-1,D0 ; set up loop index @0 CLR.W -(SP) DBRA D0,@0 ; clear off space for ParamBlock MOVE.L SP,A0 ; A0 = ptr to ParamBlock MOVE.L VCBQHdr+QHead,D0 ; get ptr to 1st VCB entry @2 BLE.S @4 ; last one, exit (must handle -1) MOVE.L D0,A1 MOVE.W VCBDrvNum(A1),IOVDrvNum(A0) BEQ.S @3 ; off-line, skip this flush _FlushVol ; flush it @3 MOVE.L QLink(A1),D0 ; get ptr to next VCB BRA.S @2 ; and loop @4 ADD #IOQElSize,SP ; restore stack BSR.S PuntDisk ; eject the disks ; Now check if the option or command key was down. If so, do Mac warm-restart MOVE.W KeyMap+6,D0 ; load up the command and option keys. ANDI.W #$8004,D0 ; are either of them set? BEQ.S @5 ; no, Power-off MOVE.W D0,OptionSave ; save the option key state between boots JMP $40000A ; do warm-restart @5 MOVE.W #$FF,D0 ; ramp the contrast to its min value TRAPTO _RampContrast ; returns immediately. ; (delay removed) <24Apr85> TRAPTO _PowerDown ; there will be no returning... PuntDisk MOVE SR,-(SP) ; save status register MOVE.W #$2000,SR ; turn interrupts back on MOVE.W #1,D4 ; drive #1 MOVE.W #5,D6 ; eject command MOVE.L SonyStart,A3 ; get start of disk 1 routines MOVEM.L D1-D6/A3,-(SP) ; save our registers JSR (A3) ; call UnitIO to access the disk MOVEM.L (SP)+,D1-D6/A3 ; replace our registers MOVE.W #2,D4 ; drive #2 MOVEM.L D1-D6/A3,-(SP) ; save our registers JSR (A3) ; call UnitIO to access the disk MOVEM.L (SP)+,D1-D6/A3 ; replace our registers MOVE (SP)+,SR ; restore status reg RTS ; and return DiskEvent TST.B D1 ; was it a button up event? BNE Downer ; no, process RTS ; else leave Downer MOVE.L sonyVars,A1 ; get location of Sony locals MOVE.W #Drv1,D1 ; assume drive 1 CMPI.B #3,D0 ; 1=disk #1 inserted, 3=disk #2 BMI @1 MOVE.W #Drv2,D1 ; offset to drive 2's locals @1 MOVE.B #2,DiskInPlace(A1,D1); save the button down state MOVE.L #DiskInsertEvt,A0 ; set the event type MOVEQ #1,D0 ; assume drive 1 CMP.W #Drv1,D1 ; is that true? BEQ.S @2 MOVEQ #2,D0 ; must be drive 2 @2 _PostEvent RTS MouseButton ; first do debounce check to properly handle flaky mice <30Apr85> MOVE.L Ticks,D0 ; save current tick count <30Apr85> CMP.B MBSTATE,D1 ; any state change? <30Apr85> BEQ.S @9 ; ignore if not <30Apr85> TST.B D1 ; up transition? <30Apr85> BMI.S @1 ; skip check if yes <30Apr85> MOVE.L D0,D2 ; else get current tick count <30Apr85> MOVE.L MBTicks,D3 ; and ticks at last change <30Apr85> SUB.L D3,D2 ; compute difference <30Apr85> CMP.L #1,D2 ; more than 1 tick change? <30Apr85> BLS.S @9 ; skip if not to ignore "bounce" <30Apr85> ; all's kosher - save current state @1 MOVE.L D0,MBTicks ; update the ticks <30Apr85> MOVE.B D1,MBSTATE ; also update the state ; Convert from Mac state ($80=up, 0=down) to Mac event (1=down, 2=up) ROR.B #$7,D1 ; $80 -> $1, $0 -> $0 ADDQ.B #1, D1 ; 2, or 1 ; post the mouse button event MOVE.L D1,A0 ; get event number where it belongs MOVEQ #0,D0 ; no message _PostEvent ; post the event @9 RTS ; The following table converts a Lisa keycode to a Mac keycode. The table is ; contiguous and represents the keycode range $20-$7F. usChart .BYTE $0F, $1D, $0D, $05, $33, $37, $39, $1B .BYTE $2D, $2F, $31, $11, $03, $29, $2B, $19 .BYTE $00, $00, $00, $00, $00, $00, $00, $00 .BYTE $00, $00, $00, $00, $00, $00, $00, $00 .BYTE $37, $31, $55, $00, $47, $67, $69, $00 .BYTE $49, $25, $00, $00, $59, $27, $75, $00 .BYTE $33, $3B, $41, $45, $4D, $51, $43, $3D .BYTE $5D, $4B, $53, $4F, $63, $57, $5F, $3F .BYTE $1D, $2D, $35, $39, $2F, $1F, $23, $21 .BYTE $65, $07, $0B, $09, $13, $11, $17, $5B .BYTE $01, $27, $29, $2B, $25, $19, $03, $1B .BYTE $61, $0D, $0F, $05, $75, $73, $71, $6F euroChart .BYTE $0F, $1D, $0D, $05, $33, $37, $39, $1B .BYTE $2D, $2F, $31, $11, $03, $29, $2B, $19 .BYTE $00, $00, $00, $00, $00, $00, $00, $00 .BYTE $00, $00, $00, $00, $00, $00, $00, $00 .BYTE $37, $31, $49, $0D, $47, $67, $63, $00 .BYTE $55, $25, $00, $00, $15, $27, $75, $00 .BYTE $33, $3B, $41, $45, $4D, $51, $43, $3D .BYTE $57, $4B, $53, $4F, $69, $5F, $59, $3F .BYTE $1D, $2D, $35, $39, $2F, $1F, $23, $21 .BYTE $65, $07, $0B, $09, $17, $13, $5B, $5D .BYTE $01, $27, $29, $2B, $25, $19, $03, $1B .BYTE $61, $0F, $11, $05, $75, $73, $71, $6F .END $EXEC{link/grafsys.text -- Does a partial link of the raw object files needed to build grafsys.obj} $SUBMIT -priam-exec-setprefix(-priam) R{un}tools-New/Linker ?{options} +R{partial link, no code stripping} +X{mac link} {no more options} obj-GrafLib obj-QuickGlue obj-FontMgr {no more files to link} {no list file} obj-GrafSys{-priam-obj-grafsys.obj is output file} $ENDEXEC $EXEC{link/miscsys.text -- Does a partial link of the raw object files needed to build miscsys.obj} $SUBMIT -priam-exec-setprefix(-priam) R{un}tools-New/Linker ?{options} +R{partial link, no code stripping} +X{mac link} {no more options} obj-Packages obj-SexyDate {no more object files to link} {no list file} obj-MiscSys{object file created} $ENDEXEC $EXEC{link/OpSys.text -- Does a partial link of the raw object files needed to build OpSys.obj} $SUBMIT -priam-exec-setprefix(-priam) R{un}tools-New/Linker ?{options} +R{partial link, no code stripping} +X{mac link action} {no more options} obj-START obj-QUEUE obj-INTHND obj-VBLCORE obj-CRSRCORE obj-DISPATCH obj-IOCORE obj-KBD obj-SONY obj-ASYNC obj-HEAP obj-HEAPGUTS obj-BLOCKMOVE obj-SOUND obj-EVENTS obj-FS obj-SEGLOADER obj-SYSUTIL obj-RESTOP{must be last file linked} {no more object files to link} {no link file} obj-OpSys{-priam-obj-opsys.obj, the object file created} $ENDEXEC $EXEC{link/toolsys.text -- Does a partial link of the raw object files needed to build toolsys.obj} $SUBMIT -priam-exec-setprefix(-priam) R{un}tools-New/Linker ?{options} +R{partial link, no code stripping} +X{mac code generation} {no more options} obj-ToolEvents obj-WMgr obj-MenuMgr obj-CMgr obj-RMgr obj-DMgr obj-Bytes obj-DeskMgr obj-GetMgr obj-TE68K obj-scrap {no more object files to link} {no list file} obj-toolSys{-priam-obj-toolsys.obj, final object file created} $ENDEXEC $EXEC {load/mwdisk.text, stuffs the macworks diskette with all of the necessary files. Assumes that a Monitor formatted diskette is in place in the Sony driver} $SUBMIT -priam-exec-setprefix(-priam-mwdisk) R{un}Transutil M{ount}C{opy}config.data #2:config.data Y{es, overwrite the file} C{opy}revc/bootfiles.data #2:bootfiles.data Y{es, overwrite the file} C{opy}mon.loader #2:mon.loader Y{es, overwrite the file} C{opy}stub.obj #2:stub.obj Y{es, overwrite the file} C{opy}mapmmu.obj #2:mapmmu.obj Y{es, overwrite the file} C{opy}drivers.obj #2:drivers.obj Y{es, overwrite the file} C{opy}sonyutil.obj #2:sonyutil.obj Y{es, overwrite the file} C{opy}hdskdrvr.obj #2:hdskdrvr.obj Y{es, overwrite the file} C{opy}monitor.obj #2:monitor.obj Y{es, overwrite the file} Q{uit} $SUBMIT -priam-exec-setprefix(-priam) F{iler}U{nmount}lower $ENDEXEC $EXEC {mac/comp.text, arg0/source = file to compile.} $SUBMIT -priam-exec-setprefix(-priam) P{ascal}$G- {no auto code generation} %0/source{file to compile} {no list file} obj-reuse{.I, intermediate file} G{enerate}$M+{mac code generation} obj-reuse{.I, input file} %0/temp{.obj, output file} $ENDEXEC $EXEC {mac/download, arg0.rsrc is the file to download, arg1 = name on Mac disk, arg2 = creator type} R{un}MacCom FYL%0.rsrc{file to copy to} %1{name of the file in macland} APPL{set type to APPL} $IF %2 = '' THEN {set creator to ????} $ELSE %2{creator type} $ENDIF {set creator to ????} {No bundle bit} Q{uit MacCom} $ENDEXEC ;File Sound.TEXT ;MACWORKS copy ;---------------------------------------------------------------- ; ; Sound Driver for the Macintosh Operating System ; ; written by Andy Hertzfeld 19-Jun-83 ; ; This is the official sound driver for the Mac OS. It is fully ; asynchronous and supports three completely different ways of making sounds: ; ; (1) 4 voice engine: allows up to 4 independent voices, each with ; their own pitch and 256 byte waveForm definition. ; ; (2) Square Wave: uses the 6522's timer 1 to generate a square wave ; at any of 256 amplitude levels. This mode is useful because it ; requires very little processor attention (as opposed to mode 1, ; which requires about 50% of the CPU) ; ; (3) Byte-Map: pumps a raw waveform into the DAC. An interpolation ; factor is supplied which allows slower or faster sampling rates than ; the nominal 22KHz ; ; Modification History: ; ; 21-Jun-83 AJH Added control codes 1,2,3 ; 25-Jun-83 AJH Optimized byte-mapped mode some; iterates in square-wave ; 26-Jun-83 AJH forced silence after ByteMap, Integrated with ROM ; 27-Jun-83 AJH disable sound when not active ; 07-Aug-83 AJH zero after ByteMap instead of FF ; 17-Aug-83 AJH no longer inspect CurIOTrap, instead always go to IODone ; 20-Aug-83 SC GoToIODone was one instruction too early ; 83/84 JWP Modified for MacWorks ; 17-Oct-84 KWK Fixed bug in square wave (duration to _Beep is a long) ; ;----------------------------------------------------------------- .INCLUDE Tlasm-SYSEQU.TEXT .INCLUDE Tlasm-GRAFEQU.TEXT .INCLUDE Tlasm-SYSMACS.TEXT .INCLUDE Tlasm-GrafTypes.Text .INCLUDE Tlasm-ToolEqu.Text .INCLUDE Tlasm-QuickMacs.TEXT .INCLUDE Tlasm-ToolMacs.Text .INCLUDE Tlasm-ResEqu.Text ; Sound Driver Global Definitions .PROC Sound,0 .DEF SoundDrvr SoundDrvr .WORD $4E00 ;write, ctl, status, needsLock .WORD 0,0,0 ;not an ornament ; Entry point offset table .WORD SoundOpen-SoundDrvr ; open routine .WORD SoundPrime-SoundDrvr ; prime .WORD SoundCtl-SoundDrvr ; control .WORD SoundStatus-SoundDrvr ; status .WORD SoundClose-SoundDrvr ; close SoundTitle .BYTE 6 ;length of string .ASCII '.Sound ' ;title ; SoundOpen initializes the Mac Sound hardware and the driver variables SoundOpen MOVE.L A1,SoundDCE ;remember the DCE pointer MOVE.B SPVolCtl,D0 ;get the default volume MOVE.B D0,SoundLevel ;remember the new level BSR Volume ;set volume level to its default CLR.B SoundActive ;no VBL task is active NoError MOVEQ #0,D0 ;no error RTS ;return to caller ; SoundClose turns off any pending sound and closes the driver SoundClose BSR.S DeAllocVBL ;deallocate the VBL control block MOVE.B D0,SoundLevel ;remember the new level TRAPTO _Silence ;turn off sound BRA.S NoError ;all done with close ; DeAllocVBL deallocates the current VBL task DeAllocVBL TST.B SoundActive ;is there one allocated? BEQ.S @1 ;if not, skip LEA SoundVBL,A0 ;point to control block _VRemove ;deallocate it CLR.B SoundActive ;flag it as inactive @1 RTS ; Procedure Volume(level: INTEGER); ; ; set the hardware gain to one of 8 levels. The volume is passed in the low ; 3 bits of D0. Volume AND #7,D0 ; only use 3 low bits MOVE.B D0,SDVolume ; update low memory variable TRAPTO _SetVolume RTS ; return ; Sound Driver Control Call Handler. Right now there are three calls: ; ; (1) KillIO ; (2) SetVolume ; (3) SetLevel SoundCtl MOVE.W CSCode(A0),D0 ;get the opCode BEQ.S SoundStatus ;ignore code 0 SUBQ #2,D0 ;which call? BEQ.S DoVolume ;2 = setVolume BLT SoundClose ;1 = KillIO ; it was call 3 (or greater) so set the level MOVE.W CSParam(A0),D0 ;get the new level MOVE.B D0,SoundLevel ;remember the new level ;fall through to exit ; currently, there are no status calls supported SoundStatus BRA GoToIODone ;go to IODone DoVolume MOVE.W CSParam(A0),D0 ;get the param BSR Volume ;set the volume BRA.S SoundStatus ;all done ; SoundPrime is the routine that gets a sound going; it simply cases out on the ; mode word. SoundPrime MOVE.L IOBuffer(A0),A1 ;get pointer to the buffer TST.W (A1)+ ;which mode? BMI LaunchSquare ;negative means squarewave ; This following is done in place of the Byte Mapped, and Four Voice sound ; generation on Pepsi. MOVE.W #$1000,D0 ;wavelength of beep MOVEQ #$10,D1 ;set duration (KWK -- was MOVE.W, high D1 could be trashed) TRAPTO _Beep RTS ; the request is for square wave sound. The rest of the user's buffer has the ; pitch, amplitude and duration information. LaunchSquare MOVEM.W (A1)+,D0-D2 ;get pitch, amplitude, duration TST.W D2 ;is the duration 0? BEQ SquareDone ;duration = 0 is terminating condition MOVE.L A0,-(SP) ;remember command block ptr ; set up the pitch CMP.W CurPitch,D0 ;is it the same as it is now? BEQ.S @1 ;if so, avoid glitch MOVE.W D0,CurPitch ;remember current pitch value ; set up the amplitude @1 MOVE D1,D0 ;get amplitude in D0 TRAPTO _SetVolume ; allocate the VBL task to turn us off when the duration has expired MOVE D2,D0 ;get duration in D0 LEA SquareVBL,A1 ;get VBL task address in A1 BSR.S AllocVBL ;plug us into the VBL pulse beat ; increment IONumDone to indicate we've processed as much as we have MOVE.L (SP)+,A0 ;recover request ptr ADDQ.L #6,IONumDone(A0) ;increment numDone ; start the sound MOVE.W CurPitch,D0 TRAPTO _Noise ; all done launching the square wave so return to the caller Done1 MOVEQ #0,D0 ;no errors possible RTS ;return to caller ; AllocVBL initializes the sound driver VBL control block and plugs it into ; the VBL chain EveryVBL MOVEQ #1,D0 ;duration is 1 for every time AllocVBL LEA SoundVBL,A0 ;point to VBL control block MOVE.L A0,D1 ;remember it for later CLR.L (A0)+ ;clear next pointer MOVE #1,(A0)+ ;qType is 1 MOVE.L A1,(A0)+ ;set up pointer to our code MOVE.W D0,(A0)+ ;set up duration TST.B SoundActive ;is it already in the queue? BNE.S @1 ;if so, don't put it in twice ST SoundActive ;flag that we're active MOVE.L D1,A0 ;recall the VBL task pointer _VInstall ;install it @1 RTS ; here is the VBL task for square wave sound. When it gains control, it ; means that the current note has timed out. If we've exhausted the buffer, ; shut off the sound and call IODone; otherwise launch the next note. SquareVBL MOVE.L SoundDCE,A1 ;get DCE pointer MOVE.L DCtlQHead(A1),A0 ;get request pointer MOVE.L IONumDone(A0),D0 ;get number done so far MOVE.L IOBuffer(A0),A1 ;get buffer ptr ADD.L D0,A1 ;point to next byte to process ADDQ.L #2,A1 ;bump past mode word ADDQ #4,D0 ;better be at least six left... CMP.L IOByteCount(A0),D0 ;have we exhausted the request? BLT LaunchSquare ;if not, go launch the next one ; the request has been all used up so turn off the sound SquareDone TRAPTO _Silence ;turn off square wave CLR.W CurPitch ;invalidate current pitch DoneVBL BSR DeAllocVBL ;deallocate the task GoToIODone MOVEQ #0,D0 ;no error possible MOVE.L SoundDCE,A1 ;the DCE should be in A1 MOVE.L JIODone,A0 ;get the IODone address JMP (A0) ;invoke it .END $EXEC{make/all, builds the entire Macworks diskette from ground up, copies into -priam-mwdisk and writes it out to an inserted Sony. It assumes the Sony already has mon.loader and config.data on it, as well as being a bootable monitor disk} $SUBMIT -priam-exec-setprefix(-priam) $SUBMIT exec-make/monitor $SUBMIT exec-make/hdskdrvr $SUBMIT exec-make/sonyutil $SUBMIT exec-make/drivers $SUBMIT exec-make/stub $SUBMIT exec-make/mapmmu $SUBMIT exec-make/disk $ENDEXEC $EXEC{make/backupdisk, creates a backup profile attatched to #2#1} $WRITELN $WRITELN 'The profile attatched to #2#1 is about to be initialized.' $WRITE 'Is it OK to continue (Y or N) [N] ? ' $READLN %1 $IF UPPERCASE(%1) = 'Y' THEN F{iler} I{initialize}#2#1 Y{es, delete the current OS volume}{init the entire volume} priam2{name of the volume} {default # of files allowed} A{dd directory}-priam2-source A{dd directory}-priam2-obj A{dd directory}-priam2-drivers A{dd directory}-priam2-tlasm A{dd directory}-priam2-tools A{dd directory}-priam2-mwdisk A{dd directory}-priam2-exec A{dd directory}-priam2-mwcopy A{dd directory}-priam2-mwp A{dd directory}-priam2-mwd $ENDIF $ENDEXEC $EXEC {make/buildrom.text, comps and links buildrom} $SUBMIT -priam-exec-setprefix(-priam) $SUBMIT exec-comp(tools-buildrom) L{ink}tools-buildrom -ws-iospaslib -ws-sys1lib -ws-sulib {no more file to link} {no list file} tools-buildrom{.obj, final file} $ENDEXEC $EXEC{make/disk, copies all the necessary MacWorks files for -priam-mwdisk to the inserted monitor format macworks diskette} $SUBMIT -priam-exec-setprefix(-priam) R{un}-priam-Tools-Transutil M{ount the monitor sony}C{opy}-priam-mwdisk-monitor.obj #2:monitor.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-drivers.obj #2:drivers.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-hdskdrvr.obj #2:hdskdrvr.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-sonyutil.obj #2:sonyutil.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-stub.obj #2:revc/stub.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-mapmmu.obj #2:mapmmu.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-revc/bootfiles.data #2:bootfiles.data Y{es, overwrite the existing file} C{opy}-priam-mwdisk-config.data #2:config.data Y{es, overwrite the existing file} C{opy}-priam-mwdisk-mon.loader #2:mon.loader Y{es, overwrite the existing file} Q{uit transutil} F{iler}U{nmount}lower {a space to handle the error} Q{uit the Filer} $ENDEXEC $EXEC{make/drivers, builds the drivers.obj file} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}drivers-drivers {no list file} obj-drivers F{iler}C{opy}obj-drivers.obj mwdisk-drivers.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC $EXEC{make/grafsys, assembles the individual obj files, then links them to form grafsys.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-QuickGlue {no list file} obj-QuickGlue{generates -priam-obj-quickglue.obj} A{ssemble}source-FontMgr {no list file} obj-FontMgr{generates -priam-obj-fontmgr.obj} R{un}tools-ChangeSeg obj-Quickglue{file to remap segments of} Y{es, map all segments}Graf{into segment Graf} R{un}tools-ChangeSeg obj-FontMgr{file to remap segments of} Y{es, map all segments}Graf{into segment Graf} $SUBMIT -priam-exec-link/grafsys $ENDEXEC $EXEC{make/hdisk, assembles the hard disk driver files, forming hdskdrvr.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}drivers-HDSKMAIN {no list file} obj-HDSKDRVR $ENDEXEC $EXEC{make/hdskdrvr, assembles the hard disk driver files, forming hdskdrvr.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}drivers-HDSKMAIN {no list file} obj-HDSKDRVR F{iler}C{opy}obj-hdskdrvr.obj mwdisk-hdskdrvr.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC n$EXEC{make/mapmmu, assembles the code to map MMU's, forming mapmmu.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-mapmmu {no list file} obj-mapmmu F{iler}C{opy}obj-mapmmu.obj mwdisk-mapmmu.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC $EXEC{make/miscsys, assembles the individual obj files, then links them to form miscsys.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-Packages {no list file} obj-Packages A{ssemble}source-SexyDate {no list file} obj-SexyDate $SUBMIT -priam-exec-link/miscsys $ENDEXEC $EXEC {make/monitor, assemble and link all of the source files for the MacWorks ROM image, then copy to mwdisk-monitor.obj} $SUBMIT -priam-exec-setprefix(-priam) {assemble and link all of the pieces} $SUBMIT exec-make/opsys $SUBMIT exec-make/grafsys $SUBMIT exec-make/toolsys $SUBMIT exec-make/miscsys {now build the Rom image} R{un}tools-BuildRom 3{build a rom}N{o, not big rom}Y{es, MW image}Y{es, default names}{continue} 4{write image out}{write to -priam-obj-romsys.obj} N{o, don't split, its a macworks image}1{quit buildrom} {finally copy to mwdisk} F{iler}C{opy}obj-romsys.obj mwdisk-monitor.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC $EXEC{make/mwdisk -- does everything needed to build a new macworks diskette} $SUBMIT -priam-exec-make/opsys $SUBMIT -priam-exec-make/grafsys $SUBMIT -priam-exec-make/toolsys $SUBMIT -priam-exec-make/miscsys $SUBMIT -priam-exec-make/drivers R{un}-priam-tools-BuildRom 3{build a rom}N{o, not big rom}Y{es, MW image}Y{es, default names}{continue} 4{write image out}{write to -priam-obj-romsys.obj} N{o, don't split, its a macworks image}1{quit buildrom} R{un}Transutil M{ount the monitor sony}C{opy}-priam-obj-romsys.obj #2:monitor.obj Y{es, overwrite the existing file} C{opy}-priam-obj-drivers.obj #2:drivers.obj Y{es, overwrite the existing file} C{opy}-priam-obj-hdskdrvr.obj #2:hdskdrvr.obj Y{es, overwrite the existing file} C{opy}-priam-mwdisk-revc/bootfiles.data #2:bootfiles.data Y{es, overwrite the existing file} Q{uit transutil} F{iler}U{nmount}lower {a space to handle the error} Q{uit the Filer} $ENDEXEC $EXEC{make/Opsys, assembles the individual obj files, then links them to form Opsys.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-START {no list file} obj-START A{ssemble}source-QUEUE {no list file} obj-QUEUE A{ssemble}source-INTHND {no list file} obj-INTHND A{ssemble}source-VBLCORE {no list file} obj-VBLCORE A{ssemble}source-CRSRCORE {no list file} obj-CRSRCORE A{ssemble}source-DISPATCH {no list file} obj-DISPATCH A{ssemble}source-IOCORE {no list file} obj-IOCORE A{ssemble}source-KBD {no list file} obj-KBD A{ssemble}source-SONY {no list file} obj-SONY A{ssemble}source-ASYNC {no list file} obj-ASYNC A{ssemble}source-HEAP {no list file} obj-HEAP A{ssemble}source-HEAPGUTS {no list file} obj-HEAPGUTS A{ssemble}source-BLOCKMOVE {no list file} obj-BLOCKMOVE A{ssemble}source-SOUND {no list file} obj-SOUND A{ssemble}source-EVENTS {no list file} obj-EVENTS A{ssemble}source-FS {no list file} obj-FS A{ssemble}source-SEGLOADER {no list file} obj-SEGLOADER A{ssemble}source-SYSUTIL {no list file} obj-SYSUTIL A{ssemble}source-RESTOP {no list file} obj-RESTOP $SUBMIT -priam-exec-link/opsys $ENDEXEC $EXEC{make/sonyutil, assembles the sony driver, forming sonyutil.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}drivers-sonyutil {no list file} obj-sonyutil F{iler}C{opy}obj-sonyutil.obj mwdisk-sonyutil.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC $EXEC{make/stub, assembles the lisabug stub, forming stub.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-stub {no list file} obj-stub F{iler}C{opy}obj-stub.obj mwdisk-stub.obj Y{es, overwrite existing file} Q{uit the Filer} $ENDEXEC $EXEC{make/toolsys, assembles the individual obj files, then links them to form toolsys.obj} $SUBMIT -priam-exec-setprefix(-priam) A{ssemble}source-ToolEvents {no list file} obj-ToolEvents A{ssemble}source-WMgr {no list file} obj-WMgr A{ssemble}source-MenuMgr {no list file} obj-MenuMgr A{ssemble}source-CMgr {no list file} obj-CMgr A{ssemble}source-RMgr {no list file} obj-RMgr A{ssemble}source-DMgr {no list file} obj-DMgr A{ssemble}source-Bytes {no list file} obj-Bytes A{ssemble}source-DeskMgr {no list file} obj-DeskMgr A{ssemble}source-GetMgr {no list file} obj-GetMgr A{ssemble}source-TE68K {no list file} obj-TE68K A{ssemble}source-SCRAP {no list file} obj-SCRAP $SUBMIT exec-link/toolsys $ENDEXEC ; FILE: MAPMMU.text ; MACWORKS Source ; ;********************************************************************************************* ; ; Sets up the Lisa MMU's so as MacWorks can function in a Mac-like environment. ; The Lisa uses the top seven bits of a 24 bit address to select an MMU segment (register). ; Thus each segment has a maximum mapable address space of 128K (17 bits of address). Each ; segment specifies an origin -- the true, on the address lines value to have as a base to add ; to the other 17 bits of address. Valid memory is mapped at boot time by the Lisa boot rom. ; For example, on a 1Meg Lisa, MMU segments 0..7 map 1 meg of valid memory to the correct address ; space. Additionally, segments 126 & 127 are used to map I/O address space (up around $FCC000 & ; $FE000, I think). Note that each segment can map from 0 to 128K of memory (doesn't have to be ; filled). Another point is that the control value of the segment specifies whether it's write- ; protected. This would be a handy feature to have for segment 32 (used by MacWorks to contain ; the Rom image). Also, with a 128K Rom, two segments will have to be used (one for the 128K of ; Rom data, and the other from the HardWare Interface globals and dispatch table). The only ; strange thing going on with the ROM segment is that the Rom boot code copies itself & all the ; rest of the Rom code (64K currently) into this segment. ; ; 1a) map all unused segments to Limbo - out beyond physical memory ; ; 1b) Compute the top of this code rounded downward to nearest multiple of 512 ; 2b) Allocate $11400 = $10000+$1000+$400 for ROM, Drivers and Dispatch Table ; 3b) Map these into segment 32 (how does address get set to $400000 for MMU seg??? ; ; NOTE: For a developer's MacWorks, all that is required is to map the origin of the Limbo ; segs to be (Origin of seg 0) + 4Meg. This will ensure that any address where the top seven ; bits don't specify a valid memory segment will generate a bus error. ; ; Modified 8-Feb-85 KWK Change limbo mapping to allow .5,1,2,4...MegaByte mem configs ; ;********************************************************************************************* .PROC MAPMMU,0 CLR.W -(A7) ; allocate space for Limb 6(A2) CLR.W -(A7) ; allocate space for ORG 4(A2) CLR.W -(A7) ; allocate space for LIMIT 2(A2) CLR.W -(A7) ; allocate space for CNTL 0(A2) MOVE.L A7,A2 CLR.W -(A7) ; push seg = zero MOVE.B #1,-(A7) ; push RW = read PEA 4(A2) ; push @ORG PEA 2(A2) ; push @LIMIT MOVE.L A2,-(A7) ; push @CNTL BSR RWMMU ; READ SEGMENT #0 MOVE.L A7,A2 ; initialize all unused segments to Limbo Limbo MOVE.L #$7C,D3 ; init segments from 124 down to last seg used for valid memory mapping MOVE.W 4(A2),6(A2) ; set origin of all limbo segs to origin of seg 0 (for now) ; initialize values for mappings on 1/2 meg system MOVE.W #$400,D0 ; set limbo origin to 1/2 meg past true origin ($400 blocks x 512 bytes/block) MOVE.L #$80000,D1 ; set memory limit for 1/2 meg MOVEQ #4,D4 ; set number of segs used for true mapping to 4 (1/2 meg mapped) LEA Limbo,A0 ; need to figure extent of real world, set A0 to loc of this code ; loop here until the memory limit checks below the location of this piece of code (Limbo) @0 CMP.L D1,A0 ; are we on a 1/2, 1, 2 or 4 meg system? BLT.S InitSeg ; @Limbo < test address, so go use current D0/D4 for seg mapping LSL.W #1,D0 ; double Limbo origin LSL.L #1,D1 ; double memory limit check LSL.W #1,D4 ; double number of segs used in valid mapping BRA.S @0 ; and loop ; now, for every segment from 124 down to the first segment not used to map valid memory, set the ; origin to be past valid memory and the limit/control to be the same as for seg 0. Enter with ; D0 = number of blocks (each 512 bytes) used by valid memory, D4 = number of segments (0..n) used ; to map valid memory (ie. also equal to first unused segment) InitSeg ADD.W D0,6(A2) ; adjust limbo origin to be past valid memory MOVE.W D3,-(A7) ; push seg CLR.B -(A7) ; push RW = write PEA 6(A2) ; push @Limbo PEA 2(A2) ; push @LIMIT PEA (A2) ; push @CNTL BSR.S RWMMU ; write segment CMP.W D4,D3 ; did we just init the last segment DBEQ D3,InitSeg ; init the next segment ; check and see if we will be running with LisaBug. If we are not, reclaim the ; space it would have taken..... including the alternate screen memory. ST $120 ; assume LisaBug exists MOVE.L $610,D0 ; get base address of alternate screen SUB.L #$4000,D0 ; subtract a fudge factor CMP.L $10C,D0 ; is the top of code within E16K bytes of screen? BGT.S @1 ; if not, we must have a debugger MOVE.L $660,D0 ; ... otherwise MOVE.L D0,$10C ; adjust our top of code ptr to bottom of screen. MOVE.L D0,$610 ; point the alt screen to the primary screen SF $120 ; let LisaMac know LisaBugs not here @1 MOVE.L $10C,D0 ; save the top of this code AND.L #$FFFE00,D0 ; Round down to nearest multiple of 512 SUB.L #$11400,D0 ; Allocate space for ROM+globals+DispTable MOVE.L D0,$10C LSR.L #8,D0 ; Convert to blocks of 512 bytes each LSR.L #1,D0 ADD.W D0,4(A2) ; Adjust origin relative to 0 MOVE.W #$76,2(A2) ; Set limit to 64k+4k+1k bytes MOVE.W #$20,-(A7) ; push seg = 32 CLR.B -(A7) ; push RW = write PEA 4(A2) ; push @ORG PEA 2(A2) ; push @LIMIT MOVE.L A2,-(A7) ; push @CNTL BSR.S RWMMU ; WRITE SEGMENT #32 ADD.W #8,A7 ; Discard the variables RTS ;********************************************************************************************* ; ; PROCEDURE RWMMU(SEG:INTEGER; ; RW:BOOLEAN; ; VAR ORG:INTEGER; ; VAR LIMIT:INTEGER; ; VAR CNTL:INTEGER); ; ; Stack: ; ; 22 segment# ; 20 readwrt read is true ; 16 @org ; 12 @limit ; 8 @cntrl ; 4 Return Address ; 0 Old A6 ; ;********************************************************************************************* RWMMU BRA.S GO MMURD TST.B $FCE010 ; set the SETUP bit MOVE.L D0,A0 ; MMU cntrl/limit address MOVE.W (A0),D2 ; get cntrl/limit value ADD.W #8,A0 ; MMU origin register MOVE.L (A0),D1 ; get origin value TST.B $FCE012 ; clear the SETUP bit RTS MMUWRT TST.B $FCE010 ; set the SETUP bit MOVE.L D0,A0 ; MMU cntrl/limit address MOVE.W D2,(A0) ; get cntrl/limit value ADD.W #8,A0 ; MMU origin register MOVE.L D1,(A0) ; get origin value TST.B $FCE012 ; clear the SETUP bit ALL RTS COUNT .EQU ALL-MMURD+2 MRD .EQU 0 MWT .EQU MMUWRT-MMURD WHERE .EQU $4000 GO LINK A6,#-COUNT MOVEM.L D3/A2,-(A7) TST.B $64C ; LISA ? BNE @50 MOVE SR,D3 ; save SR ORI.W #$0700,SR ; disable interrupts MOVE.L #WHERE,A0 ; save bytes from $4000 LEA MMURD,A2 ; code to transfer MOVE.W #COUNT/2-1,D0 LEA -COUNT(A6),A1 @5 MOVE.W (A0),(A1)+ ; save it MOVE.W (A2)+,(A0)+ ; replace it DBF D0,@5 MOVE.L #WHERE,A1 ; MMURD MOVE.W 22(A6),D0 ; get segment number ADD.W D0,D0 SWAP D0 ; segment num * 2 in high word MOVE.W #$8000,D0 ; make sure low part is $8000 TST.B 20(A6) ; is it a read BNE.S @10 ; yes MOVE.L 16(A6),A0 ; set up for a Write MOVE.W (A0),D1 ; get the origin into D0 MOVE.L 8(A6),A0 MOVE.B 1(A0),-(A7) ; get the access control bits in high MOVE.W (A7)+,D2 ; byte low word MOVE.L 12(A6),A0 MOVE.B 1(A0),D2 ; get the limit in low ADD.W #MWT,A1 JSR (A1) ; do a write BRA.S @30 @10 JSR (A1) ; do a read AND.W #$FFF,D1 ; mask lower 12 bits MOVE.L 16(A6),A0 MOVE.W D1,(A0) ; save the origin MOVE.W D2,-(A7) MOVE.B (A7)+,D0 AND.W #$F,D0 MOVE.L 8(A6),A0 MOVE.W D0,(A0) ; save the control bits AND.W #$FF,D2 MOVE.L 12(A6),A0 MOVE.W D2,(A0) ; save the limit @30 MOVE.L #WHERE,A0 ; restore bytes to $4000 MOVE.W #COUNT/2-1,D0 LEA -COUNT(A6),A1 @40 MOVE.W (A1)+,(A0)+ DBF D0,@40 MOVE D3,SR ; restore SR BRA.S @60 @50 TST.B 20(A6) ; read ? BEQ.S @60 MOVE.W 22(A6),D0 ; get segment number MULS #$100,D0 MOVE.L 16(A6),A0 MOVE.W D0,(A0) ; save the origin MOVE.L 12(A6),A0 MOVE.W #0,(A0) ; save the limit MOVE.L 8(A6),A0 MOVE.W #7,(A0) ; save the control bits @60 MOVEM.L (A7)+,D3/A2 UNLK A6 MOVE.L (A7)+,A0 ADD.W #16,A7 JMP (A0) .END ; FILE: MAPMMU.text ; MACWORKS Source ; ;********************************************************************************************* ; ; Sets up the Lisa MMU's so as MacWorks can function in a Mac-like environment. ; The Lisa uses the top seven bits of a 24 bit address to select an MMU segment (register). ; Thus each segment has a maximum mapable address space of 128K (17 bits of address). Each ; segment specifies an origin -- the true, on the address lines value to have as a base to add ; to the other 17 bits of address. Valid memory is mapped at boot time by the Lisa boot rom. ; For example, on a 1Meg Lisa, MMU segments 0..7 map 1 meg of valid memory to the correct address ; space. Additionally, segments 126 & 127 are used to map I/O address space (up around $FCC000 & ; $FE000, I think). Note that each segment can map from 0 to 128K of memory (doesn't have to be ; filled). Another point is that the control value of the segment specifies whether it's write- ; protected. This would be a handy feature to have for segment 32 (used by MacWorks to contain ; the Rom image). Also, with a 128K Rom, two segments will have to be used (one for the 128K of ; Rom data, and the other from the HardWare Interface globals and dispatch table). The only ; strange thing going on with the ROM segment is that the Rom boot code copies itself & all the ; rest of the Rom code (64K currently) into this segment. ; ; 1a) map all unused segments to Limbo - out beyond physical memory ; ; 1b) Compute the top of this code rounded downward to nearest multiple of 512 ; 2b) Allocate $11400 = $10000+$1000+$400 for ROM, Drivers and Dispatch Table ; 3b) Map these into segment 32 (how does address get set to $400000 for MMU seg??? ; ; NOTE: For a developer's MacWorks, all that is required is to map the origin of the Limbo ; segs to be (Origin of seg 0) + 4Meg. This will ensure that any address where the top seven ; bits don't specify a valid memory segment will generate a bus error. ; ; Modified 8-Feb-85 KWK Change limbo mapping to allow .5,1,2,4...MegaByte mem configs ; ;********************************************************************************************* .PROC MAPMMU,0 CLR.W -(A7) ; allocate space for Limb 6(A2) CLR.W -(A7) ; allocate space for ORG 4(A2) CLR.W -(A7) ; allocate space for LIMIT 2(A2) CLR.W -(A7) ; allocate space for CNTL 0(A2) MOVE.L A7,A2 CLR.W -(A7) ; push seg = zero MOVE.B #1,-(A7) ; push RW = read PEA 4(A2) ; push @ORG PEA 2(A2) ; push @LIMIT MOVE.L A2,-(A7) ; push @CNTL BSR RWMMU ; READ SEGMENT #0 MOVE.L A7,A2 ; initialize all unused segments to Limbo Limbo MOVE.L #$7C,D3 ; init segments from 124 down to last seg used for valid memory mapping MOVE.W 4(A2),6(A2) ; set origin of all limbo segs to origin of seg 0 (for now) ; initialize values for mappings on 1/2 meg system MOVE.W #$400,D0 ; set limbo origin to 1/2 meg past true origin ($400 blocks x 512 bytes/block) MOVE.L #$80000,D1 ; set memory limit for 1/2 meg MOVEQ #4,D4 ; set number of segs used for true mapping to 4 (1/2 meg mapped) LEA Limbo,A0 ; need to figure extent of real world, set A0 to loc of this code ; loop here until the memory limit checks below the location of this piece of code (Limbo) @0 CMP.L D1,A0 ; are we on a 1/2, 1, 2 or 4 meg system? BLT.S InitSeg ; @Limbo < test address, so go use current D0/D4 for seg mapping LSL.W #1,D0 ; double Limbo origin LSL.L #1,D1 ; double memory limit check LSL.W #1,D4 ; double number of segs used in valid mapping BRA.S @0 ; and loop ; now, for every segment from 124 down to the first segment not used to map valid memory, set the ; origin to be past valid memory and the limit/control to be the same as for seg 0. Enter with ; D0 = number of blocks (each 512 bytes) used by valid memory, D4 = number of segments (0..n) used ; to map valid memory (ie. also equal to first unused segment) InitSeg ADD.W D0,6(A2) ; adjust limbo origin to be past valid memory MOVE.W D3,-(A7) ; push seg CLR.B -(A7) ; push RW = write PEA 6(A2) ; push @Limbo PEA 2(A2) ; push @LIMIT PEA (A2) ; push @CNTL BSR.S RWMMU ; write segment CMP.W D4,D3 ; did we just init the last segment DBEQ D3,InitSeg ; init the next segment ; check and see if we will be running with LisaBug. If we are not, reclaim the ; space it would have taken..... including the alternate screen memory. ST $120 ; assume LisaBug exists MOVE.L $610,D0 ; get base address of alternate screen SUB.L #$4000,D0 ; subtract a fudge factor CMP.L $10C,D0 ; is the top of code within E16K bytes of screen? BGT.S @1 ; if not, we must have a debugger MOVE.L $660,D0 ; ... otherwise MOVE.L D0,$10C ; adjust our top of code ptr to bottom of screen. MOVE.L D0,$610 ; point the alt screen to the primary screen SF $120 ; let LisaMac know LisaBugs not here @1 MOVE.L $10C,D0 ; save the top of this code AND.L #$FFFE00,D0 ; Round down to nearest multiple of 512 SUB.L #$11400,D0 ; Allocate space for ROM+globals+DispTable MOVE.L D0,$10C LSR.L #8,D0 ; Convert to blocks of 512 bytes each LSR.L #1,D0 ADD.W D0,4(A2) ; Adjust origin relative to 0 MOVE.W #$76,2(A2) ; Set limit to 64k+4k+1k bytes MOVE.W #$20,-(A7) ; push seg = 32 CLR.B -(A7) ; push RW = write PEA 4(A2) ; push @ORG PEA 2(A2) ; push @LIMIT MOVE.L A2,-(A7) ; push @CNTL BSR.S RWMMU ; WRITE SEGMENT #32 ADD.W #8,A7 ; Discard the variables RTS ;********************************************************************************************* ; ; PROCEDURE RWMMU(SEG:INTEGER; ; RW:BOOLEAN; ; VAR ORG:INTEGER; ; VAR LIMIT:INTEGER; ; VAR CNTL:INTEGER); ; ; Stack: ; ; 22 segment# ; 20 readwrt read is true ; 16 @org ; 12 @limit ; 8 @cntrl ; 4 Return Address ; 0 Old A6 ; ;********************************************************************************************* RWMMU BRA.S GO MMURD TST.B $FCE010 ; set the SETUP bit MOVE.L D0,A0 ; MMU cntrl/limit address MOVE.W (A0),D2 ; get cntrl/limit value ADD.W #8,A0 ; MMU origin register MOVE.L (A0),D1 ; get origin value TST.B $FCE012 ; clear the SETUP bit RTS MMUWRT TST.B $FCE010 ; set the SETUP bit MOVE.L D0,A0 ; MMU cntrl/limit address MOVE.W D2,(A0) ; get cntrl/limit value ADD.W #8,A0 ; MMU origin register MOVE.L D1,(A0) ; get origin value TST.B $FCE012 ; clear the SETUP bit ALL RTS COUNT .EQU ALL-MMURD+2 MRD .EQU 0 MWT .EQU MMUWRT-MMURD WHERE .EQU $4000 GO LINK A6,#-COUNT MOVEM.L D3/A2,-(A7) TST.B $64C ; LISA ? BNE @50 MOVE SR,D3 ; save SR ORI.W #$0700,SR ; disable interrupts MOVE.L #WHERE,A0 ; save bytes from $4000 LEA MMURD,A2 ; code to transfer MOVE.W #COUNT/2-1,D0 LEA -COUNT(A6),A1 @5 MOVE.W (A0),(A1)+ ; save it MOVE.W (A2)+,(A0)+ ; replace it DBF D0,@5 MOVE.L #WHERE,A1 ; MMURD MOVE.W 22(A6),D0 ; get segment number ADD.W D0,D0 SWAP D0 ; segment num * 2 in high word MOVE.W #$8000,D0 ; make sure low part is $8000 TST.B 20(A6) ; is it a read BNE.S @10 ; yes MOVE.L 16(A6),A0 ; set up for a Write MOVE.W (A0),D1 ; get the origin into D0 MOVE.L 8(A6),A0 MOVE.B 1(A0),-(A7) ; get the access control bits in high MOVE.W (A7)+,D2 ; byte low word MOVE.L 12(A6),A0 MOVE.B 1(A0),D2 ; get the limit in low ADD.W #MWT,A1 JSR (A1) ; do a write BRA.S @30 @10 JSR (A1) ; do a read AND.W #$FFF,D1 ; mask lower 12 bits MOVE.L 16(A6),A0 MOVE.W D1,(A0) ; save the origin MOVE.W D2,-(A7) MOVE.B (A7)+,D0 AND.W #$F,D0 MOVE.L 8(A6),A0 MOVE.W D0,(A0) ; save the control bits AND.W #$FF,D2 MOVE.L 12(A6),A0 MOVE.W D2,(A0) ; save the limit @30 MOVE.L #WHERE,A0 ; restore bytes to $4000 MOVE.W #COUNT/2-1,D0 LEA -COUNT(A6),A1 @40 MOVE.W (A1)+,(A0)+ DBF D0,@40 MOVE D3,SR ; restore SR BRA.S @60 @50 TST.B 20(A6) ; read ? BEQ.S @60 MOVE.W 22(A6),D0 ; get segment number MULS #$100,D0 MOVE.L 16(A6),A0 MOVE.W D0,(A0) ; save the origin MOVE.L 12(A6),A0 MOVE.W #0,(A0) ; save the limit MOVE.L 8(A6),A0 MOVE.W #7,(A0) ; save the control bits @60 MOVEM.L (A7)+,D3/A2 UNLK A6 MOVE.L (A7)+,A0 ADD.W #16,A7 JMP (A0) .END p;File MENUASM1.TEXT ;---------------------------------------------------------- ; ; Menu Manager for MacIntosh User Interface ToolBox ; ; written by Andy Hertzfeld June 29, 1982 ; ; (c) 1982 by Apple Computer, Inc. All rights reserved. ; ; This file contains the source code for the ; MacIntosh menu manager, which is based on Bill Atkinson's ; Lisa menu manager. ; ; MODIFICATION HISTORY: ; ; 24-Jul-82 AJH Fixed left corner problem in DrawMenuBar ; 02-Aug-82 AJH Converted for new memory manager; no more oh's! ; 07-Sep-82 AJH Made DrawMBar a separate routine to save code ; 20-Sep-82 AJH Fixed problem in DeleteMenu ; 10-Oct-82 AJH Converted to QuickDraw Trap Interface ; 16-Oct-82 AJH Made it independent of screen size (use wmgr^.portRect) ; 25-Oct-82 AJH Made space between menus wider for 512 dots (12 -> 18) ; 16-Nov-82 AJH Made the menuBar 4 pixels wider ; 11-Dec-82 AJH Made space between menus less wide (14 pixels) ; 27-Dec-82 AJH Moved menuData to end of block; defProc as resource ; 20-Jan-83 AJH Fixed purged defProc problem (lock after LoadResource) ; 24-Jan-83 AJH Added support for deskOrns stealing the menuBar ; 25-Jan-83 AJH Fixed MenuSelect mouse-ahead problem ; 28-Jan-83 AJH Added FlashMenuBar ; 15-Mar-83 AJH Made Enable/DisableItem not recalc menu size ; 18-Mar-83 AJH Made InvertTitle go one pixel higher ; 04-Apr-83 AJH Made it get menuFlash from parameter RAM ; 13-Apr-83 AJH Tweaked MBarEnable logic in MenuSelect ; 13-Aug-83 SC The code scrunch-a-matic pass... ; Made NoIndex fall into GotIndex ; Moved getIndex, invertTitle ; Added utility GetNInvert ; Made saveBits inline ; 19-Aug-83 AJH Moved loc of menu flash bits. ; 27-Aug-83 AJH handled out of memory case in SaveBits by deep-shitting ; 07-Sep-83 AJH save in DrawMBar ; ;------------------------------------------------------------------------------- ; ; PROCEDURE InitMenus -- initialize the menu manager by allocating an empty menuList ; INITMENUS BSR.S ALLOCMLIST ;allocate and initialize a menuList block MOVE.L A0,MENULIST ;make it the current menuList ; MOVEQ #$0C,D0 ;only use 2 bits worth AND.B SPMisc2,D0 ;get pRam flags LSR #2,D0 ;get menuFlash in the low bits MOVE D0,MenuFlash ;set up menuFlash field CLR.W MBarEnable ;initally, application owns MBar CLR.L MRMACHOOK ;init Mr. Mac hook CLR.L MENUHOOK ;init user hook _ClearMenuBar ;initialize menuBar parameters and clear it _DrawMenuBar RTS ; ; AllocMList is a utility that allocates a menuList and initializes it. It passes back a ; menuList handle in A0. ; ALLOCMLIST MOVEQ #MLISTSIZE,D0 ;get size of menuList data structure _NEWHANDLE ;allocate one ALLOC1 MOVE.L (A0),A1 ;dereference handle CLR.W (A1)+ ;lastMenu initialized to zero MOVE.W #10,(A1)+ ;lastRight initialized to ten CLR.W (A1)+ ;skip to 1st entry ; MOVEQ #23,D0 ;24 longs = 16*6 bytes in menuList @1 CLR.L (A1)+ ;initialize one entry DBRA D0,@1 ;loop till done ; RTS ; ; PROCEDURE ClearMenuBar -- clear the menuBar/menuList data structure ; CLEARMENUBAR MOVE.L MENULIST,A0 ;get handle to menuList BSR.S ALLOC1 ;clear it out CLR.W THEMENU ;no menu is hilited RTS ; ; PROCEDURE InsertMenu(menu: menuInfoHandle; beforeId: INTEGER); ; ; insert a menu into the menuList. beforeID is the menuID of the menu ; that the new menu should be inserted before. If beforeID can't be found, ; the new menu is inserted at the end of the list. If the menu is already ; in the list, don't insert it ; INSERTMENU MOVEM.L D3-D4/A2-A4,-(SP) ;save working registers ; MOVE.L MENULIST,A3 ;get menuList handle MOVE.L (A3),A3 ;get menuList pointer ; CMP #MAXMENU,LASTMENU(A3) ;is the menubar all full? BGE.S DONEINSERT ;if so, give up ; MOVE.L 26(SP),A4 ;get menuInfoHandle MOVE.L (A4),A2 ;get ptr to menu block MOVE MENUID(A2),D1 ;get menuId of menu to be installed BSR.S GETINDEX ;is it already in the list? BNE.S DONEINSERT ;don't insert same one twice ; JSR SETWPORT ;get into the window manager port CLR.L -(SP) ;make sure we use the system font _TextFont ;set the font to the system font _TextFace ;set textface to normal ; CLR.W -(SP) ;make room for function result PEA MenuData(A2) ;push pointer to title string _StringWidth ;figure out the width MOVEQ #13,D3 ;keep title width in D3 ADD.W (SP)+,D3 ;force some space in between (tweak) MOVE.W 24(SP),D1 ;get beforeID BSR.S GETINDEX ;get index into menuList of beforeID menu BNE.S INSERTFOUND ;if we found one, go move em down ; ; the "beforeID" menu was not in the list, so insert this one at the end of the list ; MOVE LASTMENU(A3),D0 ;get # of menus in menuList ADDQ #6,D0 ;there's one more now MOVE LASTRIGHT(A3),D1 ;D1 holds the newLeft BRA.S GOINSERT ;go insert it at the end ; ; D0 points to the "beforeID" menu, so move it and all those after it down one slot ; INSERTFOUND MOVE MENULEFT(A3,D0),D1 ;get newLeft := menuLeft MOVE LASTMENU(A3),D2 ;get # of menus @1 MOVE.L 0(A3,D2),6(A3,D2) ;move entry down one slot MOVE.W 4(A3,D2),10(A3,D2) ;move rest of entry ADD D3,10(A3,D2) ;menuLeft := menuLeft+titleWidth SUBQ #6,D2 ;move up one slot CMP D0,D2 ;are we done yet? BGE.S @1 ;loop until we are ; ; insert the new menu in the list ; GOINSERT ADD D3,LASTRIGHT(A3) ;bump lastRight by titleWidth MOVE.L A4,MENUOH(A3,D0) ;move offset handle into menuList MOVE D1,MENULEFT(A3,D0) ;move in new left for this slot ADDQ #6,LASTMENU(A3) ;bump total number of menus ; JSR RESTOREPORT ;restore original grafPort ; DONEINSERT MOVEM.L (SP)+,D3-D4/A2-A4 ;restore registers SIXBYTEXIT MOVE.L (SP)+,A0 ;get return address ADDQ #6,SP ;strip parameters JMP (A0) ;return to caller ; ; GetIndex is a utility routine that returns an index into the menuList given a ; menuId. The menuId is passed in D1 while the index is returned in D0. If the ; menuId can't be found, D0 will return a zero. A0, A1 and D2 are trashed. ; The Z-flag can be used to determine if one was found. As a special bonus, on ; exit A0 points to the menu, if a menu was found. ; GETINDEX TST D1 ;is index 0? BEQ.S NOINDEX ;if so, return 0 MOVE.L MENULIST,A1 ;get menuList handle MOVE.L (A1),A1 ;handle -> pointer MOVE LASTMENU(A1),D2 ;get # of menus BEQ.S NOINDEX ;if none, say we can't find it ; ; here is the search loop. Get the next handle, and check the menuID ; GILOOP MOVE.L MENUOH(A1,D2),D0 ;get the handle BEQ.S NEXTINDEX ;skip if the slot is empty MOVE.L D0,A0 ;get handle in A-reg MOVE.L (A0),A0 ;handle -> pointer CMP MENUID(A0),D1 ;is this the one? BEQ.S GOTINDEX ;if they match, we're done ; NEXTINDEX SUBQ #6,D2 ;bump to next entry BNE.S GILOOP ;loop if there's more to do ; ; we couldn't find it so return zero in D0 ; NOINDEX MOVEQ #0,D2 ;return 0 ; ; we found it so return index in D0 ; GOTINDEX MOVE D2,D0 ;get index in D0 RTS ; ; PROCEDURE DeleteMenu(menuID: INTEGER) -- delete a given menu from the menuList. ; Note that deleteMenu will not de-allocate the menu, since it may be in other ; menuLists. ; DELETEMENU MOVE.W 4(SP),D1 ;get the menuID BSR GETINDEX ;get the index into D0 BEQ.S TWOBYTEXIT ;if not found, there's nothing to delete ; MOVE.L MENULIST,A1 ;get menuList handle MOVE.L (A1),A1 ;A1 points to the menuList ; CMP.W LASTMENU(A1),D0 ;is this one the last one? BNE.S @1 ;if not, go compute deleteWidth ; ; special case deleting the last menu, making lastRight the old menuLeft ; MOVE.W MenuLeft(A1,D0),LASTRIGHT(A1) ;set new lastRight BRA.S OneLessMenu ;go finish up by decrementing count ; ; its not the last item so we have to move all later one up ; @1 MOVE MENULEFT+6(A1,D0),D2 ;get menuLeft of menu to right SUB MENULEFT(A1,D0),D2 ;compute deleteWidth SUB D2,LASTRIGHT(A1) ;lastRight := lastRight - deleteWidth ; ; here is the loop to move all the menus after the deleted one up one slot, adjusting ; their menuLeft fields ; DELETELOOP MOVE.L 6(A1,D0),0(A1,D0) ;move next one into current slot MOVE.W 10(A1,D0),4(A1,D0) ;move rest of it SUB D2,MENULEFT(A1,D0) ;menuLeft := menuLeft - deleteWidth ADDQ #6,D0 ;bump to next menu slot CMP (A1),D0 ;are we done yet? BLE.S DELETELOOP ;loop till done ; OneLessMenu SUBQ #6,LASTMENU(A1) ;one less menu in list TWOBYTEXIT MOVE.L (SP)+,A0 ;get return address ADDQ #2,SP ;strip parameter JMP (A0) ;return to caller ; ; GETITLERECT is a utility routine that computes the rectangle bounding a menuTitle ; given a menuList pointer in A1, and and an index in D0 ; GETITLERECT LEA TEMPRECT,A0 ;get address of the rectangle MOVE #1,(A0)+ ;top is one MOVE MENULEFT(A1,D0),(A0) ;left is menuLeft[index] SUBQ #1,(A0)+ ;really want left-1 MOVE #19,(A0)+ ;bottom is 19 ; CMP LASTMENU(A1),D0 ;is it the last one? BEQ.S @1 ;if so, use LastRight ; ADDQ #6,D0 ;bump to next slot MOVE.W MENULEFT(A1,D0),(A0) ;right is menuLeft[index+1] BRA.S @2 ;return to caller ; @1 MOVE.W LASTRIGHT(A1),(A0) ;right is lastRight @2 ADDQ #4,(A0) ;leave some margin on right RTS ;return to caller ; ; Utility DrawMBar -- clear the menuBar and draw the line underneath it, leaving ; the wmgrPort clipRgn clipped to it. This is a separate routine so InitWindows ; can call it to save code. ; DrawMBar MOVE.L WMGRPORT,A3 ;get pointer to the window manager port MOVE.L CLIPRGN(A3),-(SP) ;push a pointer to the clipRgn CLR.L -(SP) ;topLeft is (0,0) MOVE.W PortRect+Right(A3),-(SP) ;push right of screen MOVE.W #$0014,-(SP) ;bottom is 20 _SetRecRgn ;clip to the menu bar ; PEA PORTRECT(A3) ;push a pointer to full screen rectangle MOVE.L #SCREENRADIUS,-(SP) ;push roundRect parameter _EraseRoundRect ;paint the menuBar white ; MOVE.W PortRect+Right(A3),-(SP) ;push extreme right MOVEQ #19,D0 ;get bottom of menu bar MOVE.W D0,-(SP) ;push for later lineTo SWAP D0 ;get into vertical postion MOVE.L D0,-(SP) ;draw the line under the menu bar _MoveTo ;move to left of line _LineTo ;draw the line RTS ; ; PROCEDURE DrawMenuBar -- draw the current menuList on the menuBar ; DRAWMENUBAR MOVEM.L D3-D5/A3-A4,-(SP) ;save work registers JSR SETWPORT ;enter the window manager port ; BSR.S DrawMBar ;draw the clear menuBar JSR FullClip ;restore full clipping ; MOVEQ #6,D3 ;start with first (leftmost) entry MOVE.L MENULIST,A3 ;get handle to menuList MOVE.L (A3),A3 ;get pointer to menuList TST.W (A3) ;any menus in the menuList? BEQ.S SKIPMDRAW ;if not, we're done ; ; here is the loop that draws the titles on the menu bar. It handles hiliting ; and graying out. ; DRAWMLOOP MOVE MENULEFT(A3,D3),-(SP) ;x coordinate is menuLeft[index]+8 ADDQ #8,(SP) ;indent a little MOVE #14,-(SP) ;y coordinate is 14 _MoveTo ;position the point to start drawing ; MOVE D3,D5 ;get index in D5 BSR GETMENUPTR ;get menu pointer in A0 MOVE.L A0,A4 ;save in A4 PEA MenuData(A4) ;push pointer to title string _DrawString ;draw the title ; BTST #0,MENUENABLE+3(A4) ;is the title enabled? BEQ.S DISBLTITLE ;if not, go gray it out ; MOVE THEMENU,D0 ;get ID of hilited menu CMP MENUID(A4),D0 ;should this one be hilited? BNE.S NEXTDRAW ;if not, go process next menu ; MOVE D3,D0 ;get index of menu to invert BSR INVERTTITLE ;invert it BRA.S NEXTDRAW ;go process the next menu ; ; the menu is disabled so gray out its title ; DISBLTITLE MOVE D3,D0 ;get index in D0 MOVE.L A3,A1 ;get menuList pointer in A1 BSR.S GETITLERECT ;build the rectangle BSR.S GRAYRECT ;dim a rectangle by masking with gray ; ; we're done processing one menu -- bump to the next one and loop if there's more to do ; NEXTDRAW ADDQ #6,D3 ;bump index to next in list CMP (A3),D3 ;are we done? BLE.S DRAWMLOOP ;loop till we're done ; SKIPMDRAW JSR RESTOREPORT ;restore original grafPort MOVEM.L (SP)+,D3-D5/A3-A4 ;restore work registers RTS ;return to caller ; ; GrayRect is a utility that bit-clears the current "tempRect" with gray ; GRAYRECT MOVE #PATBIC,-(SP) ;set bit clear mode _PenMode ;set the penMode PEA WMgrGray ;push gray _PenPat ;make that the pen pattern PEA TempRect ;push the rectangle _PaintRect ;bit clear with gray _PenNormal ;restore the pen to normal RTS ; ; PROCEDURE HiliteMenu(menuID: INTEGER); -- hilite the title of the menu with the ; specified menuID. Since only one menu can be hilited at a time, unhilite any other one ; HILITEMENU MOVE.L D4,-(SP) ;save work register JSR SETWPORT ;get into the window manager port BSR FULLCLIP ;make sure clipping is wide open ; MOVE THEMENU,D1 ;which one is hilited now? CMP 8(SP),D1 ;is it the parameter? BEQ.S DONEHILITE ;if so, nothing to do ; BSR.S GetNInvert ; get index of old an un-hilite ; @1 MOVE 8(SP),D1 ;get new menuID MOVE D1,THEMENU ;update current hilited field BSR.S GetNInvert ; get index of new an hilite ; DONEHILITE JSR RESTOREPORT ;restore original grafPort MOVE.L (SP)+,D4 ;restore D4 BRA TWOBYTEXIT ;standard exit saves code ; ; PROCEDURE EnableItem(menuHandle,item) -- enable an item in a menu or, if the item ; number is zero, enable the entire menu. It has no effect if the item is already ; enabled. ; ENABLEITEM BSR.S ENABLEFETCH ;set up registers with shared code BSET D0,D1 ;enable the item ENABLECOM MOVE.L D1,MENUENABLE(A0) ;update the menu's flags BRA SIXBYTEXIT ;common exit saves code ; ENABLEFETCH MOVE.L 10(SP),A0 ;get the menu handle MOVE.L (A0),A0 ;handle to pointer MOVE 8(SP),D0 ;get bit index MOVE.L MENUENABLE(A0),D1 ;pick up current state of flags RTS ; ; PROCEDURE DisableItem(menuHandle, item) -- disable an item in a menu, or if the item ; is zero, disable the entire menu. Shares code with EnableItem to stay small ; DISABLEITEM BSR.S ENABLEFETCH ;set up registers BCLR D0,D1 ;disable the item BRA.S ENABLECOM ;go update flags and return ; ; GetMenuPtr is a utility routine that given an index into the current menuList in D5, ; it returns a pointer to the menu in A0. It returns a handle to the same menu in A1. ; GETMENUPTR MOVE.L MENULIST,A0 ;get menuList handle MOVE.L (A0),A0 ;handle -> pointer MOVE.L MENUOH(A0,D5),A1 ;get menu handle MOVE.L (A1),A0 ;handle -> pointer adios RTS ; ; ; Freindly utility to do this common sequence GetNInvert BSR GetIndex ; get the index BEQ.S adios ; Fall into InvertTitle ; ; InvertTitle is a utility routine that is called when in the wmgrPort. Invert the ; title of the menu index pointed to by D0. A0,A1,D0,D1 are trashed ; INVERTTITLE MOVE.L MENULIST,A1 ;get menuList handle MOVE.L (A1),A1 ;get menuList pointer CMP LASTMENU(A1),D0 ;is it a valid one? BGT.S DONETINVERT ;if too big, its no good AND #$FFFE,D0 ;avoid embarrasing address errors BEQ.S DONETINVERT ;if its zero, don't do anything ; BSR.S GETITLERECT ;get the rectangle bounding title PEA TEMPRECT ;push a pointer to it _InverRect ;invert it DONETINVERT RTS ;return to caller ; ; FUNCTION MenuSelect(mousePt:Point): LongInt -- MenuSelect is the most important ; routine in the menu manager. It is called when the application receives a ; mouse button down event on the menuBar and retains control until the mouse button ; goes up. It returns a long integer contain 2 16 bit values. The high word ; is called "whichMenu" and holds the menuId of the menu that was chosen; if its ; zero, no menu was chosen. The low word is called "whichItem" and contains the item ; number of the selected item. If no choice was selected, the longWord result is zero ; ; ; MenuSelect stack frame definitions ; STARTPT .EQU 8 ;1st parameter is 8, and is 4 bytes long FUNCRESULT .EQU 12 ;longword function result (menu,item) ITEMRESULT .EQU 14 ;item portion of funcResult ; MSavePort .EQU -32 ;saved grafPort MENURECT .EQU -28 ;rectangle (8 bytes) used by menu routines SAVEDBASE .EQU MENURECT+8 ;base address for bitMap SAVEDROWBYTES .EQU SAVEDBASE+4 ;rowBytes for bitmap SAVEBOUNDS .EQU SAVEDROWBYTES+2 ;bounds rect for bitmap ; MENUSELECT LINK A6,#-32 ;set up a stack frame MOVEM.L D3-D7/A3,-(SP) ;save work registers ;must save D7 for DoXfer ; CLR.L -(SP) ;push zero for this and stilldown _HiliteMenu ;make sure no menu is hilited CLR.L FUNCRESULT(A6) ;assume nothing will be selected ; _WaitMouseUp ;is the mouse still down? TST.B (SP)+ ;examine result BEQ DONEMSELECT ;if not StillDown, dont bother ; PEA MSavePort(A6) _GetPort MOVE.L WmgrPort,-(SP) ;get into the window manager port _SetPort BSR FULLCLIP ;make sure clipping region is wide open ; ; during the main loop of menuSelect, D3 holds the mouse point, D4 holds the old ; menuIndex (into menuList data structure) and D5 holds the current menuIndex. First ; initialize these variables ; CLR D4 ;oldMenuIndex is empty MOVE.L STARTPT(A6),D3 ;start off with initial mouse point BRA.S WhichPullDown ;skip the bar test the first time ; ; here is the main loop of menuSelect. First see if the mouse is on the menu bar ; MSLOOP CMP.L #$00140000,D3 ;is mousePt.v < 20 ? BGE SAMEMENU ;if so, better see if the menu switched ; ; the x coordinate of the point is in D3. Compare it with the menuList data structure ; to find out which, if any menu that mouse is in. ; WhichPullDown CLR D5 ;assume its not in any MOVE.L MENULIST,A0 ;get menuList handle MOVE.L (A0),A0 ;handle -> pointer CMP LASTRIGHT(A0),D3 ;is it to right of rightmost point in bar? BGE.S CHECKCHANGED ;if so, its dont bother searching ; MOVE LASTMENU(A0),D5 ;start at the last (rightmost) one CHOOSEMLOOP CMP MENULEFT(A0,D5),D3 ;compare with the mouse point BGT.S CHECKCHANGED ;if greater, we've found it SUBQ #6,D5 ;bump left to next menu BNE.S CHOOSEMLOOP ;loop if there's any left ; ; at this point, D5 contains the menuIndex of the menu that the mouse points at, ; or a zero if no menu is pointed at. Check to see if this menu is different than ; the previous one pointed at ; CHECKCHANGED MOVE MENULEFT(A0,D5),D6 ;keep menuLeft of selected menu CMP D5,D4 ;menuIndex = oldMenuIndex ? BEQ.S SAMEMENU ;if the some, nothing to pull down ; ; if there is one already pulled down, get rid off it by restoring its bits ; TST D4 ;was there one? BEQ.S SKIPRESTORE ;if not, skip restoring BSR RESTOREBITS ;restore saved bits MOVE D4,D0 ;get menuIndex for title invert BSR INVERTTITLE ;unhilite old title SKIPRESTORE MOVE D5,D4 ;old menuIndex = current menuIndex ; ; if the new menuIndex is non-zero (i.e., one was chosen), pull down the menu. ; CLR.W FUNCRESULT(A6) ;remember that none are chosen TST D5 ;is there a new one chosen? BEQ.S GoNEXTMOUSE ;if not, go read the mouse again ; ; pull down the menu ; BSR GETMENUPTR ;get pointer to menuRecord in A0 MOVE MENUID(A0),FUNCRESULT(A6) ;return whichMenu is being pulled down CLR.W ITEMRESULT(A6) ;assume no item chosen ; ; set up the menuRect -- first adjust menuLeft to ensure the menu stays on the screen ; MOVE.L WmgrPort,A1 ;get pointer to wmgrPort MOVE D6,D0 ;get menuLeft in D0 ADD MENUWIDTH(A0),D0 ;get right edge ADDQ #8,D0 ;leave a little margin CMP PortRect+Right(A1),D0 ;compare with size of screen BLE.S MAKEMRECT ;if smaller, we're cool ; ; adjust menuLeft so the menu fits on the screen ; MOVE PortRect+Right(A1),D6 ;get right edge SUBQ #8,D6 ;leave some margin SUB MENUWIDTH(A0),D6 ;compute where left should be ; MAKEMRECT LEA MENURECT(A6),A1 ;get address of rectangle MOVEQ #20,D0 ;Get a 20 around MOVE D0,(A1)+ ;set up top of menuRect MOVE D6,(A1)+ ;set up menuLeft ADD MENUHEIGHT(A0),D0 ;get height MOVE D0,(A1)+ ;set up bottom ADD MENUWIDTH(A0),D6 ;compute right of menuRect MOVE D6,(A1)+ ;move in right point ; MOVE D5,D0 ;get menu index BSR INVERTTITLE ; ; implement Mr. MacIntosh if he's installed ; MOVE.L MRMACHOOK,D0 ;is Mr. Mac installed? BEQ.S NOMRMAC ;too bad, he's not MOVE.L D0,A0 ;get address in register PEA MENURECT(A6) ;tell the hook where to draw JSR (A0) ;call it TST D0 ;did it get him? BNE.S DONEMS ;if so, we're done NOMRMAC BSR DRAWTHEMENU ;draw the menu ; SAMEMENU TST D5 ;examine menu index GoNEXTMOUSE BEQ.S NEXTMOUSE ;don't choose unless there's one down ; PEA MENURECT(A6) ;don't let menuProc write outside of rect _ClipRect ;clip to menuRect BSR GETMENUPTR ;get handle to menu in A1 ; MOVE #MCHOOSEMSG,-(SP) ;tell menu to choose itself MOVE.L A1,-(SP) ;push menu handle PEA MENURECT(A6) ;push menuRect MOVE.L D3,-(SP) ;push mouse point PEA ITEMRESULT(A6) ;push place to stick result BSR GetTheMProc ;get the menu definition proc JSR (A0) ;call it BSR UnlockMProc ;unlock it ; BSR FULLCLIP ;restore full clipping ; ; here is the bottom of the menu select loop. We read the mouse position again, and ; loop if the button is still down ; NEXTMOUSE SUBQ #4,SP ;allocate place to get mouse MOVE.L SP,-(SP) ;use temp buffer for mouse point _GetMouse ;get mouse in wmgr coordinates MOVE.L (SP)+,D3 ;keep mouse point in D3 ; ; call user hook if its installed ; MOVE.L MENUHOOK,D0 BEQ.S @1 MOVE.L D0,A0 JSR (A0) ; @1 CLR.W -(SP) ;make room for stillDown result _WaitMouseUp ;is the button still down? TST.B (SP)+ ;test result BNE MSLOOP ;if its stillDown, loop again ; ; the mouse button went up. Give feedback to the user if she made a choice ; TST D5 ;was one selected? BEQ.S NOFLASH ;if not, dont bother BSR FLASHFEEDBACK ;flash the choice for feedback BSR.S RESTOREBITS ;restore bits under menu ; ; if no item selected, then no menu either ; NOFLASH TST ITEMRESULT(A6) ;was an item selected? BNE.S @1 ;if so, we're cool CLR.W FUNCRESULT(A6) ;otherwise, no menu was, either MOVE D5,D0 ;better make sure no menu hilited BSR InvertTitle ;turn off all hiliting ; @1 MOVE FUNCRESULT(A6),THEMENU ;remember menuID of hilited menu BMI.S DoSysMenu ;if its a system one, handle it TST.W MBarEnable ;does deskOrn own the menuBar? BMI.S DoSysMenu ;if so, go tell it about it DONEMS MOVE.L MSavePort(A6),-(SP) ;get old port _SetPort ;restore callers grafPort ; DONEMSELECT MOVEM.L (SP)+,D3-D7/A3 ;restore registers UNLK A6 ;dismantle stack frame MOVE.L (SP)+,(SP) ;strip sole parameter RTS ;return to caller ; ; ; handle system menus ; DoSysMenu MOVE.L FuncResult(A6),-(SP) ;push theMenu,theItem _SystemMenu ;tell the desk manager about it CLR.L FuncResult(A6) ;pretend we got nothing BRA.S DoneMS ;let common code finish up ; ; is a utility that takes the bits that were saved by SaveBits and ; puts them back on the screen. ; RESTOREBITS MOVE.L SAVEDHANDLE,A0 ;get the handle to saved bits MOVE.L (A0),SAVEDBASE(A6) ;update base address PEA SAVEDBASE(A6) ;source is buffer MOVE.L WMGRPORT,A0 ;get window manager port PEA PORTBITS(A0) ;destination is the screen BSR.S DOXFER ;xfer the bits ; ; deallocate the buffer that was used to save the bits ; MOVE.L SAVEDHANDLE,A0 ;get the save buffer's handle _DISPOSHANDLE ;deallocate it RTS ; ; DOXFER is a utility used to share code between SaveBits and restoreBits. It completes ; the call to CopyBits by pushing the rectangle and move parameters. It trashes ; A3 so be careful using it. ; DOXFER MOVE.L (SP)+,D7 ;save return address in D7 PEA SAVEBOUNDS(A6) ;push the bounds rectangle MOVE.L (SP),-(SP) ;its both source and destination CLR -(SP) ;mode is srcCopy CLR.L -(SP) ;mask region is NIL _CopyBits ;let Bill blast the bits MOVE.L D7,-(SP) RTS ;return to caller ; handle case of having no memory to save the bits -- issue a deepShit alert ; since the application should have been installed in the GrowZone hook NoMemory MOVEQ #DSMemFullErr,D0 ;get memFull error code _SysError ;issue deepShit alert ; ; DrawTheMenu is an internal utility that draws the structure of a menu. It uses ; the menu whose index is in D5. First set up the rectangle of bits to save. ; DRAWTHEMENU MOVEM.L D4/A2-A4,-(SP) ;preserve work registers MOVE.L #$00020002,D4 ;shadow is 2,2 ; LEA TEMPRECT,A3 ;point to the rectangle LEA MENURECT(A6),A4 ;get menuRect of current menu MOVE.L A4,A2 ;copy it for rectangle move MOVE.L A3,-(SP) ;push pointer to saveRect, too ; MOVE.L (A2)+,(A3)+ ;copy menuRect into tempRect MOVE.L (A2),(A3)+ ;copy botRight,too MOVE.L #$FFFCFFFC,-(SP) ;push -4,-4 ; jwp ADDQ.W #4,TEMPRECT ; don't save any of the menu bar ; jwp _InsetRect ;expand saveRect by 4 pixels ; Inline SAVEBITS MOVEQ #15,D0 ; round off factor ADD -(A3),D0 ; get right+15 MOVE -(A3),D1 ; get bottom SUB -(A3),D0 ; get width in D0 SUB -(A3),D1 ; D1 gets heigth ; ; compute rowBytes from width -- ((width+15) DIV 16)*2 ; LSR #4,D0 ADD D0,D0 ;double it MOVE.W D0,SAVEDROWBYTES(A6) ;save the rowbytes ; ; allocate memory to hold the bits ; MULU D1,D0 ;memory = rowBytes*height _NEWHANDLE ;allocate memory BNE.S NoMemory ;if we couldn't get it, in trouble MOVE.L A0,SAVEDHANDLE ;remember the handle MOVE.L (A0),SAVEDBASE(A6) ;use pointer for base address MOVE.L (A3)+,SAVEBOUNDS(A6) ;set up boundsRect MOVE.L (A3),SAVEBOUNDS+4(A6) ; ; save bits from screen to buffer ; MOVE.L WMGRPORT,A0 ;get window manager port PEA PORTBITS(A0) ;source bitmap is screen PEA SAVEDBASE(A6) ;destination bitmap is buffer BSR.S DOXFER ;xfer the bits ; MOVE.L A4,-(SP) ;push pointer to menuRect _EraseRect ;erase the menuRect ; ; paint the boundary and shadow -- first set up the pen ; A2 still points to right half of menuRect ; _PenNormal MOVE.L (A2),-(SP) ;push (right,bottom) MOVE.L (SP),-(SP) ;make two copies MOVE.L (A4),-(SP) ;push (top,left) SUBQ #1,2(SP) ;really want left - 1 MOVE.L (SP),-(SP) ;make another copy MOVE.W (A2),4(SP) ;really want (left-1,bottom) MOVE TOP(A4),12(SP) ;really want (right,top) _MoveTo ;MoveTo(left-1,top) _LineTo ;LineTo(left-1,bottom) _LineTo ;LineTo(right,bottom) _LineTo ;LineTo(right,top) ; ; paint a drop shadow for the menu ; BSR.S PaintDropShadow ; draw the menu by invoking the menu's definition procedure ; MOVE.L A4,-(SP) ;push menuRect ptr _ClipRect ;set clipping to the menu rect ; CLR.W -(SP) ;message = draw BSR GETMENUPTR ;get a pointer to the menu record MOVE.L A1,-(SP) ;push menu handle MOVE.L A4,-(SP) ;push menuRect pointer SUBQ #8,SP ;point and whichItem are not looked at BSR.S GetTheMProc ;get the menu definition routine JSR (A0) ;call it BSR.S UnlockMProc ;unlock it ; ; restore clipping to normal ; BSR FULLCLIP ; MOVEM.L (SP)+,D4/A2-A4 ;restore work registers RTS ; ; Utility PaintDropShadow -- drop shadows the rectangle in A4, by the amount in D4 ; PaintDropShadow MOVE.L D4,-(SP) ;push shadow factor _PenSize ;make penSize = shadow factor ; MOVE RIGHT(A4),D0 ;get right of menuRect MOVE D0,-(SP) ;push right MOVE TOP(A4),-(SP) ;push top ADD D4,(SP) ;want top+shadow MOVE D0,-(SP) ;push right MOVE BOTTOM(A4),D0 ;get bottom MOVE D0,-(SP) ;push bottom MOVE LEFT(A4),-(SP) ;push left ADD D4,(SP) ;want left+shadow MOVE D0,-(SP) ;push bottom ; _MoveTo ;MoveTo(left+shadow,bottom) _LineTo ;LineTo(right,bottom) _LineTo ;LineTo(right,top+shadow) ; _PenNormal ;restore normal pen RTS ; ; Utility GetTheMProc -- given a menuRecord pointer in A0, it returns a pointer ; to the menu definition procedure . It does this by de-referencing ; the defProc handle, reloading it into memory if necessary. ; GetTheMProc MOVE.L MenuDefHandle(A0),A0 ;get the handle MOVE.L A0,-(SP) ;push the handle _LoadResource ;load it BSET #7,(A0) ;lock it MOVE.L (A0),A0 ;handle -> pointer RTS UnlockMProc BSR GetMenuPtr ;figure out menuPtr UnlkMDef MOVE.L MenuDefHandle(A0),A0 ;get the handle BCLR #7,(A0) ;unlock it RTS ; ; FlashFeedBack is a utility routine that implements flash feedback on menu selections. ; tThe "MenuFlash" global determines the number of flashes ; FLASHFEEDBACK MOVE D4,-(SP) ;save work register MOVE MENUFLASH,D4 ;get menu flash count BEQ.S FLASHDONE ;if zero, no flashing at all MOVE ITEMRESULT(A6),TOOLSCRATCH ;was anything selected? BEQ.S FLASHDONE ;if not, don't give feedback ; PEA MENURECT(A6) ;clip to the menu _ClipRect ; ; Here is the flash loop. It uses TICKS for timing. ; FLASHLOOP MOVEQ #0,D1 ;zero point to force selection off BSR.S CALLCHOOSE ;call the menuProc to turn it off MOVE.L D3,D1 ;now use real point to turn it on BSR.S CALLCHOOSE ;call menuProc to turn it on ; SUBQ #1,D4 ;any more times? BNE.S FLASHLOOP ;do it flashCount times ; BSR.S FULLCLIP ;restore full screen clipping ; FLASHDONE MOVE (SP)+,D4 ;restore D4 RTS ;return to caller ; ; FullClip is a utility that sets the clipping region of the wmgrPort to the portRect ; It is used to save code ; FULLCLIP MOVE.L WMGRPORT,A0 ;get grafPtr PEA PORTRECT(A0) ;push pointer to portRect _ClipRect ;make that the clipRgn RTS ; ; CallChoose is a utility procedure to call a menu's menuProc to select an item ; CALLCHOOSE MOVE #MCHOOSEMSG,-(SP) ;the message is choose BSR GETMENUPTR ;get handle to menu specified by D5 MOVE.L A1,-(SP) ;push menuHandle PEA MENURECT(A6) ;push rectangle of current menu MOVE.L D1,-(SP) ;mouse point PEA TOOLSCRATCH ;scratch location to store result BSR.S GetTheMProc ;get the menuProc JSR (A0) ;call it BSR.S UnlockMProc ;unlock it ; ; delay based on TICKS ; MOVE.L TICKS,D0 ADDQ.L #3,D0 ;1/20 second delay (tune it!) FDLOOP CMP.L TICKS,D0 ;is it time yet? BGT.S FDLOOP ;loop till it is RTS ; ; FUNCTION GetMenuBar: MenuListHandle -- allocate a menuList data structure and copy ; the current menuList into it ; GETMENUBAR BSR ALLOCMLIST ;allocate and clear it MOVE.L A0,4(SP) ;return it as the function result MOVE.L (A0),A1 ;get menuList pointer MOVE.L MENULIST,A0 ;get handle of current menuList mBarCommon MOVE.L (A0),A0 ;turn into pointer MOVEQ #MLISTSIZE,D0 ;get menuList size _BLOCKMOVE ;copy current into newly allocated one RTS ;return to caller ; ; PROCEDURE SetMenuBar(menuListHandle) -- copy the parameter menu list into the current ; menu List. ; SETMENUBAR MOVE.L 4(SP),A0 ;get parameter menuList handle ; MOVE.L MENULIST,A1 ;current menuList is the destination MOVE.L (A1),A1 ;handle -> pointer ; MOVE.L (SP)+,(SP) ;strip parameter BRA.S mBarCommon ;go move it ; ; PROCEDURE DisposeMenu(menuHandle) -- dispose of the dynamic storage used by the ; menu data structure ; DISPOSEMENU MOVE.L 4(SP),A0 ;get menuHandle _DISPOSHANDLE ; MOVE.L (SP)+,(SP) ;strip parameter RTS ; ; PROCEDURE FlashMenuBar(menuID: INTEGER); ; ; FlashMenuBar inverts the title of a given menu item. If the menuID is 0, ; it inverts the whole menu bar. ; FlashMenuBar ;jwp MOVE.L WmgrPort,A0 ;get the wmgrPort MOVE.L ClipRgn(A0),A0 ;get clipRgn handle MOVE.L A0,-(SP) ;preserve for later _HandToHand ;clone the clip MOVE.L (SP)+,D0 ;get the clipHandle MOVE.L A0,-(SP) ;push the copy MOVE.L A0,-(SP) ;push it again MOVE.L D0,-(SP) ;push wmgrClip handle ;jwp MOVE.L D4,-(SP) ;preserve work register JSR SetWPort ;get into the window manager port BSR FullClip ;set the clip region wide open MOVE.W 20(SP),D1 ;get the menuID BEQ.S FlashWholeBar ;if 0, go flash the whole bar BSR.S GetNInvert ;get index and possibly hilite DoneFMB JSR RestorePort ;restore the caller's grafPort MOVE.L (SP)+,D4 ;restore work register ;jwp _CopyRgn ;restore the original clip _DisposRgn ;dispose of the tempRgn ;jwp BRA TwoBytExit ;standard exit saves code ; the menuID was zero, so better flash the entire menuBar FlashWholeBar MOVE.L WMgrPort,A0 ;get ptr to window manager port PEA PortRect(A0) ;push the screen rect MOVE.L ClipRgn(A0),-(SP) ;push clipRgn handle CLR.L -(SP) ;topLeft is zero,zero MOVE.W PortRect+Right(A0),-(SP) ;use full screen width MOVE.W #$0014,-(SP) ;push height _SetRecRgn ;clip to menuBar MOVE.L #ScreenRadius,-(SP) ;push rounding factor _InverRoundRect ;do it! BRA.S DoneFMB ;File MENUASM2.TEXT ;---------------------------------------------------------- ; ; Menu Manager for MacIntosh User Interface ToolBox ; part II -- text menu routines ; ; written by Andy Hertzfeld July 3, 1982 ; ; (c) 1982 by Apple Computer, Inc. All rights reserved. ; ; This file contains the source code for the ; MacIntosh menu manager's text menu routines. ; ; MODIFICATION HISTORY: ; ; 02-Aug-82 AJH Converted for new memory manager; no more oh's! ; 22-Aug-82 AJH Got rid of menu manager dependency on icon manager ; 10-Oct-82 AJH Converted to QuickDraw Trap Interface ; 07-Nov-82 AJH Made menuKey case insensitive ; 19-Dec-82 AJH Added "AddRsrcMenu" call ; 27-Dec-82 AJH Moved menuData to end of block; defProc as resource ; 21-Jan-83 AJH Fixed bug in GetSizeLeft in SetItem ; 25-Jan-83 AJH Fixed outline/shadow reversal bug ; 28-Jan-83 AJH Improved CalcMenuSize ; 05-Feb-83 AJH AddRsrcMenu -> AddResMenu; made it not load the objects ; 13-Feb-83 AJH Made PlotIcon use copyBits instead of XferRect ; 16-Mar-83 AJH Made CalcMenuSize not bold disabled items ; 17-Apr-83 AJH Made MenuKey know about desk ornament menus ; 21-Apr-83 AJH Made CalcMenuSize add some extra for command line ; 28-Apr-83 AJH moved calcMenuSize into the defProc ; 06-May-83 AJH fixed PlotIcon bug (didnt set up topLeft) ; 11-May-83 AJH Added CountMItems ; 18-May-83 AJH fixed GetMHandle bug ; 08-Jun-83 AJH/SC saved code by making SetItem use Munger ; 14-Jun-83 AJH made SetItem call CalcMenuSize ; 15-Jun-83 AJH added InsertResMenu ; 30-Jun-83 AJH fixed bug in InsertResMenu when there's none of that type ; 23-Aug-83 BLH, LAK Fixed bug in GetMHandle (didn't return result!) ; 27-Aug-83 AJH saved a little code in CalcMenuSize (use common subroutine) ; ;---------------------------------------------------------- ; ; GetItemRecord is the main utility used for accessing the menu item data structure. ; It has a register interface to save code. On entry, A0 points to a menuInfo block, ; while D0 has the item number of interest. On exit, A0 points to the item string ; of interest while A1 points to that item's attribute byte list. If the item can't ; be found, A0 and A1 both return NIL and the z-flag is set. ; GETITEMRECORD TST D0 ;make sure item number is valid BLE.S NOITEM ;if its not, don't bother ; MOVEQ #0,D1 ;clear D1 for byte arithmetic LEA MENUDATA(A0),A1 ;get menuData ptr MOVE.B (A1)+,D1 ;get title length ADD D1,A1 ;skip over title string ; ; here is the item search loop. A1 points to the beginning of the next item. ; GETILOOP SUBQ #1,D0 ;is this the one we're looking for? BEQ.S GOTITEM ;if so, we got it MOVE.B (A1)+,D1 ;get length of current item BEQ.S NOITEM ;length zero marks end of list ; ADDQ #4,D1 ;there are 4 bytes of item properties ADD D1,A1 ;bump to next item BRA.S GETILOOP ;loop till done ; ; the item couldn't be found so return NIL ; NOITEM SUB.L A0,A0 ;zero A0 MOVE.L A0,A1 ;and A1 too MOVE.L A0,D0 ;set the z-flag RTS ;return to caller ; ; we found the item so return a pointer to it in A0 and a pointer to the item properties ; in A1 ; GOTITEM TST.B (A1) ;is this the NIL item? BEQ.S NOITEM ;if so, we really didn't get one ; MOVE.L A1,A0 ;A0 points to item string MOVE.B (A1)+,D1 ;get length ADD D1,A1 ;bump to item properties RTS ;return to caller (z-clear) ; ; PROCEDURE SetItemProperty(menuHandle,itemNumber,propertyValue) -- an internal routine ; to set a property byte of an item. A small integer offset in D2 determines ; which property. ; SETITEMPROPERTY MOVE.L 8(SP),A0 ;get menuHandle MOVE.L (A0),A0 ;handle -> pointer MOVE 6(SP),D0 ;get item number BSR.S GETITEMRECORD ;find the item BEQ.S TWOPARMEXIT ;if not, skip MOVE.B 5(SP),0(A1,D2) ;move in the property byte MOVE.L 8(SP),-(SP) ;push the menuHandle _CalcMenuSize ;recalculate the menuRect ; TWOPARMEXIT MOVE.L (SP)+,A0 ;get return address ADDQ #8,SP ;strip 8 bytes of parameters JMP (A0) ;return to caller ; ; PROCEDURE GetItemProperty(menuHandle,itemNumber, VAR propertyValue) -- is an internal ; routine similar to SetItemProperty that gets a property of a requested item. ; The property is selected by the small integer in D2. ; GETITEMPROPERTY MOVE.L 10(SP),A0 ;get menuHandle MOVE.L (A0),A0 ;turn into menu pointer MOVE 8(SP),D0 ;get item number BSR.S GETITEMRECORD ;look it up ; MOVE.L 4(SP),A0 ;get result pointer CLR (A0) ;assume result of zero MOVE.L A1,D0 ;did we find the item? BEQ.S TENBYTEXIT ;if not, we're done ; MOVE.B 0(A1,D2),1(A0) ;move the requested property byte TENBYTEXIT MOVE.L (SP)+,A0 ;get return address ADD #10,SP ;strip 10 bytes of parameters JMP (A0) ;return to caller ; ; PROCEDURE GetItemIcon(menuHandle,itemNumber,VAR iconChar) -- get the current value of ; an item's icon field. Use GetItemProperty. ; GETITEMICON MOVEQ #ITEMICON,D2 ;set up property selector BRA.S GETITEMPROPERTY ;do it! ; ; PROCEDURE SetItemIcon(menuHandle,itemNumber, iconChar) -- set the current value of ; an item's icon field. Use SetItemProperty. ; SETITEMICON MOVEQ #ITEMICON,D2 ;set up property selector BRA.S SETITEMPROPERTY ;do it! ; ; PROCEDURE GetItemStyle(menuHandle,itemNumber,VAR styleChar) -- get the current value of ; an item's style field. Use GetItemProperty. ; GETITEMSTYLE MOVEQ #ITEMSTYLE,D2 ;set up property selector BRA.S GETITEMPROPERTY ;do it! ; ; PROCEDURE SetItemStyle(menuHandle,itemNumber,styleChar) -- set the current value of ; an item's style field. Use SetItemProperty. ; SETITEMSTYLE MOVEQ #ITEMSTYLE,D2 ;set up property selector BRA.S SETITEMPROPERTY ;do it! ; ; PROCEDURE GetItemMark(menuHandle,itemNumber,VAR markChar) -- get the current value of ; an item's markfield. Use GetItemProperty. ; GETITEMMARK MOVEQ #ITEMMARK,D2 ;set up property selector BRA.S GETITEMPROPERTY ;do it! ; ; PROCEDURE SetItemMark(menuHandle,itemNumber,markChar) -- set the current value of an ; item's mark field. Use SetItemProperty. ; SETITEMMARK MOVEQ #ITEMMARK,D2 ;set up property selector BRA.S SETITEMPROPERTY ; ; PROCEDURE CheckItem(menuHandle,itemNumber, checked: BOOLEAN) -- check or unCheck ; an item. Strategy: transform the boolean into the proper char right on the stack ; CHECKITEM TST.B 4(SP) ;test the boolean BEQ.S CNOMARK ;go use noMark symbol MOVE #CHECKMARK,4(SP) BRA.S SETITEMMARK ;go mark it ; CNOMARK MOVE #NOMARK,4(SP) ;unCheck it BRA.S SETITEMMARK ;go do it ; ; FUNCTION MenuKey(commandChr): LongInt -- menuKey is kind of a companion routine to ; MenuSelect; it is called when a command character is typed and scans the current ; menu data structure, returning a (whichMenu, whichItem) pair that corresponds to the ; selected command. If a command is not recognized, the result returned is zero ; MENUKEY MOVEM.L D3-D5/A3,-(SP) ;save work registers MOVE.B 21(SP),D4 ;get character to search for ; CMP.B #$60,D4 ;is it lower-case? BLT.S @2 ;if not, skip SUB.B #$20,D4 ;map into upper case ; @2 MOVE.L MENULIST,A0 ;get menuList handle MOVE.L (A0),A0 ;get menuList pointer MOVE LASTMENU(A0),D5 ;start scanning from the bottom ; ; here is the outer loop where each menu in the menuList is considered ; KEYBARLOOP BSR GETMENUPTR ;get menu pointer in A0 MOVE.L A0,A3 ;stash in A3 for safe keeping BTST #0,MENUENABLE+3(A3) ;is this menu enabled? BEQ.S NEXTBAR ;if not, skip it MOVEQ #1,D3 ;start with item number 1 ; ; here is the inner loop where each item is considered ; KEYITEMLOOP MOVE.L MENUENABLE(A3),D0 ;get enable flags BTST D3,D0 ;make sure this item is enabled BEQ.S KEYNEXTITEM ;if not, skip it ; MOVE.L A3,A0 ;get menuPtr in A0 MOVE D3,D0 ;get item number in D0 BSR GETITEMRECORD ;search for item BEQ.S NEXTBAR ;if not, this menu is exhausted ; MOVE.B ItemCmd(A1),D0 ;get the command char CMP.B #$60,D0 ;is it lower case? BLT.S @1 ;if not, skip SUB.B #$20,D0 ;map into upper case ; @1 CMP.B D0,D4 ;do they match? BEQ.S GOTMKEY ;if they do, we've found it! ; KEYNEXTITEM ADDQ #1,D3 ;bump to next item BRA.S KEYITEMLOOP ;loop till we find it or no more ; NEXTBAR SUBQ #6,D5 ;try next menu in menuList BNE.S KEYBARLOOP ;loop if there's more to do ; ; no corresponding command key was found, so return zero as the result ; KEYNOTFOUND CLR.L 22(SP) ;clear the function result MKEYDONE MOVEM.L (SP)+,D3-D5/A3 ;restore work registers BRA TWOBYTEXIT ;standard exit saves code ; ; we got a good key so return the result (whichMenu, whichItem) ; GOTMKEY BSR GETMENUPTR ;get pointer to correct menuID MOVE MENUID(A0),D0 ;get its ID SWAP D0 ;whichMenu goes into the high part MOVE D3,D0 ;D3 has whichItem MOVE.L D0,22(SP) ;update function result SWAP D0 ;get menuID MOVE D0,-(SP) ;push as parameter _HiliteMenu ;hilite it ; ; check to see if this one belongs to a desk ornament ; TST MBarEnable ;does desk ornament own menuBar? BMI.S DeskMKey ;if so, handle it MOVE.L 22(SP),D0 ;is theMenu negative? BPL.S MKEYDONE ;if not, we're done ; ; the menu belonged to a desk ornament so tell the desk manager about it ; DeskMKey MOVE.L D0,-(SP) ;push the result _SystemMenu ;invoke desk manager CLR.L 22(SP) ;pretend we didn't get anything BRA.S MKEYDONE ;all done ; ; PROCEDURE GetItem(menuHandle,itemNumber,VAR string); -- get the text of a menu item ; GETITEM MOVE.L 10(SP),A0 ;get menuInfo handle MOVE.L (A0),A0 ;handle to pointer MOVE.W 8(SP),D0 ;get item number BSR GETITEMRECORD ;look it up ; MOVE.L 4(SP),A1 ;get result stringPtr,(zflag preserved) BEQ.S NOITEM1 ;if not, return empty string ; ; use blockMove to copy the string ; MOVEQ #0,D0 ;clear out D0 MOVE.B (A0),D0 ;get length of string ADDQ #1,D0 ;move length byte, too _BLOCKMOVE ;let memory manager move it fast DONEGI BRA TENBYTEXIT ;standard exit saves code ; ; item not found, so return the null string ; NOITEM1 CLR.B (A1) ;set result string length to zero BRA.S DONEGI ;standard exit saves code ; PROCEDURE CalcMenuSize(menuHandle) -- scan the menuData of a menu and set the menu's ; width and heigth fields accordingly, by invoking the menu definition proc CALCMENUSIZE MOVE #2,-(SP) ;tell mProc to calc size MOVE.L 6(SP),A0 ;get the menu handle MOVE.L A0,-(SP) ;push it SUB #12,SP ;push 12 "dont care" bytes MOVE.L (A0),A0 ;get the menu ptr BSR GetTheMProc ;get the menu definition proc JSR (A0) ;call it MOVE.L 4(SP),A0 ;get the menuHandle MOVE.L (A0),A0 ;handle -> pointer BSR UnlkMDef ;get def handle and unlock it MOVE.L (SP)+,(SP) ;strip parameter RTS ;return to caller ; ; EnableTest is a utility which tests if an item is enabled. It expects a menuHandle ; in A3 and the item number in D4. It returns the result in the Z-flag ; ENABLETEST MOVE.L (A3),A0 ;get menu pointer MOVE.L MENUENABLE(A0),D0 ;get enable flags BTST D4,D0 ;is item enabled? BEQ.S ETDONE ;if not, return 0 BTST #0,D0 ;test menu bit ETDONE RTS ;return to caller ; ; FUNCTION NewMenu(menuID,titleString):menuHandle -- allocate a new menu block with ; the specified menuID and title. ; NEWMENU MOVEQ #0,D1 ;clear out the high part MOVEQ #MenuBlkSize+2,D0 ;get size of menuInfo block MOVE.L 4(SP),A0 ;point to the title string MOVE.B (A0),D1 ;get its length ADD.W D1,D0 ;compute real length _NEWHANDLE ;allocate it MOVE.L A0,10(SP) ;return menuHandle as result MOVE.L (A0),A1 ;get menu pointer MOVE.W D1,-(SP) ;save title size for later ; ; initialize the fields of the menu definition record ; MOVE.W 10(SP),(A1)+ ;init menuID CLR.L (A1)+ ;zero width and heigth initially ; initialize the menu defProc handle SUBQ #4,SP ;make room for function result MOVE.L #MDefRType,-(SP) ;push MDEF resource type CLR.W -(SP) ;default defProc is 0 _GetResource ;get it MOVE.L (SP)+,A0 ;get mDef handle in A0 ; since GetResource can allocate memory, better rebuild the ptr from its handle MOVE.L 12(SP),A1 ;get menuHandle MOVE.L (A1),A1 ;get menuPtr ADDQ #MenuDefHandle,A1 ;bump to defHandle MOVE.L A0,(A1)+ ;install menuProc MOVE.L MinusOne,(A1)+ ;everything's enabled to start ; ; move in the title string MOVE.W (SP)+,D1 ;get length of string MOVE.L 4(SP),A0 ;get string ptr @1 MOVE.B (A0)+,(A1)+ ;move in the string DBRA D1,@1 ;loop till done ; ; add the string of length zero on the end ; CLR.B (A1)+ ;string of length zero BRA SIXBYTEXIT ;all done -- standard exit saves code ; ; PROCEDURE AppendMenu(menuHandle,itemString) -- add the list of items represented by ; the item string to the menu. The itemString may contain one or more items, seperated ; by carriage returns or semicolon characters. The item strings are also scanned for ; special meta-characters that change their initial properties. ; ; ; AppendMenu Stack Frame Definition ; ITEMSTATE .EQU -276 ;one byte parse state field ITEMFLAGS .EQU ITEMSTATE+1 ;one byte enable flag field ITEMCOUNT .EQU ITEMFLAGS+1 ;integer -- number of chars in buffer ITEMPROPERTIES .EQU ITEMCOUNT+2 ;4 attribute bytes of item ITEMBUFFER .EQU ITEMPROPERTIES+4 ;256 byte buffer for items APPENDMENU LINK A6,#-280 ;get a buffer for building items MOVEM.L D3-D4/A3-A4,-(SP) ;save work registers MOVE.L 8(A6),A4 ;get itemString ptr in A4 MOVE.L 12(A6),A3 ;get menuHandle in A3 CLR D3 MOVE.B (A4)+,D3 ;get length of itemString ; BSR.S INITITEM ;initialize local buffer for new item SUBQ #1,D3 ;bias for DBRA loop ; ; process each character in the itemString, one character at a time ; APLOOP MOVE.B (A4)+,D0 ;get next character BSR.S APPENDCHAR ;process it DBRA D3,APLOOP ;process each character ; BSR CLOSEITEM ;close out any item that may be open ; MOVE.L A3,-(SP) ;push menu handle _CalcMenuSize ;calculate new width and heigth ; MOVEM.L (SP)+,D3-D4/A3-A4 ;recover registers UNLK A6 ;de-allocate stack frame BRA TWOPARMEXIT ;standard exit saves code ; ; InitItem initializes the item buffer ; INITITEM LEA ITEMSTATE(A6),A0 ;get address of item buffer state CLR.L (A0)+ ;clear out state/flags/count CLR.L (A0) ;clear property parameters RTS ; ; Meta character definition table -- this table defines which meta-character ; perform what functions; the order is icon,command,mark,style,delimiter,delimiter, ; disable. ; MCHARTABLE .ASCII '^' ;arrow is the icon character .ASCII '/' ;slash is command character .ASCII '!' ;exclamation point is mark character .ASCII '<' ;less-than is the style character .BYTE $0D ;carriage return is one delimiter .ASCII ';' ;semi-colon is the other .ASCII '(' ;leftParen is disable character ; ; style character table ; STYLETAB .ASCII 'BIUOS' ;bold,italic,underline,outline,shadow ; ; AppendChar does all the real work. It processes the character in D0 by checking out ; what state its in. If in a command state, it uses the character to update a ; property byte. In normal state, characters go into a buffer that is added to the ; menu when a carriage return or semicolon is scanned ; APPENDCHAR CLR D1 MOVE.B ITEMSTATE(A6),D1 ;get current state BEQ.S NORMSTATE ;if normal state, go handle it ; CMP.B #ITEMICON+1,D1 ;is it an icon item? BNE.S @1 ;if not,skip SUB #$30,D0 ;adjust ASCII ; @1 CMP.B #ITEMSTYLE+1,D1 ;is it a style? BNE.S UPDATEPROP ;handle style characters special ; ; handle style properties special by scanning table and oring bits ; MOVEQ #4,D2 ;start at last one @2 CMP.B STYLETAB(D2),D0 ;is it in the table? BEQ.S ADDTOSTYLE ;if so, modify D0 DBRA D2,@2 ;bump to next one ; ; if its not in table, just ignore it ; RTS ; ADDTOSTYLE LEA ITEMPROP-1(A6),A0 ;get property table address MOVE.B 0(A0,D1),D0 ;get current property BSET D2,D0 ;set appropriate property ; UPDATEPROP LEA ITEMPROP-1(A6),A0 ;get address of item property table MOVE.B D0,0(A0,D1) ;update the property CLR.B ITEMSTATE(A6) ;return state to normal RTS ; ; Handle the normal state by scanning for any of the 7 special characters ; NORMSTATE LEA MCHARTABLE,A0 ;get address of character table MOVE #6,D1 ;scan it backwards NSLOOP CMP.B 0(A0,D1),D0 ;is it one of the special ones? BEQ.S SPECIALCHAR ;if so, go handle it DBRA D1,NSLOOP ; ; it wasn't in the exception table so it must be a normal character. Just append it ; to the item buffer. ; MOVE ITEMCOUNT(A6),D1 ;get total number of characters LEA ITEMBUFFER(A6),A0 ;get address of buffer MOVE.B D0,0(A0,D1) ;stick character in buffer ADDQ #1,ITEMCOUNT(A6) ;bump buffer index RTS ; ; it was one of the special characters -- first see if its a property command ; SPECIALCHAR CMP #3,D1 ;which char was it? BGT.S CHECKTERM ;if not 0 through 3, not a property cmd ; ; it was a property meta-character so update the state accordingly ; ADDQ #1,D1 ;bump state MOVE.B D1,ITEMSTATE(A6) ;update state RTS ; ; check for termination characters like carraige return or semicolon ; CHECKTERM SUBQ #6,D1 ;was it enable character? BNE.S CLOSEITEM ;it must have been a termination character ; ; disable the item ; ST ITEMFLAGS(A6) ;flag the item to be disabled RTS ; ; CloseItem is called to add the item in the buffer (and its associated properties) to ; the actual menu data structure. After this is done, re-initialize for the next item ; CLOSEITEM MOVE ITEMCOUNT(A6),D1 ;get the # of character in buffer BEQ.S CLOSEDONE ;if none, we're done ; ; change the size of the itemList by adding in the size of item to be added. At this ; point, the A3 has the menu handle ; MOVE.L A3,A0 ;get menuHandle _GETHANDLESIZE ;get the size in D0 ADD #5,D0 ;there's 4 attribute bytes plus string size ADD D1,D0 ;add in size of item to be added _SETHANDLESIZE ;make it bigger ; ; now we must search for the end of the itemList in menuData. Note that we better ; rebuild our pointers since things might have moved around ; CLR D0 ;clear out for byte math MOVE.L (A3),A1 ;get pointer to menu LEA MenuData(A1),A1 ;point to menuData string ; MOVE.B (A1)+,D0 ;get length of title ADD D0,A1 ;bump to 1st item MOVEQ #1,D2 ;D2 keeps track of item number ; NILLOOP MOVE.B (A1),D0 ;get the length of the item BEQ.S FOUNDNIL ;if zero, we've found it ADD D0,A1 ;add in the length ADDQ #5,A1 ;bump past property bytes ADDQ #1,D2 ;bump item counter BRA.S NILLOOP ;loop till we find it ; ; we've found the trailing NIL entry, so move in the new item right there ; FOUNDNIL LEA ITEMBUFFER(A6),A0 ;get address of item MOVE.B D1,(A1)+ ;move in the length byte FMOVE1 MOVE.B (A0)+,(A1)+ ;move the item in SUBQ #1,D1 ;can't use blockMove cause we need BNE.S FMOVE1 ;A1 for next move ; LEA ITEMPROPERTY(A6),A0 ;point to the property bytes MOVE #3,D1 ;there are 4 bytes to move FMOVE2 MOVE.B (A0)+,(A1)+ ;move it in DBRA D1,FMOVE2 ;move em in ; ; don't forget the NIL at the end; also, disable item if necessary ; CLR.B (A1)+ ;put null string at end TST.B ITEMFLAGS(A6) ;was it disabled? BEQ.S CLOSEDONE ;if not, we're done ; MOVE.L A3,-(SP) ;push menu pointer MOVE D2,-(SP) ;push item number BSR DISABLEITEM ;disable it ; CLOSEDONE BSR INITITEM ;clear out the item buffer RTS ; ; FUNCTION GetMHandle(menuID): menuHandle -- convenience procedure which returns ; a menuHandle given a menuID. If the menuID cannot be found in the current menuList, ; NIL is returned ; GETMHANDLE CLR.L 6(SP) ;set result to NIL MOVE 4(SP),D1 ;get menuID BSR GETINDEX ;get menuIndex BEQ.S @1 ;if none, return NIL MOVE D5,-(SP) ;preserve D5 MOVE D0,D5 ;get menuIndex in D5 BSR GETMENUPTR ;get handle to menu in A1 MOVE (SP)+,D5 ;restore D5 MOVE.L A1,6(SP) ;set function result @1 BRA TWOBYTEXIT ;standard exit saves code ; ; PROCEDURE SetItem(menuHandle:Handle; itemNum: INTEGER; newItem: String) -- change ; the specified item to the new string, keeping the same attributes as the old one ; SETITEM MOVEM.L D3-D4/A2-A4,-(SP) ;save work registers MOVE.L 30(SP),A3 ;get menu handle MOVE.W 28(SP),D3 ;get item number MOVE.L 24(SP),A2 ;get string pointer ; MOVE.W D3,D0 ;get item in D0 MOVE.L (A3),A0 ;get menu ptr in A0 BSR GETITEMRECORD ;find our entry BEQ.S SETIDONE ;if not, we're done MOVE.L A0,A4 ; remember item ptr in A4 SUB.L (A3),A4 ; turn into an offset MOVEQ #1,D4 ; get length of old ADD.B (A0),D4 ; get size of old(+1 for length byte) MOVEQ #1,D2 ; get length of new ADD.B (A2),D2 ; get size of new(+1 for length byte) SUBQ #4,SP ; make room for result MOVE.L A3,-(SP) ; push menu handle MOVE.L A4,-(SP) ; offset to string to be replaced CLR.L -(SP) ; no search string MOVE.L D4,-(SP) ; old length MOVE.L A2,-(SP) ; ptr to new MOVE.L D2,-(SP) ; length of new _Munger ADDQ #4,SP ; ignore result MOVE.L A3,-(SP) ; push the menu handle _CalcMenuSize ; recalculate the size ; ; all done with SetItem ; SETIDONE MOVEM.L (SP)+,D3-D4/A2-A4 ;restore work registers BRA TENBYTEXIT ;standard exit saves code ; ; PROCEDURE SetMenuFlash(flashCount: INTEGER) -- set the system global "flashCount" to ; a new value, controlling the duration of the flash feedback given when a menu item ; is selected ; SETMENUFLASH MOVE.W 4(SP),MENUFLASH ;update flashcount BRA TWOBYTEXIT ;standard exit saves code ; ; PROCEDURE AddResMenu(theMenu: menuHandle; theType: RsrcType); ; ; AddResMenu appends all the names of a given resource type class ; to the menu passed as a parameter. It ignores names with a period or ; percent sign as their first character. It is simply a cover for InsertResMenu, ; which it falls into. ; AddResMenu MOVE.L (SP)+,A0 ; get return address MOVE #999,-(SP) ; push large "afterItem" MOVE.L A0,-(SP) ; restore return address ; ; PROCEDURE InsertResMenu(theMenu: menuHandle; theType: RsrcType, afterItem: INTEGER); ; ; InsertResMenu adds all the names of a given resource type class ; to the menu passed as a parameter. It ignores names with a period ; as their first character. It adds them after the item specified by "afterItem". ; InsertResMenu LINK A6,#-270 ;allocate local space MOVEM.L D3-D6/A3-A4,-(SP) ;save some work registers CLR.W -(SP) ;push FALSE _SetResLoad ;don't load the objects SUBQ #2,SP ;make room for function result MOVE.L 10(A6),-(SP) ;push the resource type _CountResources ;get the number of objects MOVE.W (SP)+,D4 ;is it zero? BEQ AddRsrcDone ;if so, we're done MOVEQ #1,D3 ;start indexing with #1 AddRsrcLoop SUBQ #4,SP ;make room for the result MOVE.L 10(A6),-(SP) ;push the type MOVE.W D3,-(SP) ;push the index value _GetIndResource ;get the resource handle LEA -270(A6),A3 ;get ptr to name buffer PEA -4(A6) ;push ptr to ID PEA -8(A6) ;push ptr to type MOVE.L A3,-(SP) ;push ptr to name string _GetResInfo ;get the name, etc. TST.B (A3) ;did it get one? BEQ.S NextRLoop ;if not, skip CMP.B #$2E,1(A3) ;if 1st char a period? BEQ.S NextRLoop ;if so, skip it CMP.B #$25,1(A3) ;how about a percent sign? BEQ.S NextRLoop ; we got a valid name so insert it in the menu data structure ; MOVE.L 14(A6),A4 ;get the menu handle MOVE.L (A4),A0 ;get the menu ptr MOVE.W 8(A6),D0 ;get the "after index" ADDQ #1,D0 ;bump to next item (inserting before) MOVE D0,D6 ;remember it in D6 BSR GetItemRecord ;look it up in menu data structure BEQ.S InsertAtEnd ;if it can't be found, append it ; A0 points to the place to insert it -- figure out the offset MOVE.L A0,D0 ;get it in temp reg SUB.L (A4),D0 ;compute offset SUBQ #4,SP ;make room for result MOVE.L A4,-(SP) ;push the menu handle EXT.L D0 ;clear out high part of offset MOVE.L D0,-(SP) ;push the offset MOVE.L A0,-(SP) ;push string ptr CLR.L -(SP) ;push 0 for insert ; A3 points to the string -- tack zeros onto the end of it and get its length in ; D0 MOVEQ #0,D0 ;clear out high part of D0 MOVE.B (A3),D0 ;get length of string LEA 1(A3,D0),A0 ;point to end of string CLR.B (A0)+ CLR.B (A0)+ ;zero property bytes CLR.B (A0)+ CLR.B (A0)+ ADDQ.L #5,D0 ;compute new length ; finish setting up munger parameters, then invoke munger to do the work MOVE.L A3,-(SP) ;push ptr to string MOVE.L D0,-(SP) ;push length of string _Munger ADDQ #4,SP ;ignore munger result ; better fix up enableFlags -- must insert a bit at the proper position MOVE.L (A4),A0 ;get menuPtr MOVE.L MenuEnable(A0),D5 ;get current enable flags MOVEQ #31,D0 ;32 bits to process BitLoop ASL.L #1,D5 ;shift it out ROXL.L #1,D1 ;shift it in CMP D6,D0 ;is this the one? BNE.S @1 ;if not, skip ASL.L #1,D1 ;insert the bit ADDQ #1,D1 ;turn it on! @1 DBRA D0,BitLoop ;loop until done MOVE.L D1,MenuEnable(A0) ;replace the flags ; ; loop through all the objects in the type class until we're done ; NextRLoop ADDQ #1,D3 ;bump the index CMP D4,D3 ;compare with the count BLE.S AddRsrcLoop ;loop if more to do ; ; all done so recalc menu size, clean up stack, etc. ; MOVE.L A4,-(SP) ;push menu handle _CalcMenuSize ;recalculate the size AddRsrcDone MOVE.W #$0100,-(SP) ;push TRUE _SetResLoad ;turn loading back on MOVEM.L (SP)+,D3-D6/A3-A4 ;restore the registers UNLK A6 ;unbuild the stack frame BRA TenBytExit ;standard exit save code ; ; handle the case of inserting the item at the end of the menu ; InsertAtEnd MOVE.L A4,-(SP) ;push the menuHandle MOVE.L A3,-(SP) ;push the string pointer _AppendMenu ;add it to the menu BRA.S NextRLoop ;go do next item ; FUNCTION CountMItems(mHandle: MenuHandle): INTEGER ; ; CountMItems returns the number of items in a given menu ; CountMItems MOVEQ #0,D2 ;start with item 0 CountMLoop ADDQ #1,D2 ;bump item number MOVE.L 4(SP),A0 ;get menu handle MOVE.L (A0),A0 ;handle -> pointer MOVE.W D2,D0 ;get item number in D0 BSR GetItemRecord ;look it up BNE.S CountMLoop ;if we got it, bump count and try again ; we couldn't get the item in D2, so D2 must have the number of items + 1 SUBQ #1,D2 ;get number of items MOVE.W D2,8(SP) ;return it as the result MOVE.L (SP)+,(SP) ;strip the parameter RTS ;return to caller ; ; PROCEDURE PlotIcon(theRect: Rect; theIcon: IconHandle) ; ; Plots a given icon at a given place in your local coordinate system. ; theMode is the LisaGraf penMode for the transfer ; PLOTICON LEA ICONBITMAP,A1 ;get address of icon mgr bitMap MOVE.L 4(SP),D0 ;get the icon handle BEQ.S DONEIPLOT ;if NIL, dont plot MOVE.L D0,A0 ;get iconHandle in address reg ; MOVE.L (A0),(A1)+ ;icon mptr is the base address MOVE #4,(A1)+ ;set up rowBytes CLR.L (A1)+ ;set up topLeft MOVE.L #$00200020,(A1) ;set up botRight ; MOVE.L 8(SP),D1 ;get theRect LEA IconBitMap,A1 ;point A1 at bitmap again ; ; push parameters for copyBits call ; MOVE.L A1,-(SP) ;source bitMap is iconBitMap MOVE.L LGLOBALS(A5),A0 ;get lisaGraf global baseaddress MOVE.L THEPORT(A0),A0 ;get thePort PEA PORTBITS(A0) ;that's the destination bitmap ; PEA BOUNDS(A1) ;boundsRect of bitmap is source MOVE.L D1,-(SP) ;theRect is the destination CLR.W -(SP) ;theMode is srcCopy (0) CLR.L -(SP) ;no mask region _CopyBits ;let Bill blit those bits DONEIPLOT BRA TWOPARMEXIT ;standard exit saves code ; ; End of MacIntosh Menu Manager! ; ;FILE MENUMGR.TEXT ;MACWORKS Copy ;---------------------------------------------------------- ; ; Menu Manager for MacIntosh User Interface ToolBox ; ; written by Andy Hertzfeld June 29, 1982 ; ; (c) 1982 by Apple Computer, Inc. All rights reserved. ; ; This file contains the interface definition used by the ; MacIntosh menu manager, which is based on Bill Atkinson's ; Lisa menu manager. The rest of the menu manager source ; files are .INCLUDED from this one. ; ; MODIFICATION HISTORY: ; ; 07-Sep-82 AJH Added "DrawMBar" entry point ; 10-Oct-82 AJH Converted for QuickDraw Trap Interface ; 22-Nov-82 AJH Made TextMenuProc and CalcMenuSize externally accessible ; 19-Dec-82 AJH Added "AddRsrcMenu" call ; 28-Jan-83 AJH Added "FlashMenuBar" ; 05-Feb-83 AJH Changed "AddRsrcMenu" to "AddResMenu" to keep Caroline happy ; 11-May-83 AJH Added "CountMItems" ; 15-Jun-83 AJH Added "InsertResMenu" ; 27-Aug-83 AJH Made it include "SysErr" ; 09-Aug-83 AJH Made it reference "WmgrGray" ; ;---------------------------------------------------------- .INCLUDE tlasm-SYSEQU.TEXT .INCLUDE tlasm-SYSMACS.TEXT .INCLUDE tlasm-SysErr.Text .INCLUDE tlasm-GRAFTYPES.TEXT .INCLUDE tlasm-TOOLEQU.TEXT .INCLUDE tlasm-ResEqu.TEXT .INCLUDE tlasm-QuickMacs.TEXT .INCLUDE tlasm-ToolMacs.TEXT .PROC MMGR,0 ; ; MacIntosh Menu Manager External Definitions ; .DEF InitMenus .DEF NewMenu .DEF DisposeMenu .DEF AppendMenu .DEF DisposeMenu ; .DEF InsertMenu .DEF DeleteMenu ; .DEF GetMenuBar .DEF SetMenuBar ; .DEF DrawMenuBar .DEF ClearMenuBar ; .DEF MenuSelect .DEF MenuKey .DEF HiliteMenu ; .DEF GetItem .DEF SetItem .DEF EnableItem .DEF DisableItem .DEF CheckItem .DEF GetItemIcon .DEF SetItemIcon .DEF GetItemStyle .DEF SetItemStyle .DEF GetItemMark .DEF SetItemMark ; .DEF GetMHandle .DEF SetMenuFlash .DEF CalcMenuSize .DEF AddResMenu .DEF InsertResMenu .DEF FlashMenuBar .DEF CountMItems ; ; Utilities defined by the menu manager ; .DEF DrawMBar .DEF FullClip .DEF PaintDropShadow .DEF PlotIcon ; ; External References used by the menu manager ; .REF SetWPort .REF RestorePort .REF WMgrGray .INCLUDE source-MENUASM1.TEXT .INCLUDE source-MENUASM2.TEXT ; .END ; File Packages.TEXT ; MACWORKS Copy ;--------------------------------------------------------------------- ; ; Macintosh Application Package Dispatch Interface ; ; written by Jerome Coonen and Andy Hertzfeld 13-May-83 ; ; ROM-based interface to RAM-based package. The packages are ; resources of type "PACK", numbered 0 through 7. Packages 0 through ; 3 are for use by applications (presumably, several applications ; sharing the same resources); packages 4 through 7 are reserved for ; math routines such as floating point arithmetic, elementary ; functions, etc. ; ; PROCEDURE InitPack(PackNumber : integer); when passed a ; PackNumber between 0 and 7 performs a GetResource of the appropriate ; PACK resource, setting up the dispatch vector for subsequent ; invocations of the package from the application. InitPack turns off ; resource loading (via SetResLoad) so that the GetResource simply ; up the resource map. Thus the package is loaded into memory, and ; need only be available at all in the resource file, if and when it ; is used. Resource loading is turned back on after the GetResource. ; InitPack follows the Pascal register conventions. ; ; PROCEDURE InitMath; calls InitPack four times with the arguments ; 4, 5, 6, and 7 and then initializes the 3 word floating point state ; area. ; ; Labels PACK0, PACK1, ..., PACK7 receive the respective A-line traps. ; The in turn transfer control to the package whose handle was saved ; by InitPack; they preserve ALL registers. They must check the ; handle, in case GetResource failed to find the package, and then ; check the pointer, in case the package has been purged. ; ; Modification history: ; ; 15-Jun-83 AJH Made it use deep shit errors defined in SysErr ; ;--------------------------------------------------------------------- .INCLUDE TLASM-ToolEqu.Text .INCLUDE TLASM-ToolMacs.Text .INCLUDE TLASM-SysErr.Text .PROC PackDispatcher,0 .DEF InitPack, InitMath .DEF Pack0, Pack1, Pack2, Pack3, Pack4, Pack5, Pack6, Pack7 ; ; PROCEDURE InitPack(PackNumber : integer); ; ; Stack: return adrs < PackNumber ; InitPack CLR.W -(SP) ; push Pascal FALSE _SetResLoad ; turn off resource loading SUBQ.L #4,SP ; room for handle from GetResource MOVE.L #$5041434B,-(SP) ; resource type PACK ; Stack: "PACK" < handle slot < return adrs < PackNumber MOVE.W 12(SP),-(SP) ; resource number = PackNumber _GetResource ; Stack: handle slot < return adrs < PackNumber MOVE.W 8(SP),D0 ; PackNumber, beyond handle and ret adrs LSL.W #2,D0 ; mul by 4 to get address index LEA AppPacks,A1 ; address of block of 8 handles MOVE.L (SP)+,0(A1,D0) ; store resource handle ; Stack: return adrs < PackNumber MOVE.B #1,-(SP) ; push Pascal TRUE _SetResLoad ; turn on resource loading MOVEA.L (SP)+,A0 ; return address ADDQ.L #2,SP ; kill PackNumber JMP (A0) ; ; PROCEDURE InitMath; ; InitMath ;jwp ; set up the stack to include calls for all 8 packages then let InitPack loop ; back to itself. First initialize a few of the floating point packages globals. CLR.W FPState ; set default modes for arithmetic LEA FPException,A0 MOVE.L A0,FPState+2 ; default halt address MOVE.L (SP)+,A0 ; get trap return MOVEQ #7,D0 ; 8 packages @1 MOVE.W D0,-(SP) ; pack to init MOVE.L A0,-(SP) ; where to go LEA InitPack,A0 ; where to go from here DBRA D0,@1 JMP (A0) RTS ; ... just in case... ;jwp ; ; Default floating point exception handler simply signals deep six alert. ; FPException MOVEQ #DSFPErr,D0 _SysError ; ; Common jump routine. Must preserve values of all registers. ; Jump to address saved in location AppPacks + OFFSET, ; where OFFSET = 2 * ( - Pack1). ; PackCom MOVEM.L D0/A0,-(SP) ; need two work registers MOVE.L 8(SP),D0 ; get return address LEA Pack1,A0 SUB.L A0,D0 ; difference is 0,2,4,6,...,14 ADD.W D0,D0 ; difference is 0,4,8,12,...,28 LEA AppPacks,A0 ; base address of handle table ADDA.L D0,A0 ; package handle address TST.L (A0) BEQ.S NoPack ; 0 handle means GetResource failed MOVEA.L (A0),A0 ; now dereference handle down to pointer TST.L (A0) ; 0 pointer means must reload resource BNE.S GoToPack MOVE.L A0,-(SP) ; push handle for LoadResource _LoadResource ; Note: preserves ALL registers. GoToPack MOVE.L (A0),8(SP) ; place resource address over old ret adrs MOVEM.L (SP)+,D0/A0 ; restore work registers RTS ; ; Signal deep six alert if GetResource failed ; NoPack LSR.W #2,D0 ; address index div by 4 gives rsrc num ADD.W #DSNoPackErr,D0 ; error number for this NoPack _SysError ; ; Routines differ only in offset into handle table. So compute offset from ; the difference - Pack1 ; Pack0 BSR.S PackCom Pack1 BSR.S PackCom Pack2 BSR.S PackCom Pack3 BSR.S PackCom Pack4 BSR.S PackCom Pack5 BSR.S PackCom Pack6 BSR.S PackCom Pack7 BSR.S PackCom .END ; FILE: QUEUE.TEXT ; MACWORKS copy ;_______________________________________________________________________ ; ; Macintosh Core Utility Routines -- Enqueue, Dequeue, InitQueue ; ; These utility routines are the queueing primitives for Macintosh. ; They are used by UNIT I/O, the vertical retrace manager and ; various drivers. ; ; Written by Andy Hertzfeld 23-APR-81 ; ; MODIFICATION HISTORY: ; ; 02-May-82 LAK removed set and reset of queue-in-use flags since ; disabling interrupts already guaranteed exclusion. ; 23 Feb 83 LAK changed a Move to a MoveQ, a Cmp to a Cmp.L. ; 08 Aug 83 LAK made code-savings changes suggested in walkthru. ;_______________________________________________________________________ .NOLIST .INCLUDE tlasm-SYSEQU.TEXT .INCLUDE tlasm-SYSERR.TEXT .INCLUDE tlasm-SYSMACS.TEXT .LIST ; .PROC QSTUFF,0 .DEF ENQUEUE,DEQUEUE,INITQUEUE ; ;----------------------------------------------------------------------- ; ; ENQUEUE -- add a queue element to a queue. On entry, A0 points to the ; queue element while A1 points to the queue header. All registers are ; preserved; there are no error conditions. ; ; The element is inserted at the end of the list. ; ; - should we make sure the element is not already in the queue? ;----------------------------------------------------------------------- ; ENQUEUE MOVE SR,-(SP) ;preserve status ADDQ #2,A1 ;point to QHead ORI #$0300,SR ;disable interrupts for exclusion CLR.L QLINK(A0) ;clear the link of the element TST.L (A1)+ ;anything in the queue? (QHead) BNE.S DOQINSERT ;if so, skip ahead ; ; the queue is empty so make the head and tail point to the new element ; MOVE.L A0,(A1) ;QTail MOVE.L A0,-(A1) ;QHead BRA.S ENQDONE ; ; insert the element at the end of the list ; DOQINSERT MOVE.L A2,-(SP) ;preserve A2 MOVE.L (A1),A2 ;get ptr to old QTail MOVE.L A0,QLINK(A2) ;update its link MOVE.L A0,(A1) ;update QTail MOVE.L (SP)+,A2 ;restore A2 SUBQ #4,A1 ;point to QHead ; ENQDONE SUBQ #2,A1 ;restore A1 RTE ;restore status and go home ; ;----------------------------------------------------------------------- ; ; DEQUEUE -- delete an element from a queue. On entry, A0 points to the queue ; element while A1 points to the queue header. On exit, D0 contains an error ; code. The only possible error is if the queue element is not present in the ; queue. All registers except D0 are preserved. ; ;----------------------------------------------------------------------- ; DEQUEUE MOVE SR,-(SP) ;preserve status MOVEM.L A2-A3,-(SP) ;preserve work registers ORI #$0300,SR ;disable interrupts for exclusion ; MOVE.L QHEAD(A1),A2 ;start searching at the head MOVE.L A2,A3 ; QDELSRCH CMP.L A3,A0 ;is this the one? BEQ.S GODEL ;(Escher and Bach)if so, go delete it ; MOVE.L A3,A2 ;update previous pointer MOVE.L QLINK(A2),A3 ;follow the link CMP.L QTAIL(A1),A2 ;have we reached the tail? BNE.S QDELSRCH ; ; the queue element can't be found so it's an error ; MOVEQ #QERR,D0 BRA.S QDONE1 ; ; we found the element so delete it ; GODEL CMP.L A2,A3 ;deleting the first element? BNE.S QUNLINK ;if not, skip ; ; special case to delete the first element in the queue ; MOVE.L QLINK(A2),QHEAD(A1) BNE.S QDELDONE ;was it the only element? CLR.L QTAIL(A1) ;if so, clear the tail too ; QDELDONE MOVEQ #0,D0 ;clear the error code QDONE1 MOVEM.L (SP)+,A2-A3 ;restore work registers RTE ;restore interrupts and return ; ; delete for the general case ; QUNLINK MOVE.L QLINK(A3),QLINK(A2) ;unlink it from the chain CMP.L QTAIL(A1),A3 ;was it the tail? BNE.S QDELDONE ;if not we're done MOVE.L A2,QTAIL(A1) ;update tail pointer BRA.S QDELDONE ;------------------------------------------------------------------ ; ; INITQUEUE -- initialize the queue header pointed to by A1 ; ;------------------------------------------------------------------ INITQUEUE CLR.W (A1)+ ;QFlags CLR.L (A1)+ ;QHead CLR.L (A1) ;QTail SUBQ #6,A1 ;preserve A1 RTS .END ; File QuickGlue.TEXT ; MACWORKS Copy ;------------------------------------------------------------------ ; ; QuickDraw/Mac OS Interface ; ; written by Andy Hertzfeld 16-Sept-82 ; ; (c) 1982 by Apple Computer, Inc. All rights reserved. ; ; QuickGlue is the QuickDraw/Mac OS interface. It is linked with QuickDraw and ; defines all of the externals required by QuickDraw except those of the ; font manager. All of these are very short and simple (memory manager traps or ; jumps through the graphics jump table). ; ; Modification History ; ; 16-Nov-82 AJH Made font manager interface go through graphics jump table ; 09-Feb-83 AJH Added LockHandle, UnLockHandle ; 17-Aug-83 SC Made all cursor jumps preserve A0 ; ;------------------------------------------------------------------ .INCLUDE Tlasm-SYSEQU.TEXT .INCLUDE Tlasm-SYSMACS.TEXT .INCLUDE Tlasm-GRAFEQU.TEXT .INCLUDE Tlasm-SysErr.Text .INCLUDE Tlasm-ToolMacs.Text ; ; Here is a subset of Unit Storage (the ones needed by ; QuickDraw), implemented by trapping to the Mac OS. ; ; ; FUNCTION NewHandle(byteCount: INTEGER): Ptr; ; .FUNC NewHandle,1 ; MOVEQ #0,D0 ;clear out high part MOVE.L (SP)+,A1 ;get return address MOVE.W (SP)+,D0 ;get the byte count _NEWHANDLE ;ask OS to do request BNE.S MemFull ;if memory full, deep shit! MOVE.L A0,(SP) ;return result handle on stack JMP (A1) ;return to caller ; handle the memory full error by deep-shitting MemFull MOVEQ #DSMemFullErr,D0 _SysError .WORD $A9FF ;invoke debugger just in case it comes back ; ; PROCEDURE SetSize(h: Handle; newSize: INTEGER); ; .DEF SetSize ; SetSize MOVEQ #0,D0 ;clear out high part MOVE.L (SP)+,A1 ;get return address MOVE.W (SP)+,D0 ;get the new size MOVE.L (SP)+,A0 ;get the handle _SETHANDLESIZE ;let OS do it BNE.S MemFull ;if out of memory, deepShit JMP (A1) ;return to caller ; ; PROCEDURE DisposeHandle(h: Handle); ; .PROC DisposeHandle,2 ; MOVE.L (SP)+,A1 ;get return address MOVE.L (SP)+,A0 ;get parameter _DISPOSHANDLE ;let OS do work JMP (A1) ;return to caller ; ; PROCEDURE LockHandle(h: Handle); ; .PROC LockHandle MOVE.L 4(SP),A0 BSET #7,(A0) MOVE.L (SP)+,(SP) RTS ; ; PROCEDURE UnLockHandle(h: handle); ; .PROC UnlockHandle MOVE.L 4(SP),A0 BCLR #7,(A0) MOVE.L (SP)+,(SP) RTS ; Following is the QuickDraw cursor interface, implemented by accessing ; system routines through the graphics jump table ; .PROC CursorDisplay,0 ; MOVE.L JShowCursor,-(SP) RTS ; .PROC CursorHide,0 ; MOVE.L JHideCursor,-(SP) RTS ; .PROC CursorImage,0 ; MOVE.L JSetCrsr,-(SP) RTS ; .PROC CursorInit,0 ; MOVE.L JInitCrsr,-(SP) RTS ; .PROC CursorObscure,0 ; MOVE.L JCrsrObscure,-(SP) RTS ; .PROC CursorShield,0 ; MOVE.L JShieldCursor,-(SP) RTS ; .PROC ScreenAdress,0 ; MOVE.L JScrnAddr,-(SP) RTS ; .PROC ScreenSize,0 ; MOVE.L JScrnSize,-(SP) RTS ; .PROC FMSwapFont,0 MOVE.L JSwapFont,-(SP) RTS .END .PROC RESTOP ;TOP OF RESIDENT MONITOR ;LINK THIS PROCEDURE IN LAST .END ; FILE Rmgr.text ; MACWORKS Copy ;****************************************************************************** ; ; Resource Manager for the Macintosh User Interface Standard ; ; ; Written by Bruce Horn 22 November 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains the interface specification for the Resource Manager. ; It .Includes files which contain toolbox, graftype and sysmacs equates ; and macro definitions, as well as the actual code for the implementation ; of the Resource Manager. ; ; 16-Dec-82 BLH Added Add/RmveResource. Massive name changing. ; 21-Jan-83 BLH Updated to new LK file system. ; 11-Feb-83 BLH Returned error codes in InitResources, ; OpenResource ; 17-Feb-83 BLH Fixed bug in CheckLoad ; 25-Apr-83 BLH Rom 3.0 overhaul: ; fixed bug in UpdateResFile, optimizing ; the writes and making it a little quicker ; to call updates. ; ; Changed openResFile to use the resource ; fork, and to handle more reasonably the ; already-open, not-open, and other error ; conditions. ; ; CloseResFile now releases all resources ; in the map before closing, compacts the ; resources in the map, and writes out the ; possibly modified map and header. ; (@@Note--CloseResFile does not compact ; yet. Due to a bug in GetHandleSize, ; this feature has been disabled. ) ; ; The resource file starts at ZERO instead ; of 512. ; ; Added memory full error handling in ; OpenResFile and CheckLoad. ; ; Added preLoad capability to initResources ; and OpenResource. ; ; Added procedure WriteResource. ; ; Added ResAdded resource attribute. ; ; Added MAttr, map attributes word, in map. ; ; MapSize and DataSize are now LONGS. ; ; Resource manager routines now save ; ALL registers except A0 and D0. ; ; Added SetResPurge to handle the purge ; hook in the Memory Manager. ; ; Added CurResFile. ; ; 12-May-83 AJH fixed bug in InitResources -- shouldn't preload when ; you can't read the map ; ; 24-May-83 BLH Fixed bug in NewMap--crashes if failed due to unlink bug. ; 25-May-83 BLH Changed interface to CmpString in GetNamedResource. ; 31-May-83 BLH Added CreateResFile, fixed AddResource bugs. ; 1-Jun-83 BLH Added DetachResource. ; 6-Jun-83 BLH Fixed bug in GetNamedResource (didn't search all maps) ; 8-Jun-83 BLH Fixed bug in CloseResource (!) ; ; 10-Jun-83 BLH Moved compacting and mapwrite from CloseResFile to Update. ; Added routine to close maps in RsrcZoneInit. ; CloseResFile closes all maps if you attempt to close ; System.Rsrc. UpdateResFile now compacts, since GetHandleSize ; finally works. ; ; 13-Jun-83 BLH Made SetResAttr, SetResInfo NOT set the mapChanged bit. ; you have to explicitly call ChangedResData to make sure ; the change is written out to the map. ; ; 18-Jul-83 BLH Added MapCompact attribute bit to minimize resource file ; compacting. Added UniqueID to return a unique ID for a given ; type in the current map. Also, shrunk code approximately ; 60 bytes due to suggestions from Donn Denman. ; ; 8-Aug-83 BLH Removed BeginSubResource, EndSubResource. Added HomeResFile. ; Added ResErrProc in globals. Made UniqueID return an ; ID which is unique in all open maps. Took out SetEOF ; from UpdateResFile, to allow pre-allocation of the ; resource fork. Put in ResrvMem to reserve low locations for ; locked handles. Added file-order preload and update to ; speed up resource I/O. ; ; 12-Aug-83 BLH Split Resource Manager into four files: RMgr, RMgrAsm1, ; RMgrAsm2, and RMgrAsm3. Utilities in RMgrAsm2. Added ; CheckGrow to handle the DiskFull condition. CheckGrow ; called from ChangedResource, AddResource, and AddReference. ; Also Added ReallocRes, AllocRes, called from (surprise!) ; changedResource and AddResource to allocate space for ; either a new resource or a resource which has changed ; size and needs to be reallocated at the end. ; ; 16-Aug-83 BLH Bug fixes and space-savers. ; ; 29-Aug-83 BLH Fixed bug in SetResInfo, RmveResource (protect bit checked ; in RHndl instead of RAttr) and made CheckGrow handle ; memoryFull condition as well as diskFull. ; AddResource/AddReference now return ResErr=DiskFull or ; MemFull in the case that CheckGrow failed. ; ; 1-Sep-83 BLH InitResources shouldn't clear ResErrProc. ; Fixed NOP (branch to next instr!), folded cmn code in ; name stuff. Fixed dangling ptr problem (!) caused by ; CheckGrow. Took out dangerous LINK stuff in SetFileIO ; and just used a standard stackframe on first link in ; StdEntry. Made GetCurMap and CloseResFile handle the case ; of no maps open (set curMap to FFxx with ST). Fixed bug ; in LoadResource where errors would not get reported through ; the ResErrProc. AddResource sets the Resource bit in the ; masterPtr, RmveResource and DetachResource clear it. ; ; 2-Sep-83 BLH MAJOR CHANGE: threw away name ID stuff, made hard offsets ; to the names in the name block stored in the resource entry ; where the nameID was previously stored. This change ; fixed a time-bomb bug which would trash the resource file ; after repeated SetResInfo calls. Also, data lengths in ; the resource file are now LONG integers (4 bytes). Fixed ; CountTypes, GetIndType to return true counts and unique types ; for all open maps, to be consistent with CountResources ; and GetIndResource. ; ; 6-Sep-83 BLH Added MapReadOnly in MAttr to inhibit writing to the resource ; file and diskFull checking. Added SetResFileAttrs, ; GetResFileAttrs to allow toggling of this bit. ; ; 8-Sep-83 BLH Fixed bug in CheckGrow which could trash the resource map ; if the handle passed to ChangedResource or AddResource ; was purged. ; ; 9-Sep-83 BLH Fixed bug in CheckLoad--ResrvMem must preserve A0, so typed ; in .Word $A040 to replace _ResrvMem. Fixed bug in RmveName. ; ; 10-Sep-83 BLH Fixed bug having to do with protected resources; cleaned ; up usage in UpdateResFile, ChangedResource, WriteResource. ; ; 23-Apr-84 KWK Added Bruce's "I swear I fixed this before!" to ; UpdateResFile in loop CRFLoop, for restoring handle ; while exiting from compacting because of error. ; ; 7-Sep-84 KWK Modified NewMap to call RdData1 instead of RdData ; (doesn't do ptr restoring stuff in RdData1) ; Modified CreateResFile to call WrData1 (in RmgrAsm2) ; instead of WrData (see note above). Additional fixes ; for making sure we have a valid ptr after FS calls ; ; 7-Sep-84 KWK Modified RdData & WrData to make sure that A2/A3 contain ; valid ptrs after FS calls. ; ; 19-Mar-85 KWK Patch in ORFCommon (RMgrAsm1) for Printer Chooser, ; forces use of boot drive if str ptr from print code ; ; 23-Mar-85 BLT Change ORFCommon to use vRefNum of zero (default ; volume) in normal case. ; ;****************************************************************************** ; ; Version 7.0. Last modified 10-Sep-83 by Bruce. ; ;****************************************************************************** .Include tlasm-SysErr.Text ;System error constants .Include tlasm-SysEqu.Text ;System equates (memory layout...) .Include tlasm-GrafEqu.Text ;Basic graphics and screen driver .Include tlasm-GrafTypes.Text ;Offsets into LisaGraf structures .Include tlasm-QuickMacs.Text ;LisaGraf traps .Include tlasm-SysMacs.Text ;System macros, line1010 calls .Include tlasm-ToolMacs.Text ;ToolBox References .Include tlasm-ToolEqu.Text ;Toolbox equates .Proc RMgr, 0 .Def InitResources ;One-time call, initializes resource manager ;and opens System.Resource (!refnum) .Def RsrcZoneInit ;Zero all handles in the system map ;which point to application heap zone .Def CreateResFile ;Given filename, create a null res file .Def OpenResFile ;Given filename, opens and returns refnum .Def UseResFile ;Given refnum, sets current res file. .Def GetResFileAttrs ;Given refnum, return resfile attributes. .Def SetResFileAttrs ;Given refnum, set resfile attributes. .Def UpdateResFile ;Given refnum, update resource file .Def CloseResFile ;Given refnum, closes the resource .Def SetResPurge ;Write resources when purging? .Def SetResLoad ;Auto-Load on get? .Def CountResources ;Given type, return number of resources .Def GetIndResource ;Given index and type, returns handle .Def CountTypes ;Returns number of types in current rsrc .Def GetIndType ;Given index, returns type .Def UniqueID ;Given type, return a unique ID for that type .Def GetResource ;Given an ID and type, give handle back .Def GetNamedResource;Given name and type, return handle .Def LoadResource ;Given handle, loads resource from source .Def DetachResource ;Given handle, detaches it from resources. .Def ReleaseResource ;Given handle, releases resource. .Def WriteResource ;Given handle, writes resource to file .Def HomeResFile ;Returns refnum of an object's home resfile .Def CurResFile ;Returns the current resource file refnum .Def GetResAttrs ;Given handle, get the attributes of a resource .Def SetResAttrs ;Given handle, set the attributes of a resource .Def GetResInfo ;Given handle, get the info of a resource .Def SetResInfo ;Given handle, set the info .Def ChangedResource ;Called to mark resource to be written .Def AddResource ;Add a resource to the resFile. .Def AddReference ;Add a reference to a resource in the current resFile. .Def RmveResource ;Remove a resource from the resFile .Def RmveReference ;Remove a resource reference in the current resFile .Def ResError ;Returns error code, 0 if no error. ;jwp .Def SizeResource .Def ChangedResource ;jwp .Include source-RMgrAsm1.Text ; ResMgr procedures .Include source-RMgrAsm2.Text ; ResMgr utilities .Include source-RMgrAsm3.Text ; ResMgr procedures .End ; FILE Rmgrasm1.text ; MACWORKS copy ;****************************************************************************** ; RMgrAsm1 ; Resource Manager ; ; Written by Bruce Horn 22 November 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains the first 16 Resource Manager routines. Utility routines ; are in RMgrAsm2, and the remainder of the RMgr routines are in RMgrAsm3. ; ; As with the OS routines, the Resource Manager routines, with one ; exception, smash A0 and D0 only. All other registers preserved. ; ; LoadResource preserves ALL registers. ; ;****************************************************************************** ; ; Modification history given in RMgr. ; ; Version 7.0. Last modified 10-Sep-83 by Bruce. ; ; ; ;****************************************************************************** ;--------------------- ; ; Register conventions throughout the Resource Manager: ; ; A0, D0, D1, D2, D7 scratch. ; ; A1 is the handle to the resource ; A2 points to an entry in the map ; A3 points to the beginning of a sub-block ; A4 is the map handle ; D3 is the resource type, if any ; D4 is the number of resource entries (-1) for looping ; D5 is the number of types (-1) for looping ; D6 is the resource map reference number ; A6 is always LINKed, through StdEntry. There is only 1 local variable, ; an IOStackFrame. A0 also always points to 8(A6) when returning from StdEntry. ;-----------------------------Resource Fork format------------------------------- ; ; The resource fork contains all the resource data, the resource map, ; and several special data objects in fixed locations. These data objects ; are normally accessed only by the Finder. ; ; -------------------------- ; Picture of a Resource Fork ; -------------------------- ; ; 0-------------------> --------------------------------- ; ! Resource Header ! ; 16------------------> ! Directory copy ! ; 128-----------------> ! ....other user data.... ! ; ! ... ! ; ResDataOffset-------> ! Resource Data ! ; ! ... ! ; ! ! ; ResMapOffset--------> ! Resource Map ! ; ! ... ! ; --------------------------------- ; ; (Note ResDataOffset+DataSize=ResMapOffset, ; ResMapOffset+MapSize=EOF.) ; ; Resource header is defined as follows: ResDataOffset .EQU 0 ; 4 byte offset to resource data in the file ResMapOffset .EQU ResDataOffset+4 ; 4 byte offset to resource map in the file DataSize .EQU ResMapOffset+4 ; 4 byte data size MapSize .EQU DataSize+4 ; 4 byte map size HdrSize .EQU MapSize+4 ;After resource header, remaining block has the following info by convention: DirCopy .EQU HdrSize ; Copy of directory entry, truncated to 112 bytes UserData .EQU DirCopy+112 ; User data starts here and continues ; up to ResDataOffset. CreateResFile ; allocates 128 bytes for userData. DataFirstRes .EQU UserData+128 ; ResDataOffset usually points here. ;-----------------------------Resource Data format------------------------------- ; ; Each resource has a four-byte length, followed by the data. The data ; is packed initially, and UpdateResFile packs the data if fragmented ; by RmveResource, ChangedResData, and UpdateResFile calls. ;-----------------------------Resource Map format-------------------------------- ; ; The Resource Map contains references to resources either in the current ; resource file or in System.Rsrc. These references include the type, ID, ; and/or names of the resources, their locations in the data section of the ; resource fork, and special attributes of each resource. ; ; -------------------------- ; Picture of a Resource Map ; -------------------------- ; ; 0-------------------> --------------------------------- ; ! (Copy of Resource Hdr) ! ; 16------------------> ! MNext ! ; ! MRefNum ! ; ! MAttr ! ; ! MTypes ! ; ! MNames ! ; MTypes--------------> ! TypeCount ! ; ! TypeA ! ; ! CountA ! ; ! OffsetA ! ; ! TypeB ! ; ! CountB ! ; ! OffsetB ! ; ! ... ! ; MTypes+OffsetA-----> ! IDA1 ! ; ! NameOffA1 ! ; ! LocationA1 ! ; ! HandleA1 ! ; ! IDA2 ! ; ! NameOffA2 ! ; ! LocationA2 ! ; ! HandleA2 ! ; ! ... ! ; MTypes+OffsetB-----> ! IDB1 ! ; ! NameOffB1 ! ; ! LocationB1 ! ; ! HandleB1 ! ; ! ... ! ; MNames-------------> ! ! ; ! "Ascii String" ! ; MNames+NameOff2----> ! ! ; ! "Another String" ! ; ! ... ! ; --------------------------------- ; ; (Note: MTypes is always the same--right after ; the resource map header. ) ; ; The resource map is defined as follows: ; A copy of resource header precedes the other resource map info. MNext .EQU HdrSize ; Handle to next map MRefNum .EQU MNext+4 ; Map file reference number MAttr .EQU MRefNum+2 ; Map attributes word (high byte only used) MTypes .EQU MAttr+2 ; Type offset in map (64K max offset) MNames .EQU MTypes+2 ; Name offset in map (64K max offset) ; Type Block: TypeCount .EQU 0 ; TypeCount(A3) gives number of types ; Offsets into a type entry: TType .EQU 0 ; Type of resources TCount .EQU TType+4 ; 2 byte count, zero based TOffset .EQU TCount+2 ; 2 byte offset TESize .EQU TOffset+2 ; Type entry size TypeShift .EQU 3 ; log2 TESize. ; Offsets into a resource entry: RID .EQU 0 ; ID of the resource RNameOff .EQU RID+2 ; Name offset of the resource RAttr .EQU RNameOff+2 ; Attribute byte (high byte of locn) RLocn .EQU RAttr ; Location of the resource in source file RHndl .EQU RLocn+4 ; Handle of the resource RefID .EQU RHndl ; If reference, ID of ref'd resource RefNameOff .EQU RefID+2 ; If reference, NameOff of ref'd resource RESize .EQU RHndl+4 ; Resource entry size ; Stack frame definition IOStkFrame .EQU -IOQElSize ; stack frame is IOQElSize long RMgrStack .EQU IOStkFrame ; Resource mgr stack frame =IOStkFrame ; Length of length longword LenWdLen .EQU 4 ; a longword is 4 bytes. ;--- ;NullRF--the null resource file. NullStr just points to the word 0. NullRMSize .EQU 30 ; Null map size ; 128 bytes reserved for user. NullRF NullStr .Word 0, DataFirstRes ; ResDataOffset .Word 0, DataFirstRes ; ResMapOffset (no data!) .Word 0, 0 ; DataSize .Word 0, NullRMSize ; MapSize .Word 0, 0 ; MNext .Word 0 ; MRefNum .Word 0 ; MAttr .Word MTyps-NullRF ; MTypes .Word MNams-NullRF ; MNames MTyps .Word -1 ; no types listed MNams .Word 0 ; this byte not copied. No names follow. ;-------------------------------------------------------------------------------- ; ; FUNCTION InitResources: INTEGER; 8(A6) ; ; One-time call, initializes resource manager and opens System.Rsrc. ; Returns the reference number of the System.Rsrc map. ; Sets ResLoad to TRUE initially, in PreLoad. ; ;-------------------------------------------------------------------------------- InitResources BSR StdEntry ; Save regs LEA theZone, A3 ; Point to theZone. MOVE.L (A3), -(SP) ; Save the current zone MOVE.L SysZone, (A3) ; Set system heap for system resource map. LEA SysResName, A1 ; Point to name of system resource. BSR.S NewMap ; Load in map, refnum in D6, result in D0. MOVE.L (SP)+, (A3) ; Restore the zone MOVE.W D6, 8(A6) ; Return result/error code. TST.L D0 ; Test newMap result in D0. BLE.S IRExit ; if <=0, error or already open. ; If newly opened, set up handle and ; refnum globals. LEA TopMapHndl, A0 ; Point to topMapHndl MOVE.L A2, (A0)+ ; Save handle as top map MOVE.L A2, (A0)+ ; and as system map MOVE.W D6, (A0)+ ; Save refnum as sysMap refnum. MOVE.W D6, (A0) ; and as current map. BSR.S PreLoad ; Preload required resources... IRExit BRA ZerByteExit ; ...and exit. ;-------- ; Utility PreLoad ; Clear the handle locations for all local (i.e. non-resSysRef) resource entries, ; and load in all resources in the current map which have the resPreload bit set. ; ; PreLoad walks through the map in file order. This was done to optimize the ; file I/O and buffer hits. ; PreLoad ST ResLoad ; Set ResLoad to True NewPreLoad BSR GetCurMap ; Get the current map BSR FirstRes ; Point A2 to the first file-order entry BMI.S PLExit ; if none, exit. PLLoop CLR.L RHndl(A2) ; Note that it hasn't been loaded yet, BTST #ResPreload, RAttr(A2) ; Should this resource be loaded in now? BEQ.S @1 ; No, check next, otherwise BSR CheckLoad ; load in the resource. @1 BSR NextRes ; Skip to next entry in file order. BEQ.S PLLoop ; If there are more, loop back PLExit RTS ; ...and return. ;-------- ; Utility NewMap ; Given filename in A1 ; Returns A1=pointer to map ; A2=handle to map ; D0=result code ; D6=file reference number NewMap MOVEQ #HdrSize, D0 ; Set length = header size _NewHandle BMI.S NMNoHandle ; If failed this little newHandle, exit, MOVE.L A0, A2 ; else save handle in A2 BSR SetFileIO ; Set up the file IO frame A0 --> BSR ORFCommon ; Open the resource file with name in A1 BEQ.S ExistRes ; If open was successful, go on CMP.W #OpWrErr, D0 ; Already open? BNE.S NMNoFile ; If not open, can't handle, else CLR.W ResErr ; Clear the error flag if open MOVE.L A2, A0 ; Get the map handle _DisposHandle ; and deallocate it. Map already exists. ; DisposHandle returns D0=0, signal already ; open. RTS ; D6=refnum of already opened file ; The file exists. Check size, and if not null, load the map and set up handles. ExistRes _GetEOF ; Get the EOF of the resource fork. MOVE.W #EOFErr, ResErr ; Assume EOF error (null fork) TST.L IOLEOF(A0) ; Is it a null fork? BEQ.S NMFail ; If so, close and fail--no map to load. MOVEQ #HdrSize, D0 ; byte count=HdrSize MOVE.L (A2), A1 ; dereference map handle to ptr. MOVE.L A1, D1 ; Copy to D1 for RdData MOVEQ #0, D2 ; Start at beginning of file BSR RdData1 ; Read data, set new ResErr result. (kwk) BMI.S NMFail ; If failed, return error+close file. MOVEQ #HdrSize, D1 ; Get header size in D1 MOVE.L (A2),A1 ; Deref handle again (kwk) MOVE.L ResMapOffset(A1), D2 ; Get offset to resource map, ADD.L D1, D2 ; but skip header MOVE.L MapSize(A1), D4 ; Get mapsize MOVE.L D4, D0 ; Get map size in D0 MOVE.L A2, A0 ; Get handle in A0 for size set _SetHandleSize ; Set size to mapsize MOVE.W D0, ResErr ; return the error code in ResErr BNE.S NMFail ; If mem full, return error+close file. MOVE.L (A2), A1 ; Dereference new map MOVE.L D4, D0 ; Get mapsize in D0 to calc bytes to read SUB.L D1, D0 ; MapSize-HeaderSize=bytes to read ADD.L A1, D1 ; skip header in buffer too BSR RdData1 ; Read in the bytes! (kwk) BMI.S NMFail ; if IO err, return and close file. ; Successful load. Return with D6=refnum of file and D0=1 for "newmap" flag MOVE.L (A2),A1 ; Deref handle (kwk) CLR.L MNext(A1) ; Zero nextMap handle MOVE.W D6, MRefNum(A1) ; Save refnum of file in map MOVEQ #1, D0 ; Return 1 for "new resource" flag RTS ; ...and return, successful open. ; error results: close the opened file, if necessary, dispose of the map ; handle if necessary, and return with D6=D0=-1. NMFail BSR SetFileIO ; Set up file frame again. D3=iorefnum. _Close ; Close the file NMNoFile MOVE.L A2, A0 ; Get the map handle _DisposHandle ; ...and dispose of it. NMNoHandle MOVEQ #-1, D6 ; set D6=-1 for failure MOVEQ #-1, D0 ; Return -1 for failed open flag RTS ; return to InitResources or OpenResource. ;-------------------------------------------------------------------------------- ; ; PROCEDURE RsrcZoneInit; ; ; Zeroes all handles in the system map which point to the application zone, ; and closes all other maps. This routine is called from the Memory Mgr ; when an application zone is initialized. ; ;-------------------------------------------------------------------------------- RsrcZoneInit BSR StdEntry ; Link A6, save regs TST.W SysMap ; Is there at least the system map? BMI.S RZIExit ; If not, just return, otherwise BSR.S CloseFiles ; Close all res files except system. ; Now make the system map forget about resources it has loaded into the ; application heap by clearing their handle locations. BSR GetCurMap ; Get the current map (i.e. system map) BSR GetEntries ; Skip to the entries BMI.S RZIExit ; If no entries, no handles to zero. RZLoop BTST #ResSysHeap, RAttr(A2) ; Is the resource in the system heap? BNE.S NxtRZ ; If so, skip to next entry, otherwise CLR.L RHndl(A2) ; Forget the disposed handle in app. heap. NxtRZ BSR NextEntry ; Otherwise skip to the next entry BPL.S RZLoop ; If there is another entry, loop back RZIExit BRA ZerByteExit ; Branch to zero byte exit. ;---- ; Utility CloseFiles ; Closes all maps except the system map. Trashes only A0/D0. CloseFiles MOVE.L TopMapHndl, A0 ; Get the top map CMP.L SysMapHndl, A0 ; Is the current map the system map? BEQ.S AllClosed ; If so, can quit (in ORFCommon) MOVE.L (A0), A0 ; Dereference map handle MOVE.W MRefNum(A0), -(SP) ; Push the map refnum _CloseResFile ; Close the resource file. BRA.S CloseFiles ; and loop back. ;-------- ; Utility ORFCommon ; ; Open the resource fork whose name is in A1. ; A0 must point to the IO block, usually allocated by SetFileIO. ; Returns error code in D0/ResErr, and IORefNum in D6. ORFCommon MOVEM.L A0/A1,-(SP) ; save some regs ; BLT 23-May-85 - Set D6 to zero to use default volume CLR.W D6 ; use default volume ;kwk -- check for call from printer chooser MOVE.L A1,D0 ; get ptr BTST #0,D0 ; check for odd ptr BNE.S @1 ; odd, skip check BTST #29,D0 ; is it a resource ptr? BEQ.S @1 ; no, skip check MOVE.L A1,A0 ; get ptr in A0 _RecoverHandle ; turn into handle LINK A6,#-270 ; get some room MOVE.L A0,-(SP) ; push handle PEA -4(A6) ; push place to stick ID PEA -8(A6) ; push place to stick type PEA -270(A6) ; push place to stick name _GetResInfo ; get the info CMP.W #$E000,-4(A6) ; right id? BNE.S @0 ; no, skip CMP.L #'STR ',-8(A6) ; right type? BNE.S @0 ; no, skip MOVE.W BootDrive,D6 ; force drive number to be boot drive @0 UNLK A6 ; restore stack fram @1 MOVEM.L (SP)+,A0/A1 ; restore regs MOVE.W D6,IOVRefNum(A0) ; set boot drive location MOVE.L A1,IOFileName(A0) ; Set filename pointer CLR.W IOFileType(A0) ; Set file type zero, and IOPermssn zero. CLR.L IOOwnBuf(A0) ; No buffer _OpenRF ; Open the file's resource fork MOVE.W IORefNum(A0), D6 ; set D6=file refnum. MOVE.W D0, ResErr ; and set ResErr AllClosed RTS ;-------------------------------------------------------------------------------- ; ; PROCEDURE CreateResFile(fileName: Str255); 8(A6) = (A0) ; ; Initialize a null resource fork for the given filename. ; If the file does not exist at all, create it. ; ;-------------------------------------------------------------------------------- CreateResFile BSR StdEntry ; Link and save regs MOVE.L (A0), A1 ; Get filename pointer BSR SetFileIO ; Set up the file IO frame A0 --> BSR.S ORFCommon ; Attempt to open the resource fork. ; D6 := refnum of file. BEQ.S FExists ; If successful, see if the fork is null. CMP.W #FNFErr, D0 ; Was the error "file not found"? BNE.S NoClose ; If a different error, can't handle, else _Create ; create a new file with null D+R forks BSR.S ORFCommon ; and attempt to open again. D6=refnum. BMI.S NoClose ; If open failed, can't do anything. FExists _GetEOF ; Get EOF of the resource fork. MOVE.W #DupFNErr, ResErr ; Assume error, fork already exists TST.L IOLEOF(A0) ; Is it a null fork, ready to initialize? BNE.S NoInit ; If not, leave DupFNErr and exit, else MOVEQ #HdrSize, D0 ; Write header only at 0. LEA NullRF, A1 MOVE.L A1, D1 ; Set buffer address to null map MOVEQ #0,D2 ; (WrDataSt -- Larry's sug) (kwk) BSR WrData1 ; Write out initial map @0. Set ResErr. MOVEQ #NullRMSize, D0 ; Set number of bytes MOVEQ #64, D2 ; Get 64 for offset... ASL.W #2, D2 ; left shift 2 for 256=FirstResData. BSR WrData1 ; Write out initial map. Set ResErr. NoInit _Close ; Close the file. Error code in D0 ignored....... ; WrData, WrDataSt preserved A0. NoClose BRA.S CORFExit ; ...and exit ;-------------------------------------------------------------------------------- ; ; FUNCTION OpenResFile(fileName: Str255): 8(A6) = (A0) ; INTEGER; 12(A6) ; ; Given filename, opens this resource. Returns resource file reference #. ; If the file was already open, returns the current refnum of the file. ; If negative, an error occurred and the code can be noted in ResErr. ; InitResources must be called before OpenResFile. ; Sets ResLoad to TRUE initially. ; ;-------------------------------------------------------------------------------- OpenResFile ;jwp BSR StdEntry ; Link A6, save regs MOVE.L (A0), A1 ; Get name pointer in A1 JSR NewMap ; D3=refnum, A1=map ptr, A2 is map handle MOVE.W D6, 12(A6) ; Return refnum of map, or -1 for error. BMI FourByteExit ; If I/O error, return. MOVE.W D6, curMap ; Save it as current map. TST.W D0 ; Was it a newly-created map? BEQ FourByteExit ; No, it was already there, don't relink. MOVE.L TopMapHndl, MNext(A1) ; If new, link. Store old top as next map MOVE.L A2, TopMapHndl ; Set new map as top map JSR NewPreLoad ; Preload required resources... ;jwp CORFExit XFourByteExit BRA FourByteExit ; ...and exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE UseResFile(refNum: INTEGER); ; ; Given refnum, sets current resource to use. If there isn't any such map, ; ignores the request. ; ;-------------------------------------------------------------------------------- UseResFile BSR.S StdMapEntry ; Link, save regs, get map and validate it MOVE.W D6, CurMap ; Set this refnum as the current map BRA.S STwoByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; FUNCTION GetResFileAttrs(refnum: INTEGER): 8(A6) ; INTEGER; 10(A6) ; ; Given a resource file, return its attribute byte in low order. ; high order is trash. ; ;-------------------------------------------------------------------------------- GetResFileAttrs BSR.S StdMapEntry ; Look for the file. If not there, exit MOVE.B MAttr(A3), 11(A6) ; Return the byte in low order BRA.S STwoByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; PROCEDURE SetResFileAttrs(refnum: INTEGER; 10(A6) ; attrs: INTEGER); 8(A6) ; ; Given a resource file, set its attribute byte. NO ERROR CHECKING! ; ;-------------------------------------------------------------------------------- SetResFileAttrs BSR StdEntry ; Look for the file. If not there, exit MOVE.W 10(A6), D6 ; Get the refnum BSR GetMap ; Get the corresponding map BMI.S @1 ; if can't find, just exit. MOVE.B 9(A6), MAttr(A3) ; Set attributes word @1 BRA.S XFourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; Utility StdMapEntry ; Links and saves regs using StdEntry. ; Given the map refnum 8(A6), scans for the map and exits if not found. ; StdEntry returns A0=8(A6), so the refnum is copied from (A0). ; Used by UseResFile, UpdateResFile, and CloseResFile. ; Callers must have a two-byte argument. StdMapEntry MOVE.L (SP)+, D0 ; Save return address in D0 BSR StdEntry ; Link and save regs MOVE.L D0, -(SP) ; Restore return address MOVE.W (A0), D6 ; Get the resource map refnum BSR GetMap ; Scan for it BMI.S @1 ; If not found, exit, otherwise RTS ; just return. @1 ADDQ #4, SP ; pop return address, MOVE.W #ResFNotFound, ResErr ; set error code, and STwoByteExit BRA TwoByteExit ; exit with two-byte parameter. ;-------------------------------------------------------------------------------- ; ; PROCEDURE UpdateResFile(refNum: INTEGER); 8(A6) ; ; Given refnum, writes out new resources and updates the map. ; If refnum=0, then use system resource. ; You must call CloseResFile before exiting the application to write out ; the new map and compact the file. ; ;-------------------------------------------------------------------------------- ; -------- Utilities for UpdateResFile, PreLoad. -------- ;---- ; Utilities FirstRes, NextRes ; File-Order routines. ; ; *FirstRes sets A2 to point to the entry of the resource which is first in ; the resource file. ; *NextRes sets A2 to point to the entry of the resource which follows the entry ; that was passed in A2. ; ; Never returns A2 pointing to a system reference. ; ; Returns EQ if there was a next entry, MI if not. ; A0, D0, A2 smashed. FirstRes MOVEQ #-1, D0 ; prime with -1 for first resource BRA.S FNCmn ; and jump to common code NextRes MOVE.L RLocn(A2), D0 ; Get the location AND.L MaskBC, D0 ; and mask off the high byte. FNCmn MOVEM.L A3-A4/D2-D7, -(SP) ; Save regs MOVE.L D0, D2 ; Copy location to find successor of BSR GetEntries ; Point to the first entry BMI.S NoNext ; If none, exit LEA MinusOne, A0 ; Point to MinusOne, but SUBQ.L #RLocn, A0 ; skip back RLocn. (RLocn(A2)=MinusOne). MOVE.L A0, D6 ; D6=new candidate. valid A2 will replace. MOVEQ #-1, D7 ; D7:=FFFFFFFF LSR.L #1, D7 ; shift rt by 1 = 7FFFFFFF, +ve infinity. FRLoop TST.B RAttr(A2) ; Test attribute byte BMI.S NoCloser ; If SysRef set, don't consider. MOVE.L RLocn(A2), D0 ; Get the resource location AND.L MaskBC, D0 ; AND off high order 8 bits. CMP.L D0, D2 ; is this location greater than given? BGE.S NoCloser ; No, try next CMP.L D0, D7 ; is it less than the last candidate? BLE.S NoCloser ; no, old candidate is still good MOVE.L D0, D7 ; else replace old candidate with new MOVE.L A2, D6 ; Save A2 candidate also in D6 NoCloser BSR NextEntry ; Get the next entry BPL.S FRLoop ; if there was one, loop back. MOVE.L D6, A2 ; Return A2 as following resource entry. MOVE.L D7, D0 ; Return D0 as the next location. ; Set condition codes for exit. ; In normal case, RLocn(A2) points to a valid location. Masking the high order ; byte off should give the actual location, and it will be the same as D0. ; In this case, CMP.L D0, D7 returns EQ. ; In the failure case, A2=-RLocn(MinusOne), so RLocn(A2)=FFFFFFFF. ; Masking RLocn(A2) with MaskBC gives 00FFFFFF. Since D0 will be 7FFFFFFF, ; CMP.L D0, D7 returns MI. MOVE.L RLocn(A2), D7 ; Get this entry's location. AND.L MaskBC, D7 ; and off high bits. CMP.L D0, D7 ; Set CC's. EQ=valid entry, MI=end of list. NoNext MOVEM.L (SP)+, A3-A4/D2-D7 ; Save regs RTS ; ...and return. ;---- ; Utility SpaceAt ; Given A2 pointing to a resource entry, return the space available in the map ; for the resource in D0. Does not include space for a length longword. ; if last resource, returns MI with D0=space available before map. ; Otherwise returns PL with D0=space available before next resource. SpaceAt MOVEM.L A0-A2/D7, -(SP) ; Save regs MOVE.L RLocn(A2), D0 ; Get the location longword AND.L MaskBC, D0 ; AND off high 8 bits. MOVE.L D0, D7 ; Copy to D7 for subtract BSR.S NextRes ; Get following resource--D0 is location. BPL.S @1 ; if one exists, calculate difference, else MOVE.L (A4), A0 ; Dereference map handle MOVE.L ResMapOffset(A0), D0 ; Get resource map offset SUB.L D7, D0 ; subtract location SUB.L ResDataOffset(A0), D0 ; and resource data offset, giving space. MOVEQ #-1, D7 ; Set CC's = MI BRA.S @2 ; and exit. @1 SUB.L D7, D0 ; subtract earlier location from new @2 MOVEM.L (SP)+, A0-A2/D7 ; Restore regs RTS ; ...and return. ; -------- UpdateResFile begins here -------- UpdateResFile BSR.S StdMapEntry ; Link, save regs, get map, exit if none. BSR ResRW ; Read/write allowed for this resFile? BNE URFExit ; no, don't allow write! ; ***Check MapChanged to see if the file needs to be updated. ; In REALITY, this should check MapUpdate (bit 4) to see if the file ; needs to be updated. This bit should be set in ChangedResource, ; AddResource/AddReference/RmveResource/RmveReference as well. The reason ; is that the resfile may need to be updated but the map might be OK. ; The HACK is that we'll use MapChanged instead of MapUpdate since I don't want ; to patch out all the other routines. This will make the map get written out ; every update whether it needs to or not, but it's better than calling SetEOF ; and causing a disk swap. MOVE.L (A4), A0 ; Dereference map handle BTST #MapChanged, MAttr(A0) ; Map Changed? (***should be MapUpdate) BEQ URFExit ; if not, don't bother to update. ; Be a Reagan Voter and check diskfull even though it might compact. BSR SetRFEOF ; Set assumed new EOF BMI URFExit ; if failed, return error. ; Write out all changed entries in the map, in file order. BSR.S FirstRes ; Point to the first resource in file order BMI.S NoEntries ; If none, go on. URLoop BTST #ResChanged, RAttr(A2) ; Test resChanged bit. BEQ.S @1 ; noWrite if -changed. BSR WrRsrc ; otherwise write out the changed resource. @1 BSR.S NextRes ; get the next entry in file order BEQ.S URLoop ; and loop back, if any left. ; Compact occurs when a resource is removed or it grows to be too big for its ; allocation in the resource file. NoEntries BCLR #MapCompact, MAttr(A3) ; Do I need to compact? BEQ.S WriteMap ; If not, skip to map write ST ResLoad ; Set resLoad to true, to load resources MOVE.L DataSize(A3), D7 ; Get old data size in D7 NEG.L D7 ; Negate it--data may be empty. BSR.S FirstRes ; Point to the first resource in file order BNE.S EndCompact ; If no resources, end compact. MOVE.L D0, D7 ; Copy location of first to D7. NEG.L D7 ; If 0, returns 0, else returns -(size) ; to compact. D7 is "compact switch". ; Compact the resource file by scanning resources in file order and possibly ; moving them. CRFLoop TST.L D7 ; Should I move down this resource? BEQ.S NoMove ; no, no space ahead. MOVE.L RHndl(A2), D3 ; Preserve handle of actual resource CLR.L RHndl(A2) ; always force a reload of a temp. resource. BSR CheckLoad ; To shuffle, load resource in. ; CheckLoad keeps A2, A3 correct. ;kwk -- 4/23/84 More fixes, this one for error case while compacting, restores ; the old handle before exiting. TST.W ResErr ; is there an error? (ie. mem full) BEQ.S @0 ; no, skip the quick exit code MOVE.L D3,RHndl(A2) ; restore the original handle BRA.S WriteMap ; write the map out, may be not compacted ;(kwk) @0 LEA RLocn(A2), A1 ; Point to attribute/location area. MOVE.B (A1), D0 ; Get attribute byte CLR.B (A1) ; Clear it ADD.L D7, (A1) ; Add the negative offset MOVE.B D0, (A1) ; and restore the attribute byte BSR WrRsrc ; Write the resource at new loc'n. MOVE.L A0, -(SP) ; Push the handle _ReleaseResource ; and release the temporary resource MOVE.L D3,RHndl(A2) ; Restore handle NoMove BSR.S SpaceAt ; D0=space available after this resource. SMI -(SP) ; remember if done MOVE.L D0, D7 ; Otherwise, NEG.L D7 ; -spaceAt... BSR SizeRsrc ; (Get size of resource in D0) ADDQ.L #LenWdLen, D0 ; (+4 for length longword) ADD.L D0, D7 ; +length of resource gives new shuffle TST.B (SP)+ ; Were we done? BNE.S EndCompact ; if so, update data size. BSR.S NextRes ; Get next resource in order BRA.S CRFLoop ; and loop back. ; File has been compacted. Unlock map and update offsets. EndCompact ADD.L D7, DataSize(A3) ; Update the data size by shuffle ; Write map out, if necessary. Compact may have not been necessary, but ; if adding resources, the map may be marked to write. WriteMap BCLR #MapChanged, MAttr(A3) ; Clear the map-changed bit before write. BEQ.S URFExit ; If the map didn't change either, exit. MOVE.L DataSize(A3), D2 ; Get updated data size ADD.L ResDataOffset(A3), D2 ; Add data offset to get new map offset MOVE.L D2, ResMapOffset(A3) ; Save new map offset from D2 MOVE.L MapSize(A3), D0 ; byte count=size of map MOVE.L A3, D1 ; Address of map in memory. D2=map offset. BSR WrData ; Write the map out! ; Write out new resource file header. ; Use same address + file refnum, but different size and location. MOVEQ #HdrSize, D0 ; byte count=HdrSize BSR WrDataSt ; Write out the header only @0. BSR SetRFEOF ; Set new EOF URFExit ; Exit through the TwoByteExit common routine. TwoByteExit MOVEQ #2, D0 ; Pop 2 bytes of parameter BRA StdExit ; and exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE CloseResFile(refNum: INTEGER); 8(A6) ; ; Updates, compacts, and closes the given resource file. ; Writes out the possibly changed map and header. Disposes of all resources ; referred to in the map. Unlinks the map from the linked list of resource ; maps, and disposes of the map handle. ; If the refnum given is 0, closes ALL resource files. ; ;-------------------------------------------------------------------------------- CloseResFile TST.W 4(SP) ; is it the system resource file? BNE.S @1 ; if not, go on, else BSR CloseFiles ; Close all files other than system @1 BSR StdMapEntry ; Link A6, save regs, get map, exit if none. ; Attempt an update, writing out all resources with the ResChanged bit set, ; updating the map, and compacting the resource file. MOVE.W D6, -(SP) ; Push the refnum _UpdateResFile ; Update the file. ; Release all the resources BSR GetEntries ; Get to the entries list. ;jwp BMI.S CRUnLink ; br if no entries ;jwp CRRelLoop TST.B RAttr(A2) ; test for ResSysRef BMI.S @2 ; If system reference, no release. MOVE.L RHndl(A2), D0 ; Get the handle BEQ.S @2 ; if nil, don't attempt a release, else MOVE.L D0, -(SP) ; push the handle _ReleaseResource ; and release it. @2 BSR NextEntry ; Go to the next entry BPL.S CRRelLoop ; If more exist, loop back. ; Unlink the map from the map list. ;jwp CRUnLink ;jwp BSR GetMap ; Get this map, and previous in D0 MOVE.L MNext(A3), A0 ; Get my next link TST.L D0 ; Did I have a previous map? (i.e. <>top?) BNE.S @3 ; If so, leave topMapHndl the same MOVE.L A0, TopMapHndl ; Otherwise save my next as top map BRA.S MapUnlinked ; ...and handle refnum stuff. @3 MOVE.L D0, A1 ; Get previous map in AReg MOVE.L (A1), A1 ; Dereference MOVE.L A0, MNext(A1) ; set previous next as my next MapUnlinked CMP.L SysMapHndl, A4 ; Am I removing the system map? BNE.S @4 ; If not, handle normally. LEA TopMapHndl, A0 ; Get the top map handle loc'n CLR.L (A0)+ ; Clear TopMapHndl, CLR.L (A0)+ ; sysMapHndl, ST (A0)+ ; set SysMap to FF, ST (A0)+ ; curMap to FF, BRA.S CloseRFile ; and close. @4 CMP.W CurMap, D6 ; Am I the current map? BNE.S CloseRFile ; No, everything's ok. If I actually am, MOVE.L (A0), A0 ; Dereference my next (which exists) MOVE.W MRefNum(A0), CurMap ; and save its refnum in CurMap. CloseRFile BSR SetFileIO ; Set up a file block _Close ; And close it. MOVE.W D0, ResErr ; Save IO result MOVE.L A4, A0 _DisposHandle ; Kill map even if error (not recoverable) CRFExit BRA.S TwoByteExit ; exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE SetResPurge(install: BOOLEAN); 8(A6) ; ; Install or remove the resource manager purge hook in the application zone. ; !!! PurgeProc is defined in HeapDefs. Should .Include it, but I don't. ; ;-------------------------------------------------------------------------------- PurgeProc .EQU 40 SetResPurge MOVE.L ApplZone, A0 ; Get the application zone MOVE.L (SP)+, D0 ; Get return address in D0 TST.B (SP)+ ; Install or remove the hook? PEA RPHook ; Push address of the purge hook BNE.S @1 ; install--leave (SP)=addr of purge hook. CLR.L (SP) ; Zero (SP) for remove. @1 MOVE.L (SP)+, PurgeProc(A0) ; Set or clear the purge hook MOVE.L D0, -(SP) ; Push return address back RTS ; and return. ; The actual purge hook code. Assumes the handle is pushed on stack. ; Smashes A0, A1 and D0 always. RPHook MOVE.L (SP)+, A1 ; Get the return address in A1 MOVE.L (SP)+, A0 ; and the handle in A0. BTST #Resource, (A0) ; Test master pointer bit BEQ.S @1 ; If not resource, just return, MOVE.L A0, -(SP) ; otherwise push the handle _WriteResource ; Attempt a write (ResChanged checked) @1 JMP (A1) ; and return to memory manager ;-------------------------------------------------------------------------------- ; ; PROCEDURE SetResLoad(autoLoad: BOOLEAN); ; ; Set automatic-loading true or false. If autoLoad=TRUE, then the ; resource manager will load resources from the disk if necessary. ; If FALSE, returns the handle, which may be empty. ; ;-------------------------------------------------------------------------------- SetResLoad MOVE.L (SP)+, A0 ; Get return address MOVE.B (SP)+, ResLoad ; Set ResLoad from proc parameter JMP (A0) ; ...and return. ;-------------------------------------------------------------------------------- ; ; FUNCTION CountResources(theType: ResType): 8(A6) ; INTEGER; 12(A6) ; ; Given the type, return the number of resources of that type accessable ; in ALL open maps. ; ;-------------------------------------------------------------------------------- CountResources BSR StdEntry ; Link A6, save regs MOVE.L (A0), D1 ; Get the type in D1 MOVEQ #0, D7 ; no resources counted BSR.S CountIResources ; Count how many resources NEG.W D7 ; Negate D7, to get number of resources MOVE.W D7, 12(A6) ; Save function result BRA FourByteExit ; exit ;-------- ; Utility CountIResources ; Given type in D1 and index in D7, count up to the correct resource of that type. ; If index<0, then just count to the end. ; Usage: Given index=0, returns D7=-(# of resources accessable) ; Given index=1's based resource index, returns A2 pointing to the entry ; or nil. CountIResources BSR GetTopMap ; Start with top map CMapLp MOVE.L D1, D3 ; Get the type in D3 for scan BSR TypeScan ; Point to the type to be counted BMI.S CNextM ; if no such type, go to next map BSR SkipToEntry ; Skip to the first entry of that type CEntryLp TST.B RAttr(A2) ; is it a system ref? BMI.S CNextE ; If so, don't count it. SUBQ.W #1, D7 ; Otherwise decrement D7 BEQ.S EndCount ; if zero, end counting CNextE BSR NextEntry ; Skip to next entry BEQ.S CEntryLp ; If everything ok, just loop CNextM ; If type changed or hit end of map, BSR NextMap ; follow link to next map BEQ.S CMapLp ; continue counting EndCount RTS ; if MI, hit end of list! ;-------------------------------------------------------------------------------- ; ; FUNCTION GetIndResource(theType: ResType; 10(A6) ; index: INTEGER): 8(A6) = (A0) ; Handle; 14(A6) ; ; Given the type and the index, return the resource handle. ; FndG and NotFndG are also used by GetResource. ;-------------------------------------------------------------------------------- GetIndResource BSR StdEntry ; Link A6, save regs MOVE.W (A0)+, D7 ; Get index in D7 BLE.S NotFndG ; (if <0, not found) MOVE.L (A0)+, D1 ; and type into d1. CLR.L (A0)+ ; Assume not found. BSR.S CountIResources ; Count up to the index BMI.S NotFndG ; not found, zero the handle ;------ FndG BSR CheckLoad ; Check handle, init/load it if needed MOVE.L A0, 14(A6) ; Save result--fall through to exit. ;------ NotFndG ; NotFndG = sixByteExit. GIRExit SixByteExit MOVEQ #6, D0 ; Pop 6 bytes of parameter BRA.S GoStdExit ;-------------------------------------------------------------------------------- ; ; FUNCTION CountTypes: INTEGER; 8(A6) ; ; Return the number of types in the current resource map. ; ;-------------------------------------------------------------------------------- CountTypes BSR StdEntry ; Link A6, save regs BSR.S CountITypes ; Stack up unique types, D7=count UNLK A6 ; Pop off the types MOVE.W D7, 8(A6) ; Set function result ZerByteExit MOVEQ #0, D0 GoStdExit BRA StdExit ; Unlink A6, restore regs ;-------- ; Utility CountITypes ; Return a list of unique types on the stack, and D7=the count. ; CountITypes MOVE.L (SP)+, A0 ; Get return address in A0 LINK A6, #0 ; Link A6 for quick unlk of types MOVEQ #0, D7 ; No types yet BSR GetTopMap ; Get the top map CITMapLp BSR GetTypes ; Get the type list BMI.S CITNxtMap ; If no types, go to next map CITTypLp MOVE.L TType(A2), D3 ; Get candidate type in D3 MOVE.L SP, A1 ; Set up climber register MOVE D7, D0 ; Get current count SUBQ #1, D0 ; Zero based for DBRA BMI.S CITAddType ; if none yet, just add CITCmpLp CMP.L (A1)+, D3 ; is this type already represented? BEQ.S CITNxtType ; If so, go to next type in map DBRA D0, CITCmpLp ; loop back for count of types CITAddType MOVE.L D3, -(SP) ; Push the new type on the stack ADDQ #1, D7 ; Increment type count CITNxtType ADDQ.L #TESize, A2 ; Skip to next type entry DBRA D5, CITTypLp ; and loop back. CITNxtMap BSR NextMap ; Link to next map BEQ.S CITMapLp ; If there are more, loop back. JMP (A0) ; and return. D7=one's based count, ; stack has the types. ;-------------------------------------------------------------------------------- ; ; PROCEDURE GetIndType(VAR theType: ResType; 10(A6) ; index: INTEGER); 8(A6) = (A0) ; ; Given the index, return the indexed type in the current resource map. ; ;-------------------------------------------------------------------------------- GetIndType BSR StdEntry ; Link A6, save regs MOVE.W (A0)+, D2 ; Get index number in D2, MOVE.L (A0), D1 ; and VAR address in D1. BSR.S CountITypes ; Make type list on stack MOVE.L D1, A0 ; Get VAR address in AReg CLR.L (A0) ; Assume failure SUBQ.W #1, D2 ; Make count zero based BMI.S GITExit ; if count was <=0, exit with error. CMP.W D2, D7 ; is it > max? BLE.S GITExit ; if so, use error result. ASL.W #2, D2 ; (count-1)*4 for offset MOVE.L 0(SP, D2), (A0) ; Set function result GITExit UNLK A6 ; Pop off the types BRA.S SixByteExit ; and exit ;-------------------------------------------------------------------------------- ; ; FUNCTION UniqueID(theType: ResType): 8(A6) ; INTEGER; 12(A6) ; ; Return a unique ID for a given type. The ID is unique across ALL maps ; that are currently open. The ID is always >0. ; ;-------------------------------------------------------------------------------- UniqueID BSR StdEntry ; Link A6, save regs MOVE.W Ticks+2, D7 ; Get low ticks for initial ID guess UILoop BSR GetTopMap ; Get the top map SUBQ #2, SP ; Make room for result _Random ; Get Bill's random number ADD.W (SP)+, D7 ; Increment ticks by a random amount. AND.W #$7FFF, D7 ; Force it positive for no good reason MOVE.L 8(A6), D3 ; Get type in D3 MOVE.W D7, D2 ; Copy ID candidate to D2. UIMapLoop BSR TypeScan ; Try to find the type BMI.S UINextMap ; if MI, no such type BSR SkipToEntry ; Skip to the first entry of that type BSR IDScan ; Now try to find the ID BEQ.S UILoop ; if found, need a new random ID. UINextMap BSR NextMap ; follow link to next map BEQ.S UIMapLoop ; If there is another map, scan it, else MOVE.W D2, 12(A6) ; Return the unique ID BRA FourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; FUNCTION GetResource(theType: ResType; 10(A6) ; theID: INTEGER): 8(A6) = (A0) ; Handle; 14(A6) ; ; Given a type and ID, return the handle to the resource. Searches ; all maps starting with the current map and ending with the system map. ; NOTE: May not search all open maps, depending which the current map is. ; FndG and NotFndG are defined in GetIndResource. ; ;-------------------------------------------------------------------------------- GetResource BSR StdEntry ; Link A6, save regs BSR GetCurMap ; Get handle to current map MOVE.W (A0)+, D2 ; Get resource ID in D2 MOVE.L (A0)+, D3 ; and type in D3 CLR.L (A0) ; Assume not found GOLoop BSR TypeScan ; Try to find the type BMI.S GoNextMap ; if MI, no such type BSR SkipToEntry ; Skip to the first entry of that type BSR IDScan ; Now try to find the ID BEQ.S FndG ; If EQ, found it! Otherwise try next map GoNextMap BSR NextMap ; follow link to new map BEQ.S GOLoop ; If there is another map, try again, BRA.S NotFndG ; else not found. ;-------------------------------------------------------------------------------- ; ; FUNCTION GetNamedResource(theType: ResType; 12(A6) ; theName: Str255): 8(A6) ; Handle; 16(A6) ; ; Given a type and name, return the handle to the resource. Searches ; all maps starting with the current map and ending with the system map. ; NOTE: May not search all open maps, depending which the current map is. ; ;-------------------------------------------------------------------------------- GetNamedResource BSR StdEntry ; Link A6, save regs BSR.S GetCurMap ; Get the current map MOVE.L (A0), A1 ; Get name pointer in A1. CLR.L 16(A6) ; Assume the resource was not found GNMapLoop MOVE.L 12(A6), D3 ; Get type of resource in D3 for scan. BSR TypeScan ; Look for the type BMI.S GNNextMap ; If no such type found, get next map BSR SkipToEntry ; Walk through the entries of this type. GNResLoop BSR NameResource ; Given RNameOff(A2), return name in A0. BMI.S NextGN ; if CC=MI, didn't find a name. ; A1 is still the target string. MOVEQ #0, D0 ; Clear out high bits MOVE.B (A0)+, D0 ; Get length (trash A0!) SWAP D0 ; and put it in high order MOVE.B (A1)+, D0 ; Get length of second string in low order _CmpString ; Check equality of strings in A0 and A1 SUBQ #1, A1 ; Point back to the Pascal string in A1 BEQ.S FndGN ; If equal, load it in! NextGN BSR NextEntry ; Skip to next entry BEQ.S GNResLoop ; If not changed type, get next GNNextMap BSR.S NextMap ; Link to next map BEQ.S GNMapLoop ; if one exists, continue, otherwise BRA.S NotFndGN ; didn't find it. FndGN BSR CheckLoad ; otherwise load it in, if necessary. MOVE.L A0, 16(A6) ; Save the handle as function result NotFndGN EightByteExit MOVEQ #8, D0 ; pop off eight bytes of parameter BRA StdExit ; and exit. ;jwp ;_______________________________________; ; ; ; SizeResource (new routine) ; ;_______________________________________; SizeResource MOVE.L MinusOne,8(SP) ; assume it can't be found BSR StdResEntry ; Link A6, save regs TST.L (A1) ;has it been purged? BEQ.S @0 ;if so, read size from file MOVE.L A1,A0 ;get handle in A0 _GetHandleSize ;whats the size? BRA.S @1 ;use common exit ; the resource is not in memory so read its size from the file @0 MOVE.B RAttr(A2),-(SP) ;save state of attributes BSR SizeRsrc ;figure out the size MOVE.B (SP)+,RAttr(A2) ;restore attributes state ; SizeResource exit code @1 MOVE.L D0,12(A6) ;return the result BRA FourByteExit ;use common exit ;jwp ; FILE RmgrAsm2.text ; MACWORKS Copy ;****************************************************************************** ; RMgrAsm2 ; Resource Manager ; ; Written by Bruce Horn 22 November 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains Resource Manager utilities only. ; ; ;****************************************************************************** ; ; Modification history given in RMgr. ; ; Version 7.0. Last modified 10-Sep-83 by Bruce. ; ;****************************************************************************** ;------------------------ ;----------GET*---------- ; Given a parameter, point registers to * ; ;---- ; GetTopMap ; Point to the top map ; GetTopMap MOVE.L TopMapHndl, A4 ; Get topMap handle MOVE.L (A4), A3 ; Dereference it MOVE.W MRefNum(A3), D6 ; Get reference number BRA.S CopyA2 ; Return, copy pointer in A2 ;---- ; GetCurMap, GetMap ; Given map refnum in D6 ; Returns map handle in A4, pointer to block in A3 and A2. ; Previous map in D0 GetCurMap MOVE.W CurMap, D6 ; Get current resFile refnum. GetMap TST.W D6 ; Test refnum (system special, or none) BMI NotFound ; if -ve, no maps open--exit with error. BNE.S @1 ; If not zero, use standard resource MOVE.W SysMap, D6 ; Otherwise use refnum of system map @1 MOVE.L TopMapHndl, A4 ; Get top map handle MOVEQ #0, D0 ; Initialize prev map handle BRA.S NMap ; Get next map NxtMap MOVE.L MNext(A3), D0 ; Get handle to next BEQ NotFound ; If the handle was nil, not found EXG A4, D0 ; set A4, and last map handle in D0. NMap MOVE.L (A4), A3 ; Dereference CMP.W MRefNum(A3), D6 ; Is it the right resource map? BNE.S NxtMap ; no, skip to the next one CopyA2 MOVE.L A3, A2 ; Copy it in A2 RTS ; Block handle in A4, ptr in A3 and A2 ;---- ; NextMap ; Given map handle in A4 ; Set up map handle, pointer, refnum of next map. Returns EQ if found, MI if not NextMap MOVE.L (A4), A3 ; Dereference map handle MOVE.L MNext(A3), A4 ; Get next handle MOVE.L A4, D0 ; Was the handle zero? BEQ.S NotFound ; If no more maps, not found MOVE.L (A4), A3 ; Otherwise dereference MOVE.W MRefNum(A3), D6 ; Get new refnum also BRA.S Found ; Say it was found ;---- ; GetTypes ; Given map handle in A4, ; Returns A2 pointing to the first type. A3 points to type block. ; D5 is type count ; GetTypes MOVE.L (A4), A3 ; Dereference map handle ADD.W MTypes(A3), A3 ; Add offset to type list MOVE.L A3, A2 ; Copy top of block MOVE.W (A2)+, D5 ; Get type count, point to first type RTS ;---- ; GetEntries ; Given map handle in A4 ; Returns A2 pointing to the first ID ; A3 points to type block ; D3 is the first type ; D4 is entry count (of the first type) ; D5 is type count ; GetEntries BSR.S GetTypes ; A2 points to first type, D5 is NTypes MOVE.L TType(A2), D3 ; Set the type BSR.S SkipToEntry ; Skip to the first entry of that type TST.W D5 ; Set CC's RTS ; ...and return. ;------------------------- ;----------*Scan---------- ; Scan for * within the map. If found, return CC=EQ. If not found, return CC=MI. ; A2 points to the correct entry. ;---- ; IDScan ; Given A2 pointing to first entry of a given type, ID in D2, entry count in D4 ; Returns A2 pointing to the correct entry, or NotFound. ; IDScan CMP.W RID(A2), D2 ; Is it the right ID? BEQ.S Found ; Yes, return to caller ADD.W #RESize, A2 ; Skip to next ID, if any DBRA D4, IDScan ; Loop back BRA.S NotFound ; Otherwise not found ;---- ; TypeScan ; Given type in D3, map handle in A ; Returns A2 pointing to the type entry ; D4 is entry count ; D5 is type count remaining (for DBRA) ; TypeScan BSR.S GetTypes ; A2 -->first type, D5 is type count BMI.S NotFound ; No types listed! CmpType CMP.L TType(A2), D3 ; Is it the right type? BEQ.S Found ; Yes, return "found", otherwise ADDQ #TESize, A2 ; Skip to next type DBRA D5, CmpType ; Decrement NTypes and try again BRA.S NotFound ; If made it to here, not found ;---- ; SkipToEntry ; Given A2 pointing to a type entry ; Returns A2 pointing to the first resource entry of that type, or nil. ; D4 is entry count ; D5 is type count remaining (for DBRA) ; SkipToEntry MOVE.W TCount(A2), D4 ; Copy entry count of first type into D4 MOVE.W TOffset(A2), A2 ; Copy offset to first entry ADD.L A3, A2 ; Adding in block address gives entry address RTS ; ...and return ;-------------------- ;---- ; Found ; Sets condition codes for found Found MOVEQ #0, D0 ; Set CC=EQ (found) RTS ;---- ; StdEntry ; Links A6, saves ALL regs except A0 and D0. ; A0 is returned pointing to the last parameter, for (A0)+ traversals. ; StdEntry CLR.W ResErr ; Clear the error flag MOVE.L (SP)+, A0 ; Get return address in A0 LINK A6, #RMgrStack ; Link A6 for IOStkFrame MOVEM.L A1-A4/D1-D7, -(SP) ; Save registers MOVE.L A0, -(SP) ; Push A0 back on as return LEA 8(A6), A0 ; Point A0 at last parameter RTS ; and return. ;---- ; StdExit ; Unlinks A6, restores ALL regs except A0, D0. ; Call ResErrHook if ResErr<>0. ; Pops off D0 bytes of parameters. ; ; ResErrProc called with error in D0 and return address of caller on stack. ; The hook can therefore filter errors and return to caller. StdExit MOVEM.L (SP)+, A1-A4/D1-D7 ; Restore registers UNLK A6 ; Unlink A6 MOVE.L (SP)+, A0 ; Get return address in A0 ADD.L D0, SP ; Pop off parameters MOVE.L A0, -(SP) ; Push return back MOVE.W ResErr, D0 ; Get ResErr in D0 BEQ.S ExitOut ; If equal, just return, otherwise MOVE.L ResErrProc, -(SP) ; push error proc BNE.S ExitOut ; if exists, return, else, ADDQ #4, SP ; pop off error proc address ExitOut RTS ; ...and return ;---- ; NotFound ; Sets condition codes to note that the resource was not found ; NotFound MOVEQ #-1, D0 ; Set MI flag (==notFound) RTS ;-------------------- ;---- ; RefScan ; Given resource handle in A1 ; Returns A2 pointing to entry which is a reference to the resource in A1, or ; points to the resource itself. RefScan BSR.S HandleScan ; A2 points to the resource entry BMI.S NotFound ; If not found, can't have a reference! CMP.W SysMap, D6 ; Does the resource live in the system? BNE.S NotFound ; If not, can't be a reference. MOVE.W RID(A2), D2 ; Get the ID of the resource into D2 BSR.S GetCurMap ; Get the current map BSR.S TypeScan ; Scan to the right type (set by HandleScan) BMI.S NotFound ; If the type not found, reference not found. BSR.S SkipToEntry ; ...and skip to first entry of that type CkRef TST.B RAttr(A2) ; Is it a system reference? BPL.S @1 ; no, skip to next entry CMP.W RefID(A2), D2 ; Is it the right one? BEQ.S Found ; Yes, found it! @1 BSR.S NextEntry ; Go to the next entry BEQ.S CkRef ; and try next one BRA.S NotFound ; Otherwise not found in current map ;---- ; HandleScan ; Given resource handle in A1 ; Returns A2 pointing to entry with the correct handle, or nil. Scans all maps. ; HandleScan MOVE.L A1, D0 ; Check for zero handle BEQ.S NotFound ; Can't have a zero handle, say not found BSR.S GetTopMap ; Point to the topmost map first LMLoop BSR.S GetEntries ; Point to the resource entries BMI.S HSNextMap ; If no entries, link to next map. HSLoop TST.B RAttr(A2) ; Test for reference bit BMI.S @1 ; if a reference, don't check. CMP.L RHndl(A2), A1 ; Is it the same handle? BEQ.S Found ; Yes, exit with found. @1 BSR.S NextEntry ; Go to the next entry BPL.S HSLoop ; Loop back if more entries HSNextMap BSR.S NextMap ; Try to link to next map BEQ.S LMLoop ; If there is one, scan it, otherwise BRA.S NotFound ; the handle wasn't found. ;---- ; NameResource ; Given A2 pointing to a resource entry ; Return A0 pointing to the resource name. ; ; Returns LT (MI) if not found NameResource MOVE.W RNameOff(A2), D0 ; Get the name offset BMI.S NRFailed ; if negative, return empty string. MOVE.L (A4), A0 ; Dereference map handle ADD.W MNames(A0), A0 ; Jump to names block ADD.W D0, A0 ; and offset to the name BRA.S Found ; say found. NRFailed LEA NullStr, A0 ; Get address of an empty string, BRA.S NotFound ; and say not found ;---- ; NextEntry ; Makes A2 point to the next ID resource entry ; ; Returns LT (MI) if not found, ; EQ if found ; GT if changed type ; NextEntry ADD.W #RESize, A2 ; Point to the next entry (sign extended) SUBQ.W #1, D4 ; Decrement resource entry count BPL.S Found ; Return if still more entries left SUBQ.W #1, D5 ; Decrement type entry count BMI.S NotFound ; If negative, hit end of map MOVE.W TypeCount(A3), D0 ; Get number of types SUB.W D5, D0 ; Get type index ASL.W #TypeShift, D0 ; *8 gives offset to the type MOVE.L TType+2(A3, D0), D3 ; Get the type in D3 MOVE.W TCount+2(A3, D0), D4 ; Get number of items in D4 MOVEQ #1, D0 ; Set CC=GT, for "typeChanged" flag RTS ; and return ;---------------- ; ; CheckLoad ; A2 points to a resource entry. ; Initializes the handle, first-time if needed, and loads in if ResLoad is true. ; Returns handle in A0. ; Trashes D0-D2, A0-A1. ; ; In the case of a reference being loaded, the refname is tried before the refID. ; If a reference is being loaded, A2 and A3 will become invalid. This is OK ; since CheckLoad in that situation is called at the end of either LoadResource ; or Get/GetNamedResource, in which case A2 and A3 are not used again. ; ; In the non-reference case, A2 and A3 are made offsets and restored after ; the call--therefore they are preserved and correct. CheckLoad MOVE.B RAttr(A2), D1 ; Get resource info from RAttr BPL.S LoadIt ; If not a reference, load in as is. BSR.S SwapCS ; Otherwise use system as current ADDQ #RefID, A2 ; Skip to reference fields MOVE.W (A2), D1 ; copy refID into D1 for getResource later BSR.S NameResource ; A0 points to the reference name, if any BMI.S TryID ; If no name, try ID SUBQ #4, SP ; Save room for handle MOVE.L D3, -(SP) ; Push the type MOVE.L A0, -(SP) ; Push the name pointer _GetNamedResource ; Get the named resource MOVE.L (SP)+, A0 ; Get the handle MOVE.L A0, D0 ; Was it zero? BNE.S SwapCS ; if not, return, A0 is the handle TryID SUBQ #4, SP ; Save room for the handle MOVE.L D3, -(SP) ; Push the type MOVE.W D1, -(SP) ; Push the ID of reference _GetResource ; Try and get resource handle MOVE.L (SP)+, A0 ; Get the handle SwapCS MOVE.W CurMap, D0 ; Get current map MOVE.W SysMap, CurMap ; Make system map current MOVE.W D0, SysMap ; and current map system RTS ; ...return ; Actually load the resource. LoadIt MOVE.L theZone, -(SP) ; Save old zone BTST #ResSysHeap, D1 ; Is it in the system heap? BEQ.S @1 ; No, use same heap MOVE.L SysZone, theZone ; otherwise set the system zone for now @1 MOVE.L RHndl(A2), A0 ; Get the handle in A0, if any. SUB.L (A4), A3 ; Make A3 an offset TST.B ResLoad ; should the resource be loaded, if nec.? BEQ.S NoResLoad ; no, just create empty handle, if needed. MOVE.L A0, D0 ; Is there a handle yet? BNE.S GotHandle ; If so, go on BSR.S PrepHandle ; Set up size, reserve mem if necessary. _NewHandle ; create a handle of the right size BEQ.S LoadRes ; if successful, load it, else BRA.S ErrLoad ; exit with error. PrepHandle BSR.S SizeRsrc ; get the size of the resource in D0. SUB.L (A4), A2 ; Make A2 an offset. BTST #ResLocked, D1 ; Is this going to be a locked handle? BEQ.S @2 ; No, allocate as usual, floating in heap. MOVE.L D0, D2 ; Save D0=length otherwise ; _ResrvMem ; reserve low memory for the locked handle .Word $A040 ; (Save/restore A0 across ResrvMem call!) MOVE.L D2, D0 ; restore D0 for memory mgr call @2 RTS ; and return. GotHandle TST.L (A0) ; is resource already in? BNE.S EndLoad ; if so, just return, otherwise BSR.S PrepHandle ; Set up size, reserve mem if necessary, _ReallocHandle ; and reallocate it to correct size. BMI.S ErrLoad ; if unsuccessful, don't try to load. ; RELOAD the resource, or possibly LOAD it for the first time. LoadRes ADD.L (A4), A2 ; Restore A2 MOVE.L A0, RHndl(A2) ; Set RHndl(A2) for RdRsrc BSR.S RdRsrc ; read the resource. BSR.S SetPL ; set purgable, lock bits in MP MOVE.W ResErr, D0 ; was there a read error? (D0 for errLoad) BEQ.S EndLoad ; If not, everything is ok. A2 correct. SUB.L (A4), A2 ; Make A2 an offset again for ErrLoad ErrLoad MOVE.W D0, ResErr ; Report error SUB.L A0, A0 ; Zero handle BRA.S EndLoadA2 ; and restore A2. ; ResLoad is FALSE. If there is already a handle, return, else allocate new. NoResLoad MOVE.L A0, D0 ; Otherwise test the handle BNE.S EndLoad ; If there is one, just return. A2 correct. SUB.L (A4), A2 ; Make A2 an offset for NewHandle call. MOVEQ #4, D0 ; Have to make a new handle _NewHandle ; Create one of minimum size _EmptyHandle ; Empty it, for load next time EndLoadA2 ADD.L (A4), A2 ; Finally restore A2. EndLoad MOVE.L A0, D0 ; Is there a good handle? BEQ.S @1 ; If not, don't write in map MOVE.L A0, RHndl(A2) ; otherwise copy it in Hndl location @1 ADD.L (A4), A3 ; Restore A3 for caller MOVE.L (SP)+, theZone ; Restore the zone RTS ; ...and return ;---- ; SetPL ; Set purgable, lock and resource bits. ; D1 = purgeable, lock bits. Smashed after SetPL. ; A0 = resource handle. High 3 bits of MP set. ; SetPL LSL.B #3, D1 ; Shift over purge, lock bits in D1. BPL.S @1 ; if lock 0, branch MOVEQ #-127, D1 ; Else set Lock, Resource bits. ($81) BRA.S @2 ; skip to rotate @1 MOVEQ #-128, D1 ; Set Resource bit, others zero. ($80) @2 ROXR.B #2, D1 ; X and C have purge bit. Shift back... AND.B #$1F, (A0) ; Mask off everything but lock and purge OR.B D1, (A0) ; Set Lock, Purge, and Resource only. RTS ; finally return. ;---- ; SizeRsrc ; A2=pointer to resource entry ; Returns the size of the resource in D0 by reading the size word from the ; resource file. ; If memory manager error, sets ResErr. SizeRsrc MOVEM.L D1-D2/A0-A1, -(SP) ; Save regs BSR.S RREntry ; Smashes changed, added bits. SUBQ.L #LenWdLen, SP ; Save space for length longword MOVE.L SP, D1 ; Point buffer to stack MOVEQ #LenWdLen, D0 ; Read 4 bytes BSR.S RdData ; Do it! MOVE.L (SP)+, D0 ; Get length in D0 MOVEM.L (SP)+, D1-D2/A0-A1 ; Restore regs RTS ; ...and return. ;---- ; RdRsrc ; A2=pointer to resource entry ; loads the resource into the handle specified in RHndl(A2). ; The handle MUST be the correct size. RdRsrc MOVEM.L D0-D2/A0-A1, -(SP) ; Save regs BSR.S RREntry ; Set up the regs for read/write BSR.S SizeRsrc ; Get the size of the resource in D0 ADDQ.L #LenWdLen, D2 ; Skip over the length longword MOVE.L (A1), D1 ; Set buffer address BSR.S RdData ; and read the bytes in! ;------------------------ ;Rd/WrRsrc utilities ;------------------------ ;--- ; RRExit ; Restore regs and return from Rd/WrRsrc. RRExit MOVEM.L (SP)+, D0-D2/A0-A1 ; Restore regs RTS ; ...and return! ;--- ; RREntry ; Given A2-->resource entry, sets up regs for read/write. ; Returns D2=address of entry, A1=handle, A0=map pointer. RREntry ANDI.B #RCBMask, RAttr(A2) ; Clear resChanged on read/write. MOVE.L (A4), A0 ; Dereference map handle again MOVE.L RLocn(A2), D2 ; Get location long word AND.L MaskBC, D2 ; Clear high 8 bits. ADD.L ResDataOffset(A0), D2 ; Add in the data offset MOVE.L RHndl(A2), A1 ; Get the handle in A1 RTS ; ...and return. ;---- ; WrRsrc ; A2=pointer to resource entry ; A4=handle to map ; Writes the resource specified in RHndl(A2) out to the resource file WrRsrc MOVEM.L D0-D2/A0-A1, -(SP) ; Save regs BSR.S RREntry ; Set up the regs for read/write MOVE.L A1, A0 _GetHandleSize ; Get the size to write out TST.L D0 ; what happened BPL.S @1 ; if good size, go on, else MOVEQ #0, D0 ; force zero length (purged!) @1 MOVE.L D0, -(SP) ; Push size on the stack MOVE.L SP, D1 ; Write the size out (buffer addr.=stack) MOVEQ #LenWdLen, D0 ; Write 4 bytes BSR.S WrData ; Write out length word data ADDQ.L #LenWdLen, D2 ; Skip over length longword MOVE.L (A1), D1 ; Read bytes from handle MOVE.L (SP)+, D0 ; Restore the size to D0 for write BSR.S WrData ; ...and do it! BRA.S RRExit ; Restore regs and exit. ;---- ; Utility SetFileIO ; ; Sets A0 to point to the IO Stack Frame, and fills out the following vars: ; D0=bytes to read/write ; D1=IO buffer address ; D2=IO position offset ; D6=file refnum ; Smashes A0. SetFileIO LEA IOStkFrame+IORefNum(A6), A0 ; Point to frame at IORefNum MOVE.W D6, (A0)+ ; Set IORefnum ADDQ #6, A0 ; Skip over unused stuff MOVE.L D1, (A0)+ ; Set IOBuffer MOVE.L D0, (A0)+ ; Set IOByteCount ADDQ #4, A0 ; Skip actual byte count MOVE.W #1, (A0)+ ; Set IOPosMode=absolute MOVE.L D2, (A0)+ ; and finally set IOPosOffset. LEA IOStkFrame(A6), A0 ; Reset A0 for stack frame RTS ; and return. ;---- ; RdData ; D0=bytes to read ; D1=buffer address ; D2=position offset ; D6=file refnum ; For both RdData & WrData, assumes that A4 is valid handle, then re-validates ; A2/A3 after file system call, which might move the heap. Rd/WrData1 are the ; old Rd/WrData routines, which don't assume A4 is valid. RdData1 ; this is the old RdData routine (kwk) MOVEM.L A0-A1/D0, -(SP) ; Save regs BSR.S SetFileIO ; Set up a file frame _Read ; Read the bytes BRA.S IOExit RdData MOVEM.L A0-A1/D0, -(SP) ; Save regs BSR.S SetFileIO ; Set up a file frame SUB.L (A4),A3 ; make A3 from ptr to offset (kwk) SUB.L (A4),A2 ; make A2 from ptr to offset (kwk) _Read ; Read the bytes ADD.L (A4),A3 ; turn A3 back into ptr (kwk) ADD.L (A4),A2 ; turn A2 back into ptr (kwk) BRA.S IOExit ;---- ; WrData, WrDataSt ; D0=bytes to read ; D1=buffer address ; D2=position offset ; D6=file refnum WrData1 ; This is the old WrData routine (kwk) MOVEM.L A0-A1/D0, -(SP) ; Save regs BSR.S SetFileIO ; Set up a file frame _Write ; Write the bytes BRA.S IOExit ; and exit WrDataSt MOVEQ #0, D2 ; PosOffset=0 WrData MOVEM.L A0-A1/D0, -(SP) ; Save regs BSR.S SetFileIO ; Set up a file frame SUB.L (A4),A3 ; make A3 from ptr to offset (kwk) SUB.L (A4),A2 ; make A2 from ptr to offset (kwk) _Write ; Write the bytes ADD.L (A4),A3 ; turn A3 back into ptr (kwk) ADD.L (A4),A2 ; turn A2 back into ptr (kwk) ;---- ; IOExit--return for RdData, WrData, SetRFEOF. IOExit MOVE.W D0, ResErr ; Save file I/O result in ResErr IOExit2 MOVEM.L (SP)+, A0-A1/D0 ; Restore regs RTS ; ...and return ;---- ; Utility SetRFEOF ; ; Set the resource file EOF, given the resfile parameters resMapOffset and ; mapSize. ; SetRFEOF MOVEM.L A0-A1/D0, -(SP) ; Save regs BSR.S ResRW ; Read/write allowed? BNE.S IOExit2 ; if not, just exit! BSR SetFileIO ; Set up a file block MOVE.L (A4), A1 ; Dereference map handle MOVE.L ResMapOffset(A1), D0 ; Get map offset ADD.L MapSize(A1), D0 ; and add map size to get EOF. MOVE.L D0, IOLEOF(A0) ; Set EOF in parameter block _SetEOF ; call the FS BRA.S IOExit ; and exit through common code ;---- ; Utility ReallocRes, AllocRes ; ; Given A2 pointing to a resource entry, allocate a space for the resource in ; the resource file. If the current location is ok, just return. If the current ; location is too small, allocate the resource on the end, updating the ResMapOffset ; and DataSize. ; ; The resource MUST be in memory. The resfile may be trashed if not. ; ; AllocRes just bypasses the spaceAt check, and automatically allocates the ; resource at the end. Does not grow the resource file--assumes a previous ; call to CheckGrow. ; ;Smashes A0, D0, D1. ; ReallocRes BSR.S GetHS ; Get handleSize+4 in D1 BSR SpaceAt ; How much space available here? BPL.S AllocMiddle ; if not end resource, alloc in middle. ; Reallocating space for last resource. Do special update for mapoffset. SUB.L D0, D1 ; If space exists at end, BLE.S ARExit ; just return. BSR.S SetDS ; Update DataSize and resMapOffset by D1 BRA.S ARMapExit ; and return, updating map. ;--- ; Get ResourceSize+4 in D1 GetHS MOVE.L RHndl(A2), A0 ; Get the resource handle from A2 _GetHandleSize ; Get the size of the handle. ADDQ #LenWdLen, D0 ; add 4 for length longword MOVE.L D0, D1 ; Copy to D1 for caller. RTS ;--- ; Get dataSize in D0, then update resMapOffset and DataSize by D1. ; Return A0 pointing to header. SetDS MOVE.L (A4), A0 ; Dereference map handle ADDQ.L #ResMapOffset, A0 ; Point to map offset ADD.L D1, (A0)+ ; update resMapOffset by D1. MOVE.L (A0), D0 ; Get DataSize in D0 (=new res loc'n) ADD.L D1, (A0) ; and update DataSize by D1 also. SUBQ.L #DataSize, A0 ; Back up A0 to header start RTS ; ... and return. ; Reallocating space for a resource in the middle of the file. ; D0=space available for the resource, D1=resourceSize+2. AllocMiddle CMP.L D0, D1 ; Compare with resourceSize+4 BLE.S ARExit ; if it fits, just return, else MOVE.L (A4), A0 ; Dereference map handle BSET #mapCompact, MAttr(A0) ; Set mapCompact bit to compact on close. AllocRes BSR.S GetHS ; Get handleSize+2 in D1 BSR.S SetDS ; Update DataSize + ResDataOffset by D1. ; Returns (A4)=A0, and D0=old DataSize. MOVE.B RLocn(A2), -(SP) ; Save attributes byte on stack. MOVE.L D0, RLocn(A2) ; Set location to old DataSize, and MOVE.B (SP)+, RLocn(A2) ; Restore attributes byte. ARMapExit BSET #mapChanged, MAttr(A0) ; Set mapChanged bit ARExit BRA.S SetRFEOF ; Set the resource file EOF now ; exit through RFEOF and IOExit. ;---- ; Utility ResRW ; ; Checks the read-only bit in the map attributes word, and returns the CC's. ; ResRW MOVE.L (A4), A0 ; Dereference map handle BTST #MapReadOnly, MAttr(A0) ; Set CC's RTS ; and return. ;---- ; Utility CheckGrow ; ; A1=resource handle which will be added to the resource file. ; D6=resFile refnum. ; ; CheckGrow checks for the DiskFull and MemFull conditions. ; It assumes the maximum overhead for adding the information to the map, as well ; as adding the data to the resource file. 256 bytes for max name size, 12 for ; the resource entry, and 6 for the type entry for a maximum delta of 274 bytes ; for the map. If the resource is added to the end of the resource file, ; 4+sizeOfResource+274 bytes will be the amount that the resource file may need ; to grow, worst case. For convenience, 280+sizeOfResource will be used. ; ; If the resource file in question is read-only, the diskFull test will not be ; performed. Be aware of this feature. ; ; If it is safe to grow both the map handle in memory and the resource file on ; disk, CheckGrow will return with CC=EQ, and ResErr=0. If either the memFull ; or DiskFull conditions occur, CheckGrow will return with CC=MI and ResErr=the ; error code. ; ; Keeps A2, A3 correct ptrs (calculates and restores with offsets). ; Other registers, if pointing inside the map block, may become invalid due to ; the SetHandleSize moving the block. CheckGrow MOVEM.L A0-A1/D0-D2, -(SP) ; Save working regs SUB.L (A4), A3 ; A3 := offset from beginning of handle SUB.L (A4), A2 ; A2 := offset from beginning of handle MOVEQ #70, D1 ; Get D1=70, then ASL.W #2, D1 ; *4=280, max overhead possible. MOVE.L A4, A0 ; Get map handle in A0 _GetHandleSize ; Get the size MOVE.L D0, D2 ; Save size in D2 ADD.L D1, D0 ; Increase by max overhead _SetHandleSize ; Grow it EXG D0, D2 ; Get old size, save error code in D2 _SetHandleSize ; and shrink it back. TST.W D2 ; Was there an error? BMI.S CGExit ; if so, set resErr on exit, D2=code. BSR.S ResRW ; Read/write allowed? BNE.S CGExit ; if not, exit without doing diskFull check. MOVE.L A1, D0 ; is there a handle to account for? BEQ.S @1 ; if not, don't add length... MOVE.L A1, A0 ; Copy to A0 for GetHandleSize call _GetHandleSize ; get the handle size in D0 MOVE.L D0, D2 ; Assume the handle was purged BMI.S CGExit ; if so, report error ADD.L D0, D1 ; otherwise add length to other overhead @1 BSR.S SetFileIO ; Set up the file frame LEA IOLEOF(A0), A1 ; A1 points to EOF loc'n _GetEOF ; Get the current EOF MOVE.L (A1), D2 ; Copy it to D2. ADD.L D1, (A1) ; increment it by maximum grow value _SetEOF ; and set it back. was it successful? EXG D0, D2 ; D2 := error, D0 := current eof. MOVE.L D0, (A1) ; Restore old EOF _SetEOF ; and set it back. CGExit MOVE.W D2, ResErr ; Set ResErr and CC's ADD.L (A4), A3 ; A3 := correct pointer in block ADD.L (A4), A2 ; A2 := correct pointer in block. MOVEM.L (SP)+, A0-A1/D0-D2 ; Restore working regs RTS ; and return. ; FILE RmgrAsm3.text ; MACWORKS Copy ;****************************************************************************** ; RMgrAsm3 ; Resource Manager ; ; Written by Bruce Horn 22 November 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file continues the Resource Manager routines. ; ;****************************************************************************** ; ; Modification history given in RMgr. ; ; Version 7.0. Last modified 10-Sep-83 by Bruce. ; ;****************************************************************************** ;-------------------------------------------------------------------------------- ; ; PROCEDURE LoadResource(theResource: Handle); ; ; Given the handle, loads the resource from its resource file, ; if necessary. Saves all registers INCLUDING A0 and D0. ; theResource is 20(A6), over fake return+A0/D0+actual return. ;-------------------------------------------------------------------------------- LoadResource MOVEM.L A0/D0, -(SP) ; Save extra registers PEA LRFinalExit ; Fake out stdExit to return here BSR StdEntry ; Do standard entry MOVE.L 20(A6), A1 ; Get the resource handle, way up on stack TST.L (A1) ; Was it purged? BNE.S LRExit ; No, don't load BSR HandleScan ; Otherwise set up A2 to point to the entry BEQ.S @1 ; if found, go on, else MOVE.W #ResNotFound, ResErr ; Set ResErr BRA.S LRExit ; and exit. @1 MOVE.B ResLoad, -(SP) ; Save current value of ResLoad ST ResLoad ; Set TRUE temporarily, forcing a load. BSR.S CheckLoad ; load the resource in, if needed MOVE.B (SP)+, ResLoad ; and restore the value of ResLoad LRExit BRA ZerByteExit ; Fake out stdExit to not pop params. LRFinalExit ; returns here actually... MOVEM.L (SP)+, A0/D0 ; Gotta restore my regs first MOVE.L (SP)+, (SP) ; Pop off handle, preserving retaddr RTS ; ...and return. ;-------------------------------------------------------------------------------- ; ; PROCEDURE ReleaseResource(theResource: Handle); 8(A6) ; ; Given the handle, releases the resource and disposes of the handle. ; Any GetResource to the released resource will then cause a disk access ; and return a new handle. ; Error condition handled in StdResEntry. ; ;-------------------------------------------------------------------------------- ReleaseResource BSR.S StdResEntry ; Link, save regs, scan for handle. BTST #ResChanged, RAttr(A2) ; is it marked to be written? BNE.S RDExit ; if so, can't release! MOVE.L A1, A0 ; Copy handle to A0 _DisposHandle ; Dispose of handle BRA.S RDCmn ; and clear the handle location in map ;-------------------------------------------------------------------------------- ; ; PROCEDURE DetachResource(theResource: Handle); 8(A6) ; ; Given the handle, releases the resource only. Does NOT dispose of the ; handle. Any GetResource to the released resource will then cause a ; disk access and return a new handle. ; Error condition handled in StdResEntry. ; ;-------------------------------------------------------------------------------- DetachResource BSR.S StdResEntry ; Link, save regs, scan for handle. BTST #ResChanged, RAttr(A2) ; is it marked to be written? BNE.S RDExit ; if so, can't detach! BCLR #Resource, (A1) ; Otherwise, say handle not a resource RDCmn CLR.L RHndl(A2) ; Clear the handle location in map only RDExit BRA.S SFourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; PROCEDURE ChangedResource(theResource: Handle); 8(A6) ; ; Mark the resource to be written out into the resource file. ; If protected, won't allow change to occur. ; If read-only flag set, won't do the diskFull check, though if at a later ; time the read-only flag is cleared, it will be written. Be careful! ; diskFull may occur at that time, which will destroy your resource file! ; ResNotFound condition handled in StdResEntry. ; ;-------------------------------------------------------------------------------- ChangedResource BSR.S StdResEntry ; Link, save regs, scan for handle. MOVE.B (A1), D7 ; Save state of lock/purge/res bits BCLR #Purge, (A1) ; Don't allow purge here (checkGrow) BTST #ResProtected, RAttr(A2); Is it protected? BNE.S CRExit ; if so, don't allow change. BSR.S CheckGrow ; Can it grow resource file + map? BMI.S CRExit ; if not, just exit with error, otherwise BSET #ResChanged, RAttr(A2) ; Set the changed bit for this resource. BSR ReallocRes ; and allocate it in-place or at end. BSR.S SetMapChanged ; ***Hack! really want MapUpdate CRExit MOVE.B D7, (A1) ; Restore state of MP bits SFourByteExit BRA.S FourByteExit ; exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE WriteResource(theResource: Handle); 8(A6) ; ; Given the handle, writes out the resource back to the resource file. ; ResChanged is checked, and the resource not written unless TRUE. ; Error condition handled in StdResEntry. ; ;-------------------------------------------------------------------------------- WriteResource BSR.S StdResEntry ; Link, save regs, scan for handle. BSR ResRW ; Read/write allowed on this file? BNE.S WRExit ; if not, don't allow the write! BTST #ResChanged, RAttr(A2) ; test resChanged attribute BEQ.S WrExit ; Don't write if not changed. BSR WrRsrc ; Else write out the changed resource WRExit BRA.S FourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; Utility StdResEntry ; Given the handle 8(A6), scans for the handle and exits if not found. ; Used by ReleaseResource, DetachResource, WriteResource, HomeResFile, ; and ChangedResource. ; StdEntry returns with A0 pointing to the last argument (the handle). ; Callers must have a four-byte argument. StdResEntry MOVE.L (SP)+, D0 ; Get return address BSR StdEntry ; Link A6, save regs MOVE.L D0, -(SP) ; Push return back. MOVE.L (A0), A1 ; Get the resource handle BSR HandleScan ; Scan for it BEQ.S CurResExit ; If found, just return (through CurResFile) ADDQ #4, SP ; Otherwise, pop the return address MOVE.W #ResNotFound, ResErr ; Set error code, BRA.S FourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; FUNCTION HomeResFile(theResource: Handle): INTEGER; ; ; Given a resource handle, returns the refnum of the resource file that ; the resource lives in. If the resource is not found, returns -1. ; Error condition handled in StdResEntry. ; ;-------------------------------------------------------------------------------- HomeResFile MOVE.W MinusOne, 8(SP) ; Assume failure. BSR.S StdResEntry ; Get the resource, and return if NIL. CMP.W SysMap, D6 ; Is it the system map? BNE.S @1 ; No, leave refnum, MOVEQ #0, D6 ; else return 0 for homeResFile value. @1 MOVE.W D6, 12(A6) ; Return map refnum as function result BRA.S FourByteExit ; and exit. ;-------------------------------------------------------------------------------- ; ; FUNCTION CurResFile: INTEGER; ; ; Returns the reference number of the current resource file. ; ;-------------------------------------------------------------------------------- CurResFile MOVE.W curMap, 4(SP) ; Set the function result as current map CurResExit RTS ; ...and return. ;-------------------------------------------------------------------------------- ; ; FUNCTION GetResAttrs(theResource: Handle): 8(A6) = (A0) ; INTEGER; 12(A6) ; ; Given a resource, return the resource attributes in the low order ; byte of the integer result. If a reference to the resource exists ; in the current resFile, returns the attributes of the reference. ; ;-------------------------------------------------------------------------------- GetResAttrs BSR.S StdEntry ; Link A6, save regs MOVE.L (A0), A1 ; Get the resource handle BSR.S RefHandle ; Scan for reference or handle BMI.S GRAExit ; if not found, resErr set, just exit. MOVE.B RAttr(A2), 13(A6) ; Set function result in low byte of result GRAExit ; GRAExit = FourByteExit. FourByteExit MOVEQ #4, D0 ; Pop 4 bytes of paramter BRA.S GRIStdExit ; and branch to standard exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE SetResAttrs(theResource: Handle; 10(A6) ; attrs: INTEGER); 8(A6) ; ; Given a resource, set the resource attributes which are in the low ; order byte of the integer parameter. These new attributes will only ; be in effect the next time the resource is loaded, except for SysHeap, ; which will be ignored. If a reference to the resource exists in the ; current resource file, the attributes of the reference will be set. ; ;-------------------------------------------------------------------------------- SetResAttrs BSR.S StdEntry ; Link A6, save regs MOVE.L 10(A6), A1 ; Get the resource handle BSR.S RefHandle ; Look for a reference or the resource BMI.S SRAExit ; if not found, resErr set, just exit. MOVE.B 9(A6), RAttr(A2) ; Set attr byte in the location longword ;jwp BSR.S SetMapChanged ; Say that the map changed ;jwp SRAExit BRA SixByteExit ; and exit. ;jwp ;-------- ; Utility SetMapChanged ; Given the map handle in A4, set the mapChanged bit in the map attributes byte. ; Smashes A0. SetMapChanged MOVE.L (A4), A0 ; Dereference map handle BSET #MapChanged, MAttr(A0) ; Set the MapChanged bit RTS ; and return. ;jwp ;-------- ; Utility RefHandle ; Scans for reference, then handle. Returns MI if neither found, and ; sets ResErr to ResNotFound. RefHandle BSR.S RefScan ; Scan for a reference to it BEQ.S GotRef ; If reference found, get result BSR.S HandleScan ; Otherwise find the resource itself BEQ.S GotRef ; if resource found, exit, else MOVE.W #ResNotFound, ResErr ; Set ResErr, CC's GotRef RTS ; ...and return ;-------------------------------------------------------------------------------- ; ; PROCEDURE GetResInfo(theResource: Handle; 20(A6) ; VAR theID: INTEGER; 16(A6) ; VAR theType: LONGINT; 12(A6) ; VAR name: Str255); 8(A6) ; ; Given a resource, return the information about the resource in the ; VAR parameters. If a reference to the resource exists in the current ; resFile, the information will be returned about the reference. ; ;-------------------------------------------------------------------------------- GetResInfo BSR.S StdEntry ; Link A6, save regs MOVE.L 20(A6), A1 ; Get the resource handle BSR.S RefHandle ; Look for a reference to this resource BEQ.S @1 ; If OK, go on, else resErr set, LEA MinusOne, A2 ; point to -1, -1, MOVEQ #0, D3 ; and set type to null type. @1 LEA 16(A6), A0 ; Get top parameter address MOVE.L (A0), A1 ; Get address of the ID variable MOVE.W RID(A2), (A1) ; Store it MOVE.L -(A0), A1 ; Get address of the type variable MOVE.L D3, (A1) ; Store it MOVE.L -(A0), A1 ; Get address of the name variable CLR.B (A1) ; Store first byte zero, for null string BSR NameResource ; Given RNameOff(A2), point A0 to the name. BMI.S GRIExit ; If can't find the name, no go BSR.S CopyStr ; Copy string in A0 to A1. GRIExit MOVEQ #16, D0 ; Pop off 16 bytes of parameter GRIStdExit BRA StdExit ; and exit. ;-------------------------------------------------------------------------------- ; ; Utility CopyStr ; Given a Pascal string in A0 and an address in A1, copies the string including ; the length byte to A1 using blockMove. CopyStr MOVEQ #0, D0 ; Zero high order bits. MOVE.B (A0), D0 ; Get length of string ADDQ.L #1, D0 ; include length byte. _BlockMove ; Copy the string to A1 RTS ; and return. ;-------------------------------------------------------------------------------- ; ; PROCEDURE SetResInfo(theResource: Handle; 14(A6) ; theID: INTEGER; 12(A6) ; theName: Str255); 8(A6) ; ; Given a resource, Set the information about the resource. ; If the name pointer is NIL, doesn't change the name. ; If the protect bit is on, returns without doing anything. If a ; reference to the resource exists in the current resFile, the information ; about the reference will be set. ; ;-------------------------------------------------------------------------------- SetResInfo ;jwp BSR.S StdEntry ; Link A6, save regs MOVE.L 14(A6), A1 ; Get the resource handle BSR.S RefHandle ; Look for a reference to this resource BMI.S TenByteExit ; Couldn't find it, or reference! BTST #ResProtected, RAttr(A2); Is it protected from being changed? BNE.S TenByteExit ; Yes, return immediately, otherwise SUB.L A1, A1 ; (no handle) JSR CheckGrow ; can the resfile + map grow? BMI.S TenByteExit ; if failed, can't set. Too close to ; a boundary condition. BSR.S SetMapChanged ; Note map has changed. ;jwp MOVE.W 12(A6), RID(A2) ; Get new ID and store it MOVE.L 8(A6), D0 ; Get pointer to new name BEQ.S TenByteExit ; If zero, don't set a new name MOVE.L D0, A0 ; Get name pointer in A0 BSR RmveName ; Remove old name, if it exists BSR AddName ; Add the new one back. ; Sets in RNameOff(A2). SRIExit TenByteExit MOVEQ #10, D0 ; pop off 10 bytes of parameters BRA.S GRIStdExit ; Restore regs, unlink A6 ;-------------------------------------------------------------------------------- ; ; PROCEDURE AddResource(theResource: Handle; 18(A6) ; theType: LONGINT; 14(A6) ; theID: INTEGER; 12(A6) ; name: Str255); 8(A6) ; ; Add a reference to the given resource in the current map. Set ID and name ; in the reference. Mark the resource to be written out into the correct ; resource map data section. ; ;-------------------------------------------------------------------------------- AddResource BSR.S StdEntry ; Link A6, save regs MOVE.W #AddResFailed, ResErr ; Assume error. CheckGrow resets ResErr. MOVE.L 18(A6), A1 ; Get handle in A1 MOVE.L A1, D0 ; Set CC's BEQ.S ARsExit ; if NIL handle, can't add! BSR HandleScan ; Is the data already a resource? BEQ.S ARsExit ; can't add another ... BSR GetCurMap ; Get the current map for diskFull check BSR CheckGrow ; Room for resource + map overhead? BMI.S ARsExit ; if not, can't add the resource. ; Return with error from CheckGrow. ; If successful, ResErr := 0. MOVE.L 14(A6), D3 ; Put type of resource to add in D3 BSR.S AddNewRef ; Set up a new reference to the resource ; Returns A2-->reference location. MOVE.W 12(A6), (A2)+ ; Set ID of reference MOVE.W MinusOne, (A2)+ ; Assume no name yet MOVE.L #$02000000, (A2)+ ; ResChanged set. MOVE.L 18(A6), A0 ; Get the handle BSET #Resource, (A0) ; Mark it as a resource MOVE.L A0, (A2) ; Finally, set handle. SUBQ.L #8, A2 ; Back up for AddName, AllocRes. MOVE.L 8(A6), A0 ; Get pointer to reference name BSR AddName ; Name set in RNameOff(A2). ; A2, A3 preserved + correct. BSR AllocRes ; Allocate new resource loc'n. ARsExit MOVEQ #14, D0 ; Pop off 14 bytes of parameter BRA.S GRIStdExit ; Restore regs, unlink A6 ;--- ; AddNewRef ; Add a new reference, and possibly its type. ; A4=current map handle ; A3=ptr to map ; D3=is type of resource to add. ; ; RETURNS ; A2 pointing to the location of the new reference. ; AddNewRef BSET #MapChanged, MAttr(A3) ; Set mapChanged bit BSR TypeScan ; Scan for type of referent (D3) in cur map BEQ.S AddEntryOnly ; If found, type exists, otherwise ; Add both a new type and a new entry. MOVEQ #RESize+TESize, D0 ; Move names up by RE+TE size to leave BSR MoveNames ; room in handle for type and res entry. BSR GetTypes ; Point to first type, if any. BMI.S AddingFirstType ; If no types, adding first! Otherwise, BSR SkipToEntry ; skip to the entry. A2-->first res entry. ; (which is loc'n to add new type). AddingFirstType MOVE.L A2, A3 ; A3 is location of new type entry. MOVE.L (A4), A2 ; Dereference map handle. ADD.W MNames(A2), A2 ; A2 points to new res entry loc'n. SUB.W #RESize+TESize, A2 ; (name block already moved.) MOVEQ #TESize, D0 ; Now shift all entries up by TESize MOVE.L A3, A0 ; (A3=first entry loc) MOVE.L A2, A1 ; new res entry locn is high bounds of block. BSR ShiftBlock ; Made room for type entry now. MOVE.L (A4), A0 ; Dereference map handle ADD.W MTypes(A0), A0 ; Point to type block ADDQ.W #1, (A0) ; Increment type count. ; Set the new type entry now. Use ()+ to skip through the entry. MOVE.L D3, (A3)+ ; Set the new type CLR.W (A3)+ ; Set TCount to 0 (1 entry) SUB.L A0, A1 ; Subtract first entry loc from block start MOVE.W A1, (A3) ; to get the offset (new entry needs +TESize too). ADDQ.L #TESize, A2 ; add offset to returned loc'n MOVE.W (A0)+, D5 ; Get number of types BRA.S UTOffsets ; and update ALL type offsets by TESize. ; The type already exists. Just add a new entry. AddEntryOnly MOVEQ #RESize, D0 ; Move names up by entry size only. BSR MoveNames ; Move names up for room MOVE.L (A4), A1 ; Dereference map handle. ADD.W MNames(A1), A1 ; end of block to move is beginning of names. SUB.W D0, A1 ; names moved already--skip back to old val. BSR TypeScan ; Scan for the type again, A1 preserved. MOVE.L A2, -(SP) ; Save type entry location for offset updating ADDQ.W #1, TCount(A2) ; Increment entry count for this type MOVE.W TOffset(A2), A2 ; Skip to the first entry of that type ADD.L A3, A2 ; add in block address gives absolute addr. MOVE.L A2, A0 ; Start moving entries from here to names. MOVEQ #RESize, D0 ; Move entries up by entry size only. BSR ShiftBlock ; shift block up by RESize. MOVE.L (SP)+, A0 ; Restore type entry location, and update ; following types by RESize. BRA.S UTInLoop ; Jump in lp to skip this type entry ;-------- ; Utility UTOffsets ; Update type offsets--D0=delta, D5=types remaining, A0 points to first entry UTOffsets ADD.W D0, TOffset(A0) ; Increment by D0 UTInLoop ADDQ.W #TESize, A0 ; bump address DBRA D5, UTOffsets ; Update each name offset RTS ;-------------------------------------------------------------------------------- ; ; PROCEDURE AddReference(theResource: Handle; 14(A6) ; theID: INTEGER; 12(A6) ; name: Str255); 8(A6) ; ; Add a reference to the given resource in the current map. Set ID and name ; in the reference. The resource being referenced must reside in the ; system map. ; ;-------------------------------------------------------------------------------- AddReference BSR.S StdEntry ; Link A6, save regs MOVE.W #AddRefFailed, ResErr ; Assume error. CheckGrow resets ResErr. MOVE.W CurMap, D0 ; Get current map refnum CMP.W SysMap, D0 ; Is it the system map? BEQ.S ARfExit ; If so, can't add a reference to self MOVE.L 14(A6), A1 ; Get the handle BSR.S RefScan ; Is there already a reference?? BEQ.S ARfExit ; Yes, can't add another... BSR HandleScan ; Did it find the handle? BMI.S ARfExit ; No, just return CMP.W SysMap, D6 ; Is it in the system map? BNE.S ARfExit ; No, can't add a reference BSR NameResource ; Given RNameOff(A2), point A0 to the name. MOVE.L A0, -(SP) ; Push pointer for later MOVE.W RID(A2), -(SP) ; Save the ID of system resource also. BSR GetCurMap ; Get the current map for diskFull check SUB.L A1, A1 ; (no handle) BSR CheckGrow ; Room for bigger resfile + map? BEQ.S @1 ; if so, ResErr := 0, otherwise... ADDQ #6, SP ; pop off ID and pointer BRA ARfExit ; and exit with error from CheckGrow. @1 BSR.S AddNewRef ; Make space for the new reference. May ; move the map, but the system map doesn't ; move in the system heap. ; Set the new entry here. MOVE.L 8(A6), A0 ; Get pointer to new name. BSR AddName ; Set RNameOff with my name. MOVE.W 12(A6), RID(A2) ; Set ID of reference CLR.L RLocn(A2) ; Clear location long BSET #ResSysRef, RAttr(A2) ; but set system reference bit ADDQ #RefID, A2 ; Point to refID/RefNameOff pair MOVE.W (SP)+, (A2) ; Set ID of referent in system map MOVE.L (SP)+, A0 ; Get system resource's name, if any BSR AddName ; and add it too. ARfExit BRA TenByteExit ; exit ;-------------------------------------------------------------------------------- ; ; PROCEDURE RmveResource(theResource: Handle); 8(A6) = (A0) ; ; Remove a resource from the map, and from the data section of the resource ; file when UpdateResFile/CloseResFile is called. ; May only be removed if it is in the current map and is not protected. ; ;-------------------------------------------------------------------------------- RmveResource BSR.S StdEntry ; Link A6, save regs MOVE.W #RmvResFailed, ResErr ; Assume error. Cmn code resets ResErr. MOVE.L (A0), A1 ; Get the handle in A1 BSR HandleScan ; Find the handle, wherever it is. BMI.S RmveExit ; Can't find it, so can't remove it. BTST #ResProtected, RAttr(A2); Is it protected? BNE.S RmveExit ; Yes, return immediately CMP.W CurMap, D6 ; Is it in the current map? BNE.S RmveExit ; If not, don't remove it, otherwise ; ...fall through to common remove code. BCLR #Resource, (A1) ; This isn't a resource anymore. MOVEQ #MCCMask, D0 ; MapChanged+MapCompact to be set in RmveCMn ;--- ; Common code for remove reference/remove resource. ; A2 points to the resource entry. ; RmveCmn MOVE.L (A4), A0 OR.B D0, MAttr(A0) ; Set mapChanged and possibly mapCompact. BSR RmveName ; Remove RNameOff(A2)'s name, if any. MOVEQ #-RESize, D7 ; Get -(resource entry size) MOVE.L A2, A0 ; Remember address of SUB D7, A0 ; NEXT entry, in A0. BSR TypeScan ; A2 points to the type MOVE.L (A4), A1 ; Dereference map handle ADD.W MNames(A1), A1 ; A1 points to the name block MOVE.L D7, D0 ; Copy (-RESize) in D0 BSR ShiftBlock ; Shift the block down MOVE.L A2, A0 ; Copy type entry address to A0 ADDQ #TESize, A0 ; Skip to next type entry SUBQ #1, D5 ; Decrement number of types BMI.S @1 ; If this is last type, go on, else BSR.S UTOffsets ; update following type offsets. @1 SUBQ.W #1, TCount(A2) ; decrement resource count for this type BPL.S EndRR ; If still more resources of this type, quit ; If this was the last resource of the type, remove the type entry. ADDQ #TESize, A2 ; Skip to next type entry MOVE.L A2, A0 ; Moving next type over current, to delete. ADD D0, A1 ; move down name address by resource entry size MOVEQ #-TESize, D0 ; Shift this block down by type entry size ADD.L D0, D7 ; Decrement down TESize more, for names. BSR ShiftBlock MOVE.L (A4), A0 ; Dereference map handle ADD.W MTypes(A0), A0 ; A2 points to the type block SUBQ #1, (A0) ; Decrement number of types MOVE.W (A0)+, D5 ; Get number of types in D5 BMI.S EndRR ; No types left! just return BSR.S UTOffsets ; Update all type offsets by TESize. EndRR MOVE.L D7, D0 ; Get accumulated shuffle in D0. BSR MoveNames ; Move names down 12 or 20 CLR.W ResErr ; Remove succeeded! RmveExit BRA FourByteExit ;-------------------------------------------------------------------------------- ; ; PROCEDURE RmveReference(theResource: Handle); 8(A6) = (A0) ; ; Remove a resource reference from the map. If it is protected, ; the reference will not be removed. ; ;-------------------------------------------------------------------------------- RmveReference BSR.S StdEntry ; Link A6, save regs MOVE.W #RmvRefFailed, ResErr ; Assume failure. Cmn code resets ResErr. MOVE.L (A0), A1 ; Get the handle BSR RefScan ; Look for a reference to it BMI.S RmveExit ; If not found, just return. BTST #ResProtected, RAttr(A2); Is it protected? BNE.S RmveExit ; Yes, don't remove the reference ADDQ #RefID, A2 ; Skip up to refID/RefNameOff pair BSR RmveName ; Get rid of the reference name, if any SUBQ #RefID, A2 ; Restore A2, for normal remove. MOVEQ #MChMask, D0 ; Set mapChanged bit in RmveCmn BRA.S RmveCmn ; and branch to the common code. ;-------------------------------------------------------------------------------- ; ; FUNCTION ResError: INTEGER; ; ; Return a resource error code. Usually the code will be an I/O error, ; but there are several resource-only errors which are reported through ; this call. ; ;-------------------------------------------------------------------------------- ResError MOVE.W ResErr, 4(SP) ; Set error code as function result RTS ; ...and return. ;-------- ; Utility AddName ; ; A2 points to a resource entry. ; A0 points to a name to associate with that entry. ; Sets RNameOff(A2) to a name offset, and copies the name to the map block. ; AddName MOVEM.L D0-D2/A0-A1, -(SP) ; save regs used (returning D2), and MOVEQ #-1, D2 ; Assume no string TST.B (A0) ; is it empty? BEQ.S ANExit ; if so, return -1 for RNameOff. MOVE.L (A4), A1 ; Dereference map handle MOVE.L MapSize(A1), D1 ; Copy current map size to D1. MOVE.L D1, D2 ; and to D2. MOVEQ #0, D0 ; Zero D0 MOVE.B (A0), D0 ; Get string length ADDQ.L #1, D0 ; Add 1 for len byte BSR ResizeMap ; Grow the map by string size. MOVE.L (A4), A1 ; Get map ptr again MOVE.W MNames(A1), D0 ; D0=offset to name block SUB.W D0, D2 ; mapSize-nameBlkoff := off to newname ADD.L D1, A1 ; Add endOfMap to get end address in A1 BSR CopyStr ; Copy the string in! ANExit MOVE.W D2, RNameOff(A2) ; Set name offset in the entry MOVEM.L (SP)+, D0-D2/A0-A1 ; Restore regs RTS ; and exit. ;-------- ; Utility MoveNames ; Given a byte count in D0, moves the names up or down by the specified amount ; Resizes map each time. Adjusts A3, A2 to compensate for any block moving. MoveNames MOVEM.L D0-D1/A0-A1, -(SP) ; Save regs MOVE.L (A4), A0 MOVE.L MapSize(A0), D1 ; Save original map size in D1 TST.L D0 ; Are we shrinking or growing? BMI.S Shrink ; Shrinking, resize after move BSR.S ResizeMap ; Otherwise grow first MOVE.L (A4), A0 ; Dereference handle (may have moved) Shrink MOVE.L A0, A1 ; Copy address ADD.W MNames(A0), A0 ; Point A0 to names block ADD.W D0, MNames(A1) ; (Update name block offset now) ADD.L D1, A1 ; Point A1 to original end of map BSR.S ShiftBlock ; Shift (A0..A1) up or down by D0 TST.L D0 ; Did we shrink or grow? BPL.S Grow ; Grew, map already resized, otherwise BSR.S ResizeMap ; Shrink it now Grow MOVEM.L (SP)+, D0-D1/A0-A1 ; Restore regs RTS ; ...and return ; -------- ; Utility ResizeMap ; Given a byte count in D0, grows or shrinks the map. ; The map may move when growing, though not when shrinking. ; Adjusts A3, A2 to compensate for any block moving. ; Calls SetRFEOF to set resource file EOF (reserving space). ; ResizeMap SUB.L (A4), A3 ; A3 := offset from map beginning SUB.L (A4), A2 ; A2 := offset from map beginning MOVEM.L A0/D0, -(SP) ; Save A0, D0 MOVE.L (A4), A0 ; Dereference handle ADD.L MapSize(A0), D0 ; Add delta to current size for new size MOVE.L D0, MapSize(A0) ; Set new mapSize. MOVE.L A4, A0 ; Get handle in A0 _SetHandleSize ; Set the new size BSR SetRFEOF ; Set new EOF MOVEM.L (SP)+, A0/D0 ; Restore regs ADD.L (A4), A3 ; make A3, ADD.L (A4), A2 ; A2 valid again after size change RTS ; ...and return ;-------- ; Utility ShiftBlock ; Given a delta (in bytes) in D0, shifts the block (A0..A1) up or down by D0. ShiftBlock MOVEM.L A0-A1/D0, -(SP) ; Save regs EXG D0, A1 ; D0=bytes to move, A1=delta SUB.L A0, D0 ; Subtract low from high to give size AND.L MaskBC, D0 ; AND off high 8 bits ADD.L A0, A1 ; Add low to bytes to move, giving dest. _BlockMove ; Move the bytes MOVEM.L (SP)+, A0-A1/D0 ; Restore regs RTS ; ...and return ;-------- ; Utility RmveName ; ; Given A2 pointing to a resource entry, removes the name ; pointed to by RNameOff(A2). Removing reference names requires that A2 point ; to RefID instead of RID. RmveName MOVEM.L D0-D2/A0-A1, -(SP) ; Save working regs MOVE.W RNameOff(A2), D2 ; Copy name offset to D2. BMI.S RNExit ; if not a real offset, just exit. MOVE.L (A4), A0 ; Dereference map handle MOVE.L A0, A1 ; Copy ptr to A1 ADD.W MNames(A0), A0 ; A0 points to the name block ADD.W D2, A0 ; and finally the string itself. ADD.L MapSize(A1), A1 ; A1 points to the end of the map. MOVEQ #0, D0 ; Zero high order MOVE.B (A0), D0 ; Get length byte ADDQ.L #1, D0 ; Add 1 to include the length byte ADD.L D0, A0 ; point A0 to the NEXT string NEG.L D0 ; Negate, since shrinking MOVE.L D0, D1 ; Copy delta to D1 for updating offsets. BSR ShiftBlock ; Shift the block (A0..A1) down strSize. BSR ResizeMap ; and shrink the map. A2-A3 corrected. MOVEM.L D3-D7/A2-A4, -(SP) ; Save rest of registers. BSR GetEntries ; Walk through entries, and update ; following name offsets. RNLoop LEA RNameOff(A2), A1 ; Get name offset addr. in A1. MOVE.B RAttr(A2), D0 ; And attributes in D0 for test InRNLp TST.W (A1) ; is it a nil entry? BMI.S NextRN ; if so, skip to next. CMP.W (A1), D2 ; is this name offset >= the removed one? BGE.S NextRN ; if so, skip it, else ADD.W D1, (A1) ; Offset shrinks by D1. NextRN ADDQ #8, A1 ; Skip to possible reference name offset BCLR #ResSysRef, D0 ; was it a reference? BNE.S InRNLp ; yes, possibly change this offset too. BSR NextEntry ; skip to the next entry BPL.S RNLoop ; loop until no more entries. MOVEM.L (SP)+, D3-D7/A2-A4 ; Restore inner regs. RNExit MOVEM.L (SP)+, D0-D2/A0-A1 ; Restore outer regs MOVE.W MinusOne, RNameOff(A2) ; Set name offset to -1. RTS ; and return. ; *******End of Resource Manager******* ןspsp  MW vvvJvv-vpvvssssssssssssvJuuvJststststuuuuuuuuu u u u u uuuuuuuuuuuuuuuuuuu u!u"u#u$u%u&u'u(u)u*u+u,u-u.u/u0u1u2u3u4u5u6u7u8u9u:u;uu?u@uAuBuCuDuEuFuGuHuIuJuKuLuMuNuOuPuQuRuuuuuuuuuu u u u u uuuuuuuuuuuuuuuuuuu u!u"uuuuuuuu u u v v v v v v v v v  v  v  v  v  v v v v v v v v v v vvvvvvvvvvv,v,v,v,vGvHvHvHvHvIv`v`v`v`v`v`v`v`v`v`vava va va va va vavavavavavavavavbvbvbvbvbvbvbvbvb vb vb vb vb vbvbvbvbvbvbvbvcvdvdvdvdvdvdvdvdvd vd vd vd vd vdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvd ve!ve"ve#ve$ve%ve&ve've(ve)ve*ve+ve,ve-ve.ve/ve0ve1ve2ve3ve4ve5ve6ve7ve8ve9ve:ve;veve?ve@veAvfBvfCvfDvfEvfFvfGvfHvfIvfJvfKvfLvfMvfNvfOvfPvfQvfRvfSvfTvfUvfVvfWvfXvfYvfZvf[vf\vfvjvjvjvjvjvjvjvjvj vj vj vj vj vjvjvjvjvjvjvjvjvjvjvjvjvkvkvkvkvkvkvk vk!vk"vk#vk$vk%vk&vk'vk(vk)vk*vk+vk,vk-vk.vk/vk0vk1vk2vk3vk4vk5vk6vk7vl8vl9vl:vl;vlvl?vl@vlAvlBvlCvlDvlEvlFvlGvlHvlIvlJvlKvlLvl v| v} v} v} v} v} v~!v~!v~!v~!v~!v~!v~!v~!v~!v~! v~! v~! v~"v"v"v"v"v"v"v"v"v" v" v#v#v#v#v#v#v#v#v$v%v%v%v%v%v%v%v%v%v% v% v% v% v% v%v%v%v%v%v%v%v%v%v%v%v%v&v&v&v&v&v&v&v&v&v& v& v& v& v& v&v&v&v&v&v&v&v&v&v&v&v&v&v&v&v&v&v&v& v&!v&"v&#v&$v&%v&&v&'v&(v&)v&*v&+v&,v&-v&.v&/v&0v&1v&2v&3v&4v&5v&6v&7v&8v&9v&:v&;v&v&?v&@v&Av&Bv&Cv&Dv&Ev&Fv&Gv&Hv&Iv&Jv&Kv&Lv&Mv&Nv&Ov&Pv&Qv&Rv&Sv&Tv&Uv&Vv&Wv&Xv&Yv&Zv&[v&\v&]v&^v&_v&`v&av&bv&cv&dv&ev&fv&gv&hv&iv&jv&kv&lv&mv&nv&ov&pv&qv&rv'v'v'v'v'v'v'v'v'v' v' v' v' v' v'v'v'v'v'v'v'v'v'v'v'v'v'v'v'v'v'v'v' v'!v'"v'#v'$v'%v'&v''v'(v')v'*v'+v',v'-v'.v'/v'0v'1v'2v'3v'4v'5v'6v'7v'8v'9v':v';v'v'?v'@v(v(v(v(v(v(v(v(v(v( v( v( v( v( v(v(v(v(v(v(v(v(v(v(v(v(v(v(v(v(v(v(v( v(!v("v(#v($v(%v(&v('v((v()v(*v(+v(,v(-v(.v(/v(0v(1v(2v(3v(4v(5v(6v(7v(8v(9v(:v(;v(v(?v(@v(Av(Bv(Cv(Dv(Ev(Fv(Gv(Hv(Iv(Jv(Kv(Lvsq