-not a Macintosh disk-FbGYa4GYa;0a:a4BH@%^Z/:ןsw   Untitled0P 0Pp "0$P&p(*,.0204P6p8:<>@B0DPFpHJLNPRTPVpXZ\^`b0dPfphjlprPvpxz|~0PpЎ  0 P p О  0 P p Ю  0 p о  0 P p  0 p 0Pp0Pp1Qq  1Qq "1$Q&q(*,.024Q6q8:<>@B0QFqHJLNPR1TVqXZ\^`b1dQfqhjlnpr1tQvqxz|~FNDRERIK@"swDeskTopTEXTMACA` ff'backup/all.textTEXTMACAyfBNDLJMACAVICN#bFREF`\i1$L$P_$ $3$Tk$$b$tV$01$%d6t$ha$xUntitled$EXEC {exec-backup/all.text, backs up the entire set of priam sources for everything to an attatched profile (slot2chan1)} $WRITELN $WRITELN 'If you have not used the profile for backup previously,' $WRITELN 'first run -priam-exec-make/backupdisk.' $WRITELN $WRITELN 'The backup profile must be attached and mounted on #2#1.' $WRITELN 'The disk will be re-initialized and all sources copyied over!!!!!!' $WRITELN $WRITE 'Is it OK to continue (Y or N) [N] ? ' $READLN %1 $IF UPPERCASE(%1) = 'Y' THEN $SUBMIT -priam-exec-do/backupdisk R{un}<-priam-exec-backup/macworks Y{es, do it} R{un}<-priam-mwcopy-mwc/backup Y{es, do it} R{un}<-priam-mwp-pr/backup Y{es, do it} R{un}<-priam-mwd-hd/backup Y{es, do it} $ENDIF $ENDEXEC $EXEC {backup/macworks.text, backs up the entire set of priam sources/files to an attatched profile (slot2chan1)} $WRITELN $WRITELN 'If you have not used the profile for backup previously,' $WRITELN 'first run -priam-exec-make/backupdisk.' $WRITELN $WRITELN 'The backup profile must be attatched and mounted on #2#1.' $WRITELN 'All files in the source/drivers/exec/tlasm/obj/mwdisk sub-' $WRITELN 'directories will be DELETED!!!!!!' $WRITELN $WRITE 'Is it OK to continue (Y or N) [N] ? ' $READLN %1 $IF UPPERCASE(%1) = 'Y' THEN F{iler}D{elete}-#2#1-source-= {four spaces}Y{es, delete them} D{elete}-#2#1-drivers-= {four spaces}Y{es, delete them} D{elete}-#2#1-exec-= {four spaces}Y{es, delete them} D{elete}-#2#1-tlasm-= {four spaces}Y{es, delete them} D{elete}-#2#1-obj-= {four spaces}Y{es, delete them} D{elete}-#2#1-mwdisk-= {four spaces}Y{es, delete them} D{elete}-#2#1-tools-= {four spaces}Y{es, delete them} C{opy}-priam-source-= -#2#1-source-= {four spaces for file list}Y{es, copy them} C{opy}-priam-drivers-= -#2#1-drivers-= {four spaces for file list}Y{es, copy them} C{opy}-priam-exec-= -#2#1-exec-= {four spaces for file list}Y{es, copy them} C{opy}-priam-tlasm-= -#2#1-tlasm-= {four spaces for file list}Y{es, copy them} C{opy}-priam-obj-= -#2#1-obj-= {four spaces for file list}Y{es, copy them} C{opy}-priam-mwdisk-= -#2#1-mwdisk-= {four spaces for file list}Y{es, copy them} C{opy}-priam-tools-= -#2#1-tools-= {four spaces for file list}Y{es, copy them} $ENDIF $ENDEXEC $EXEC {comp.text, arg0 = file to compile. Assumes that the file exists on -priam, and that the remainder of the prefix is given} $SUBMIT -priam-exec-setprefix(-ws) P{ascal}-priam-%0{file to compile} {no list file} {same .obj file to auto-generate code for} $SUBMIT -priam-exec-setprefix(-priam) $ENDEXEC ;Dispatch.TEXT ;MacWorks copy ;_______________________________________________________________________ ; ; EMT1010 -- MacIntosh Operating System Dispatcher ; ; The following code receives all Line 1010 emulator ; traps and transfers control to the appropriate system ; code to interpret the trap. A 16-bit/entry RAM-based ; dispatch table is used to derive the addresses to ; dispatch to. Since this table is patchable, a system ; or application program can patch in and intercept ; any system call to fix bugs, etc. ; ; The dispatcher implements two distinct types of dispatches, ; differing mainly in register preservation conventions. If ; bit 11 of the trap word is set, a "toolBox" dispatch is performed. ; ToolBox dispatches are used for routines that follow Pascal ; parameter passing conventions (i.e., parameters on the stack). ; The dispatcher ensures that by the time control is transfered to ; the target routine, the stack is exactly the same as it would be ; if the routine was JSRed to. All registers are preserved up to the ; the time the target routine receives control; after that it is up to ; the target routine (which will typically follow the Pascal conventions ; and trash D0-D2/A0-A1). The low 9 bits of the trap specify which ; routine to invoke. ; ; For toolBox dispatches, bit 10 is a flag that specifies whether or not to ; pop off an extra return address immediately before dispatching. This is useful ; when traps must be JSRed to instead of being inserted in-line (like the Lisa ; Pascal environment). ; ; The other type of dispatch is used when bit 11 is clear and is called ; an "OS" dispatch. For OS dispatches, the dispatcher is responsible for ; preserving some registers on the stack before calling the target routine; ; this is cool since OS routines never get their parameters on the stack. ; If bits 8 and 9 of trap are clear, all registers except D0 are preserved; ; this is the typical (default) case. If bit 8 is set, all registers except ; D0 and A0 are preserved; this allows some core routines to return a result ; in A0. The low 8 bits of the trap specify which routine to invoke. ; Bit 10 is used by the I/O system as a sync/async flag. ; ; For OS traps, A0 and D0 are guaranteed to be preserved until the target routine ; receives control so they can be used for passing parameters. D1 holds the ; actual instruction the caused the trap so the I/O system can test bit 8 for ; the sync/async flag. ; ; For both toolBox and OS traps, the address of the target routine is computed ; from a 16 bit word stored in the 1024 byte system dispatch table. The index ; (low 8 or 9 bits of the trap) is doubled and a word is fetched from this table. ; If the high order bit of the word is clear, the dispatch is to ROM, otherwise ; the dispatch is made to RAM. Two longwords kept in low memory hold the base ; addresses for RAM and ROM routines, to which the doubled dispatch word is added ; to derive the effective address which is actually dispatched to. Note that ; this means that there is only a 64K range of RAM and ROM addresses that can be ; dispatched to. ; ; Written by Andy Hertzfeld July 15, 1982 ; ; Modification History: ; ; 24-Oct-82 AJH Added bit 10 auto-pop for toolBox dispatches ; Added GetTrapAddress, SetTrapAddress ; 22-Feb-83 LAK Changed OS dispatch to save fewer regs since OS routines ; now observe Pascal regsave conventions; ; (async bit is bit 10). ; 12-May-83 JTC Tweaked for speedup. ; 08-Aug-83 LAK Saved space in SetTrapAddr as per 17-July code review. ; ;_______________________________________________________________________ .DEF EMT1010,GetTrapAddress,SetTrapAddress .NOLIST .INCLUDE Tlasm-SYSEQU.TEXT .LIST .PROC DSPATCH ; ; Here is the entry point that receives Line 1010 emulator traps. Its address is ; set up in location $28 (hardware vector for Line 1010 traps) by SysInit. ; EMT1010 SUBQ.L #2,SP ;make two bytes of extra space MOVEM.L D1-D2/A2,-(SP) ;save minimum number of work registers MOVE.L 16(SP),A2 ;get the PC when the trap occured MOVE.W (A2)+,D2 ;get the actual trap; bump PC past it MOVE.L A2,16(SP) ;replace updated PC ; ; Trap word is binary 1010 XYYY YYYY YYYY. X=1 for toolbox, X=0 for OS. ; Check by comparing trap word to hex $A800. If trap word less than $A800 ; then X=0 so it's an OS trap. Else it's toolbox. Note that CMP A,B bases its ; result on B-A so a result of "less than" means A>B ! ; MOVE.W D2,D1 ;remember trap word in D1 CMPI.W #$A800,D2 ;$A8000 > D2 means X=0, i.e. OS trap BCS.S OSTRAP ;Carry Set on unsigned less than. ; ; It's a toolBox dispatch; compute the address to dispatch to into D2. ; Trap word is binary 1010 1YZZ ZZZZ ZZZZ. Y=1 if and only if there is an extra ; return address on the stack. Check by comparing trap word to $AC00. If trap ; word less than $AC00, then no stack fixup required. ; BSR.S ComputeDispatch MOVE.L D2,12(SP) ;replace status and extra word with dispatch addr CMPI.W #$AC00,D1 ;$AC00 > D1 means Y=0, i.e. no auto pop MOVEM.L (SP)+,D1-D2/A2 ;restore work regs, CCR unchanged ; ; Check to see if we should overwrite the extra return address. ; BCS.S @1 ;was bit 10 set? MOVE.L (SP)+,(SP) ;overwrite extra return address @1 RTS ;transfer control to target routine ; ; ComputeDispatch is a code-saving utility that, given a trap number in D2, ; computes the appropriate dispatch address into D2. A2 is trashed. ; ComputeDispatch AND.W #$01FF,D2 ;get index from 0-511 EXT.L D2 ;clear hi bits for address comp below ADD.W D2,D2 ;double for word index LEA DispatchTab,A2 ;get dispatch table address MOVE.W 0(A2,D2),D2 ;get dispatch offset word ADD.W D2,D2 ;double, get hi bit into carry BCS.S GoRAM ;if hi bit was set, it's in RAM ADD.L ROMBASE,D2 ;compute dispatch address RTS ;return to caller ; GoRAM ADD.L RAMBASE,D2 ;compute dispatch address RTS ; ; here we handle OS traps. First get dispatch address in D2 ; OSTRAP BCLR #8,D2 ;save/restore A0? (also clear this bit) BNE.S NoA0 ;if not, go handle that case ; ; handle case of saving A0,A1,D1, and D2 (most common case) ; BSR.S ComputeDispatch ;compute dispatch address MOVE.L D2,A2 ;get dispatch address in A-reg MOVEM.L A0-A1,-(SP) JSR (A2) MOVEM.L (SP)+,A0-A1 ; ; clean up the stack and return to user ; OSTDone MOVEM.L (SP)+,D1-D2/A2 ;restore the rest of the registers ADDQ #4,SP ;pop status and zero word off stack TST.W D0 ;make condition code reflect D0 RTS ;return to caller ; ; handle case of saving A1,D1, and D2 ; NoA0 BSR.S ComputeDispatch ;compute dispatch address MOVE.L D2,A2 ;get dispatch address in A-reg MOVE.L A1,-(SP) JSR (A2) MOVE.L (SP)+,A1 BRA.S OSTDone ; ; GetTrapAddress -- given the trap word in D0, return its corresponding address ; in A0 ; GetTrapAddress MOVE.L A2,-(SP) ;observe pascal regsave conventions MOVE D0,D2 ;get param into D2 BSR.S ComputeDispatch ;compute the address (trashes A2) MOVE.L D2,A0 ;return result in A0 MOVE.L (SP)+,A2 ;restore A2 MOVEQ #0,D0 ;no error RTS ; ; SetTrapAddress -- given an address in A0, and a trap number in D0, install the ; trap in the dispatch table ; SetTrapAddress MOVE.L A0,D1 ;get address in D-Reg CMP.L RomBase,D1 ;ROM or RAM space? BLT.S InRamSpace ;skip if its RAM ; ; its a ROM address so compute offset from ROM base ; SUB.L RomBase,D1 ;subtract ROM base address (bit 0 = 0) ; SetTCommon ROR.W #1,D1 ;divide by 2 AND.W #$01FF,D0 ;use low 9 bits of trap number ADD.W D0,D0 ;double it LEA DispatchTable,A0 ;get address of core routine dispatch table MOVE.W D1,0(A0,D0) ;set up entry in table MOVEQ #0,D0 ;no error RTS ;all done ; ; its a RAM address so compute offset from RAM base ; InRAMSpace SUB.L RamBase,D1 ;subtract RAM base address ADDQ #1,D1 ;bit 0 = 1 to flag ram BRA.S SetTCommon ;let common code do the rest ; .END ; FILE Dmgr.text ; MACWORKS Copy ;****************************************************************************** ; ; DMgr ; ; Alert and Dialog Box Manager for the Macintosh User Interface Standard ; ; Written by Bruce Horn 27 September 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains the interface specification for the Alert and Dialog Box 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 Alert and Dialog Box Manager sections. ; ; ; Converted to QuickDraw/ToolBox Trap Interface by AJH 17-Oct-82 ; Interface modified by Bruce 19-Nov-82 ; Modified for ROM 1.8 release by Bruce 22-Nov-82 ; Changed interface, fixed bugs, added stuff by Bruce 1-Dec-82 ; Updated to new Resource manager by Bruce 19-Dec-82 ; Changed to allow multiple dialogs by Bruce 20-Jan-83 ; Updated to new file system 3-Feb-83 ; Converted to new traps 16-Feb-83 ; Major 3.0 Rom Change by Bruce: 16-Apr-83 ; Fixed clipping bug in ScanAD (clipped user's port!) ; Changed GetNewDialog to take a window template ; Fixed DialogEvent to return TRUE on updates ; Changed inDialog to return the event every time ; Took out extra error beeps. Only alert calls sounds. ; Changed alert interface to Alert(ID, actionProc) ; Changed InitDialogs to take only one parameter ; Added SelIText to select a given field ; Rewrote Could/FreeAlert (adding LoadLockSw and FreeSw) ; Added Enter/default dismissal handling in Alerts ; Changed the nextField key to Tab ; Fixed the FlushEvents call in Alerts ; Took out IsDialog. Use WindowPk.kind=dialogKind ; SetItmDA-->SetDItem. GetItmDA-->GetDItem. ; ParamSounds-->AlertSounds. ; ParamAlert and ParamDialog-->ParamText (not separate) ; Got rid of AlertKind. Only have DialogKind now. ; Fixed control drawing to allow hidden controls. ; non-printing chars now return to application ; 25-Apr-83 SC Converted to mini edit ; 10-May-83 SC Changed InDialog to DialogSelect and ; DialogEvent to IsDialogEvent. Also changed the way ; things are called from main loop because of TextEdit ; Created aux. unit Dmgrutil for Cut and paste ; Made DAParams a handle ; Adopted BYTES procs for handle munging ; 18-May-83 SC Spring Cleaning... ; Got rid of event loop in DialogSelect ; Cleaned up DAevent for this ; Added new proc ModalDialog for mody people ; Changed SelIText to lessen refresh ; Made edit text return item# on keys ; Made dialogs and alerts share same local defs ; Changed mask for events ; ContDialog changed to itemdisable ; Much much more... ; 28-May-83 SC Changed alert action procs into filter procs ; Alerts now use modal dialogs ; Filter proc now passes the dialog ; Dumped DAParams for DABeeper: one sound proc with level ; param and DAStrings: the same 4 handles ; Default sound proc(Zounds) ; 3-June-83 SC Fixed ModalDialog to use event avail ; ModalDialog now beeps only on mouse downs outside ; 9-June-83 SC AlertSound -> ErrorSound. ; Fixed scramble bug in item list not locked down. (Affected ; begininWind, endinWind and scanAD). ; 2-July-83 SC Fixed modal/update caret bug. ; 14-July-83 SC New parameters for textbox and teupdate. ; 25-July-83 SC Removed 2-July pending updates/activates will kill the caret ; Made modaldialog call filter w/ all events, not just dialog ; Fixed clip bug in selIText caused by new TextEdit. ClipWide ; now in BeginInWind ; Removed obscure cursor in keyEvent ; 5-Aug-83 SC NewDialog now sets system font. ; 6-Aug-83 SC Call _SysBeep in Zounds. ; 7-Aug-83 SC Added NIL params to ParamText. ; 8-Aug-83 SC Scrunched code: ; Dropped .L in STSet, etc. ; Added shared routine(replacing GetWDefA2) to could alert, etc ; Dropped most of the BTST in DMGRASM2 ; Used SUBQ's in EventAD for parsing the event code ; Added some Go... labels in event code ; cleaned up round up in GETDLItem in asm3 ; 11-Aug-83 SC Scrunched code: ; Dropped preload of alerts in dialog init ; Cleaned up default alertfilter, CloseDialog, GetDItem ; SelIText, FrameOut, StrToHandle ; 13-Aug-83 SC Scrunched code: ; Changed _GetPort restores to not use stack frame ; Dropped saveSP from InitDialogs ; Changed closeDialog zero clip ; Cleaned up calls to FrameOut ; Added goto stdExit ; 17-Aug-83 SC Added sysbeep param (Ha-ha) ; ; 24-Aug-83 BLH Removed excess clipping stuff, made dialogs respect ; the clip set by user. ; Made modal default CR's and enters instead of alerts ; Fixed shift selection in TEClick (your fault if it ; breaks, Capps!) ; 29-Aug-83 SC Dropped extraneous check after findcontrol in eventad ; 1-Sept-83 SC Newdialog sets adefitem to 1 ; 2-Sept-83 SC Redid Filter proc call from 25-July (Where did it go?) ; 5-Sept-83 SC Fixed lock mismatch in alerts ; 6-Sept-83 SC Tried to make beeps sound more pleasant ; 7-Sept-83 SC Used Delay in zounds not delay loop ; 9-Sept-83 SC Added lo mem default font stuff ; ;****************************************************************************** .NOList .Include tlasm-SysEqu.Text ;System equates (memory layout...) .Include tlasm-SysMacs.Text ;System macros, line1010 calls .Include tlasm-GrafEqu.Text ;Basic graphics and screen driver .Include tlasm-GrafTypes.Text ;Offsets into LisaGraf structures .Include tlasm-QuickMacs.Text ;LisaGraf references .Include tlasm-ToolMacs.Text ;ToolBox References .Include tlasm-ToolEqu.Text ;Toolbox equates .Include tlasm-ResEqu.Text ;Resource type equates .List .Proc DMgr, 0 .Def InitDialogs ;Dialog window manager defs .Def GetNewDialog .Def NewDialog .Def IsDialogEvent .Def DialogSelect .Def ModalDialog .Def DrawDialog .Def CloseDialog .Def DisposDialog .Def GetDItem .Def SetDitem .Def SetIText .Def GetIText .Def SelIText .Def CouldDialog .Def FreeDialog ;Alert Box manager defs .Def Alert .Def StopAlert .Def NoteAlert .Def CautionAlert .Def CouldAlert .Def FreeAlert ;Common definitions for both alerts and dialogs .Def ParamText .Def ErrorSound .REF TEIdle .REF TENew .REF TEDispose .REF TEClick .REF TEKey .REF TEUpdate .REF TEActivate .REF TEDeActivate .REF TESetText .REF TECalText .REF TESetSelect .REF TextBox .REF Munger .REF HandToHand .REF PtrToXHand .REF PtrToHand .Include source-DMgrAsm1.Text .Include source-DMgrAsm2.Text .Include source-DMgrAsm3.Text .End ;****************************************************************************** ; DMgrAsm1 ; Alert and Dialog Box Manager for the Macintosh User Interface Standard ; ; Written by Bruce Horn 7 October 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; DMgrAsm1 has the Dialog Manager entry points only. ; DMgrAsm2 has scanning and list access routines, ; and DMgrAsm3 has utility routines. ; ; MODIFICATION HISTORY: see dmgr ; ; ****************************************************************************** ICRect .Word 10, 20, 42, 52 ; Icon rectangle in alert box ; Character values ChCR .EQU $0D ; carriage return ChSpace .EQU $20 ; space ChBackspace .EQU $8 ; Backspace ChTab .EQU $09 ; Tab key (next field) ChEnter .EQU $03 ; Enter key (take default action) ; ScanAD switches LoadLockSw .EQU -2 ; load and lock items FreeSw .EQU -1 ; Free items InitSw .EQU 0 ; Initialize the dialog list DrawSw .EQU 1 ; Draw items DispSw .EQU 2 ; Dispose items ; Mouse and key mask FEMask .EQU $000001FF ; flush all "vanilla" events FEMaskNoUp .EQU $000001BF ; flush all "vanilla" sans update ABTitleStr .Word 0 ; Standard title is empty ; These are the standard local vars for almost all alert and dialog routines ; The local frame big enough for these is allocated by StdEntry RectEdit .EQU -8 ; Current rectangle being edited TextEdit .EQU RectEdit-4 ; Handle to text being edited CaretTicks .EQU TextEdit-4 ; Tick count at last caret change whichWindow .EQU CaretTicks-4 ; window temp whichCtrl .EQU whichWindow-4 ; control temp localPoint .EQU WhichCtrl-4 ; Local point actionProc .EQU LocalPoint-4 ; Action procedure curRect .EQU actionProc-8 ; current rectangle oldPort .EQU curRect-4 ; Previous GrafPort scratch .EQU oldPort-16 ; scratch area theEvent .EQU scratch-16 ; temporary event record eventMsg .EQU theEvent+2 ; message eventAscii .EQU theEvent+5 ; character eventTicks .EQU theEvent+6 ; time of event in ticks eventPos .EQU theEvent+10 ; mouse position eventMeta .EQU theEvent+14 ; Meta keys EvtLink .EQU theEvent ; Link size ; ------------------------------------------------------------------- ; Standard register assignments throughout the Dialog manager: ; ; A0, A1 Free ; D0, D1, D2 Free ; ; A2 Pointer to dialogList item (display item) ; A3 Handle to dialogList ; A4 current window pointer (self) ; ; D3 dialogList item index ; D4 switch register #1 ; D5 switch register #2 ; ; D6 scratch registers ; D7 UserField .EQU 0 ; Open field for user if all 0 CtrlItem .EQU 2 ; Control item bit BtnCtrl .EQU 0 ; Low two bits=0 then button ChkCtrl .EQU 1 RadCtrl .EQU 2 ResCtrl .EQU 3 StatText .EQU 3 ; Static text bit EditText .EQU 4 ; Editable text bit BothText .EQU $18 ; mask for both text types @ IconItem .EQU 5 ; Icon bit PicItem .EQU 6 ; Picture bit ItemDisable .EQU 7 ; If set, item hit can return ; ---------------------------------- ; ; Procedure InitDialogs(restartProc: ProcPtr); { Where to save state if syserr } ; ; This procedure initializes the fail-safe procedures. OpenResource should be ; called before InitDialogs, in order to load in the required alerts which ; correspond to the fail-safe procedures. ; Assumes OpenResources has already been called by the application ; Initializes the dialog and alert windows InitDialogs BSR StdEntry MOVE.L 8(A6), RestProc ; Get restartProc pointer to save ; Set up alert stage counters CLR.W ACount ; Say starting with 0 for count ST ANumber ; some bogus ANumber ; Initialize the beeper and string procs LEA DABeeper,A0 ; clear the string handles LEA Zounds,A1 ; point to default sound routine MOVE.L A1,(A0)+ ; set beeper to default CLR.L (A0)+ ; DAStrings s/b zeroed CLR.L (A0)+ ; DAStrings s/b zeroed CLR.L (A0)+ ; DAStrings s/b zeroed CLR.L (A0)+ ; DAStrings s/b zeroed CLR dlgFont ; default font for dialogs Exit4 MOVEQ #4, D0 ; Pop parameter BRA StdExit ; ...and return. ; Default sound proc beeps Zounds MOVE.L (SP)+,A1 ; return address(sysbeep saves all) MOVE (SP)+,D2 ; get number of beeps zLoop BEQ.S zAdios MOVE #3,-(SP) ; decent beep tone _SysBeep ; MOVEQ #-1,D0 ;@1 DBRA D0,@1 ; delay 1/10 second MOVE #6,A0 _Delay SUBQ #1,D2 ; any more beeps? BRA zLoop ; ------------------------------------------------- ; Alert box calls ;--------------------------------------------------------------- ; ; Standard alert filter for callers who don't pass an action proc AlertFilter MOVEM.L (SP)+,D1-D2/A0-A1 ; D1-return; D2-item var ; A0-event add; A1-dialog add CLR (SP) ; assume failure return CMP #KeyDwnEvt,EvtNum(A0) ; See if key event type BNE.S AFFailed CMP.B #CHEnter,EvtMessage+3(A0); get event message BEQ.S @1 ; yahoo if enter CMP.B #CHCR,EvtMessage+3(A0) ; get event message BNE.S AFFailed ; exit if not enter or return @1 MOVE.L D2,A0 ; point to item result MOVE AdefItem(A1),(A0) ; return default item ADDQ.B #1,(SP) ; turn false into true AFFailed MOVE.L D1,A1 ; get return address zAdios JMP (A1) ; return ; ---------------------------------- ; ; Function StopAlert(alertID: Integer; ; actionProc: ProcPtr): Integer; ; ; This function puts up a STOP alert, complete with stop sign. It returns the ; number of the button pressed on exit. ; The STOP icon or picture must be defined with ID=0. ; StopAlert MOVEQ #StopIcon, D0 ; Say STOP alert BRA.S CmnAlert ; jump to common code ; ---------------------------------- ; ; Function NoteAlert(alertID: Integer; ; actionProc: ProcPtr): Integer; ; ; This function puts up a Note alert. It returns the number of the button pressed ; on exit. ; The NOTE icon or picture must be defined with ID=1. ; NoteAlert MOVEQ #NoteIcon, D0 ; Say NOTE alert BRA.S CmnAlert ; jump to common code ; ---------------------------------- ; ; Function CautionAlert(alertID: Integer; ; actionProc: ProcPtr): Integer; ; ; This function puts up a Caution alert. It returns the number of the button ; pressed on exit. ; The CAUTION icon or picture must be defined with ID=2. ; CautionAlert MOVEQ #CtnIcon, D0 ; Say Caution alert BRA.S CmnAlert ; jump to common code ; ---------------------------------- ; ; Function Alert(alertID: Integer; ; actionProc: ProcPtr): Integer; ; ; This is the general alert call. It does not display a standard icon. ; This section of code is shared by all the types of alerts. It puts up the alert ; box, handles the user events, and returns the function value to the caller. ; Alert parameters--offset from A6 ABRetAddr .EQU 4 ; Return address ABAction .EQU ABRetAddr+4 ; action proc IDNumber .EQU ABAction+4 ; alert or dialog box ID ABResult .EQU IDNumber+2 ; Integer result ; In CmnAlert, ; A2 is handle to the alert structure ; A3 is the old grafport ; A4 is the alertWindow ; D3 is the icon ID ; D4 is the stages word Alert MOVE.L #-1, D0 ; not pre-set by Note/Wait/Stop/Caution CmnAlert BSR StdEntry ; link A6, save regs MOVE.W D0, D3 ; Save icon ID in D3 MOVE.W IDNumber(A6), D0 ; Set alert ID number BSR GetAHndl ; Get it, returns handle in A2, ptr in A1. MOVE.W ACount, D0 ; Get alert count MOVE.W IDNumber(A6), D1 ; and the alert ID number. CMP.W ANumber, D1 ; is this alert number the same as last? BEQ.S NextStage ; yes, increment count MOVE.W D1, ANumber ; Otherwise update ANumber MOVEQ #0, D0 ; and zero the count NextStage MOVE.W D0, ACount ; Save new count MOVE.W AStages(A1), D4 ; Get the stages field STLoop SUBQ #1, D0 ; decrement stage number BLT.S STSet ; At correct stage LSR #4, D4 ; Shift stages down BRA.S STLoop STSet AND #$F, D4 ; And off the four bits ; Stages nybble: ; D4 = ( X )( X )( XX ) ; ok/cancel--------^ ^box ^^----sound proc ; beep first MOVE.L D4, D0 AND #3, D0 ; Mask off low bits BSR CallSound ; Make a sound--must preserve A0-A2 ; Should I put up alert box? BTST #2, D4 ; is the bit set? BNE.S DoAlert ; Yes, do an alert box MOVE.W MinusOne, ABResult(A6) ; -1 means no result BRA AlertExit ; and return DoAlert MOVE.L #FEMask, D0 ; Set event and stop masks _FlushEvents ; Flush all mouse+key events before alert. MOVE.W AItems(A1), D0 ; Get item list ID in D0 BSR GetILCopy ; Get item list copy in A0. Smashes A1 MOVE.L (A2), A1 ; Dereference again. SUBQ #4, SP ; Save space for result CLR.L -(SP) ; Push 0--allocate new pointer PEA ABounds(A1) ; Push standard rectangle PEA ABTitleStr ; Push title string ST -(SP) ; Push visible TRUE MOVE.W #DBoxProc, -(SP) ; Push DialogBox proc ID (DBoxProc) MOVE.L MinusOne, -(SP) ; Push minusOne, for frontmost CLR.W -(SP) ; No go away button CLR.L -(SP) ; Push 0 refcon MOVE.L A0, -(SP) ; Items set _NewDialog ; Make a new dialog MOVE.L (SP), A4 ; Get new dialog pointer--leave for SetPort PEA oldPort(A6) ; Save oldPort _GetPort _SetPort ; And start writing in alert window. TST.W D3 ; Test default icon number BMI.S NoDefIcon ; check CC: if <0, none defined SUBQ #4, SP ; Save space for result MOVE.W D3, -(SP) ; Push default icon number _GetIcon ; Get the icon MOVE.L (SP)+, D0 ; Pop handle. Is it nil? BEQ.S NoDefIcon ; Yes, don't try to plot it. PEA ICRect ; Push icon rectangle first MOVE.L D0, -(SP) ; and push back handle _PlotIcon ; finally plot it! NoDefIcon ; Calculate the default item and stuff in dialog record ASR #3, D4 ; Shift ok/cancel bit down, trashing stages ADDQ.W #1, D4 ; one's based MOVE D4,ADefItem(A4) ; set the default item in the dialog ; Hilite the default item with bigger frame MOVE.L A4, -(SP) ; Push window pointer MOVE.W D4, -(SP) ; Push index of def item calc'ed aboive PEA scratch(A6) ; Ignore kind and ... MOVE.L (SP),-(SP) ; Ignore handle PEA curRect(A6) ; but get rectangle _GetDItem ; Get the item info LEA curRect(A6), A0 ; Copy address of rectangle in A0 MOVEQ #3, D0 ; 3x3 pensize MOVEQ #16, D1 ; outset by 4x4 MOVEQ #4, D2 ; Roundness 16x16 BSR FrameOut ; Frame outside rectangle pointed to by A0. ; Now that the dialog's up, call modal dialog with the user's action proc MOVE.L ABAction(A6),-(SP) ; push user's action proc PEA ABResult(A6) ; pass receiver for item _ModalDialog ; Something got hit AEExit MOVE.L oldPort(A6), -(SP) ; Push old port _SetPort ; Restore it MOVE.L A4, -(SP) ; Push alert window _DisposDialog ; Dispose of the dialog+item list AlertExit BCLR #Lock, (A2) ; Unlock alert template MOVE.W ACount, D1 ; Get alert count again ADDQ.W #1, D1 ; Add 1 to the alert count CMP.W #4, D1 ; Has it overflowed? BLT.S SetIt ; no, go ahead and set it, otherwise MOVEQ #3, D1 ; set it back SetIt MOVE.W D1, ACount ; set alert count to 4, both places MOVEQ #6, D0 ; Pop off 6 bytes of parameter BRA.S Go1StdExit ; ...and return. ;----------------------------------Dialogs---------------------------------- ; ; Utility GetILCopy ; ; This utility returns a copy of the handle to the item list given the ID ; ; D0 contains the item list ID ; A0 returns the handle ; Smashes D0-D2, A1. ; ;----------------------------------Dialogs---------------------------------- GetILCopy SUBQ #4, SP ; Save space for handle MOVE.L #ItmLstType, -(SP) ; push type MOVE.W D0, -(SP) ; Push ID number _GetResource ; and get resource handle MOVE.L (SP)+, A0 ; save handle in register ; Now copy the handle _HandToHand ; clone the handle in A0 RTS ; ...and return. ; ------------------------ ; ; Utility CallSound ; ; Calls sound routine indexed by D0 CallSound MOVEM.L D0-D2/A0-A2, -(SP) ; Save regs MOVE.L DABeeper,D1 ; see if defined BEQ.S noBeeper MOVE D0,-(SP) ; pass the stage # MOVE.L D1,A0 ; call the sound routine JSR (A0) noBeeper MOVEM.L (SP)+, D0-D2/A0-A2 ; Restore regs RTS ;----------------------------------Dialogs---------------------------------- ; ; FUNCTION GetNewDialog(dialogID: Integer; 16(A6) ; wStorage: Ptr; 12(A6) ; behind: WindowPtr): dialogPtr; 8(A6) ; ; This procedure returns a pointer to a dialogWindow as defined in the resource ; file. Calls NewDialog. ; ;----------------------------------Dialogs---------------------------------- GetNewDialog BSR StdEntry MOVE.W 16(A6), D0 ; Get dialog ID BSR GetDHndl ; Returns handle in A2, ptr in A1, locked. SUBQ #4, SP ; Save space for NewDialog result MOVE.L 12(A6), -(SP) ; Push wStorage MOVE.L A1, -(SP) ; Point to bounds rectangle PEA DTitle(A1) ; point to title string at end ADDQ.L #8, A1 ; Skip over bounds rect MOVE.L (A1)+, -(SP) ; Push visible flag, window def proc ID MOVE.L 8(A6), -(SP) ; Push behind MOVE.W (A1)+, -(SP) ; Push go away flag MOVE.L (A1)+, -(SP) ; Push refcon MOVE.W (A1)+, D0 ; Get item ID BSR.S GetILCopy ; Return copy of item list in A0 MOVE.L A0, -(SP) ; Push copy back on. _NewDialog ; Get new dialog window BCLR #Lock, (A2) ; Unlock the dialog record now MOVE.L (SP)+, 18(A6) ; Result in 18(A6) MOVEQ #10, D0 ; Pop off 10 bytes of parameter Go1StdExit BRA.S Go2StdExit ; and exit ;----------------------------------Dialogs---------------------------------- ; ; FUNCTION NewDialog(wStorage: Ptr; 34(A6) ; boundsRect: Rect; 30(A6) ; title: Str255; 26(A6) ; visible: BOOLEAN; 24(A6) ; theProc: INTEGER; 22(A6) ; behind: WindowPtr; 18(A6) ; goAwayFlag: BOOLEAN; 16(A6) ; refCon: LongInt; 12(A6) ; items: Handle): DialogPtr; 8(A6) ; ; This procedure returns a pointer to a dialogWindow. The field called items ; is a DialogList, which can be traversed by the Dialog Manager to draw the ; contents of the window. ; ;----------------------------------Dialogs---------------------------------- NewDialog BSR StdEntry ; Link A6, save regs. TST.L 34(A6) ; Test wStorage BNE.S GotStorage ; If not nil, got storage for me already MOVE.L #DWindLen, D0 ; Otherwise have to allocate new pointer _NewPtr ; Make the OS call MOVE.L A0, 34(A6) ; Copy it in parameter area GotStorage LEA 12(A6), A0 ; Start copying here MOVEQ #30, D0 ; Copy 30 bytes of parameters SUB.L D0, SP ; Subtract for stack frame MOVE.L SP, A1 ; Copy to the stack _BlockMove ; Copy all parameters except Items _NewWindow ; Create new window MOVE.L (SP)+, A4 ; Get result in A4 MOVE.L A4, 38(A6) ; Save windowPtr result MOVE.L 8(A6), A3 ; Get itemList handle to set in record MOVE.W #DialogKind, WindowKind(A4) ; Kind is Dialog MOVE.L A3, Items(A4) ; Set Item list handle MOVE.W MinusOne, EditField(A4) ; Say no field selected yet MOVE.W dlgFont,txFont(A4) ; System Font SUBQ #4,SP ; save port on stack MOVE.L SP,-(SP) _GetPort ; and get it. MOVE.L A4, -(SP) ; Push new port _SetPort MOVE #1,ADefItem(A4) ; set the default default item in the dialog SUBQ #4,SP ; make room for returned handle PEA portRect(A4) ; dummy rectangle MOVE.L (SP),-(SP) ; push twice _TENew ; allocate a text edit record MOVE.L (SP),teHandle(A4) ; save in window MOVE.L (SP)+,A1 ; get teHandle MOVE.L (A1),A2 ; and dereference it MOVE.L teTextH(A2),A0 ; get text handle _DisposHandle ; and get rid of it _SetPort BSR BeginInWind ; Set the port, edit field selection MOVEQ #InitSw, D4 ; Initialize the dialog item list BSR ScanAD ; scan through BSR EndInWind ; and restore after. MOVEQ #30, D0 ; Pop 30 bytes of parameter Go2StdExit BRA.S Go3StdExit ; use standard exit ; ;----------------------------------Dialogs---------------------------------- ; ; ClaimEvent used by IsDialogEvent and DialogSelect ; ; A utility to claim an event pointed to by A2 as a dialog event. The ; window pointer in question is returned in A4. ; ; Trashes D3 ; ClaimEvent MOVE.W EvtNum(A2), D3 ; Get the event number SUBQ #UpDatEvt,D3 ; if update or activate check message BEQ.S CheckMsg SUBQ #ActivateEvt-UpDatEvt,D3 BEQ.S CheckMsg SUBQ #4, SP ; Save space for windowptr _FrontWindow MOVE.L (SP)+, A4 ; Get frontmost window RTS CheckMsg MOVE.L EvtMessage(A2), A4 ; Get the window from activate/update RTS ;----------------------------------Dialogs---------------------------------- ; ; Function IsDialogEvent(event: EventRecord): BOOLEAN; ; ; This is a convenience finction used to classify an event as belonging to ; a dialog. A true result means it does and should be passed to DialogSelect ; to handle it. ; ;----------------------------------Dialogs---------------------------------- IDRetAddr .EQU 4 ; EventDialog return address IDEvent .EQU IDRetAddr+4 ; in event IDResult .EQU IDEvent+4 ; function result IsDialogEvent BSR StdEntry ; Link A6, save regs. ; First check if this is a dialog event CLR IDResult(A6) ; assume failure MOVE.L IDEvent(A6), A2 ; Get the event pointer BSR.S ClaimEvent ; -> A4 points to dialog window CMP.W #DialogKind, WindowKind(A4) BNE.S NotDlg ; If a dialog owns event, return TRUE ; For mouse down further qualify by making sure button is in content of the ; current window MOVE.W EvtNum(A2), D3 ; Get the event number SUBQ #MButDwnEvt,D3 ; see if mouse down BNE.S OKDlg ; if not it's ok SUBQ #2, SP ; Set up findWindow stack: MOVE.L EvtMouse(A2),-(SP) ; Push mouse position PEA whichWindow(A6) ; Push whichWindow variable _FindWindow ; Find out where the mouse hit was SUBQ.W #InContent, (SP)+ ; Is it in the content? BNE.S NotDlg ; If not in content, return not dialog CMP.L whichWindow(A6),A4 ; Was it THIS dialog window's content? BNE.S NotDlg OKDlg ADDQ.B #1,IDResult(A6) ; turn false into true NotDlg MOVEQ #4, D0 ; Pop 4 bytes of parameters Go3StdExit BRA.S Go4StdExit ;----------------------------------Dialogs---------------------------------- ; ; Function DialogSelect( event: EventRecord; ; VAR dialog: DialogPtr; ; VAR itemHit: INTEGER ): BOOLEAN; ; ; This function handles events in the given dialog window. It returns ; TRUE if a dialog event was handled, FALSE if clicked outside the window. ; ItemHit is the index of the item clicked on. It will return the dialog ; and the item hit if the event was handled (eg. TRUE returned) ; ;----------------------------------Dialogs---------------------------------- DSRetAddr .EQU 4 ; EventDialog return address DSItemHit .EQU DSRetAddr+4 ; item clicked on DSWindow .EQU DSItemHit+4 ; returned dialog wondow VAR DSEvent .EQU DSWindow+4 ; in/out event DSResult .EQU DSEvent+4 ; function result DialogSelect BSR StdEntry ; Link A6, save regs. ; First check if this is a dialog event CLR DSResult(A6) ; Set return boolean MOVE.L DSWindow(A6), A3 ; Get VAR address of dialog pointer CLR.L (A3) ; Set to NIL, assuming failure MOVE.L DSEvent(A6), A2 ; Check event classification and get dialog ptr BSR.S ClaimEvent ; -> A4 is dialog pointer in question MOVE.L A4, (A3) ; return dialog pointer from claim event BSR BeginInWind ; Set up registers, stack frame MOVE.L DSEvent(A6), A0 ; Copy the passed event in DSEvent LEA theEvent(A6), A1 ; to theEvent for easy decomposition MOVEQ #EvtBlkSize,D0 _BlockMove ; theEvent is the first event. BSR EventAD ; Handle event--result in D3 TST.W D3 ; Check to see if my event or not BMI.S NotDS ; Not my event ADDQ.B #$01, DSResult(A6) ; turn into TRUE, was my event. @ NotDS MOVE.L DSItemHit(A6), A0 ; get VAR address of user's item MOVE.W D3, (A0) ; Save item number hit BSR EndInWind ; Restore port NotMine MOVEQ #12, D0 ; Pop 16 bytes of parameter Go4StdExit BRA.S Go5StdExit ;----------------------------------Dialogs---------------------------------- ; ; PROCEDURE ModalDialog( filterProc: ProcPtr; VAR itemHit: INTEGER); ; ; This should be called after NewDialog or GetNewDialog has created a ; dialog and brought it to the top. It will ignore all non-dialog ; events and wait for a dialog event that chooses a valid item. This is ; used by the alert stuff above and by any user's who desire modal dialogs. ; ; The filter proc is defined as: ; ; FUNCTION FilterProc( frontWind: WindowPtr; event: EventRecord; VAR item: INTEGER): BOOLEAN; ; ; If result = true then filter simulated that the item got hit by user ; If false let things go on as usual ; ;----------------------------------Dialogs---------------------------------- MDitemAdd .EQU 8 ; VAR item address MDfilterProc .EQU 12 ; filterProc ModalDialog BSR StdEntry ; ho hum ; Hop into the event loop ELoop _SystemTask ; Allow desk ornaments some time ;jwp SUBQ #4,SP _FrontWindow ; see if current window has an edit field MOVE.L (SP)+,A0 TST.W EditField(A0) ; do we have an editable field? BMI.S @1 MOVE.L TEHandle(A0),-(SP) _TEIdle @1 ;jwp SUBQ #6, SP ; Save space for boolean result MOVE.W #$017F, -(SP) ; Get vanilla events - NOT disk-inserted PEA theEvent(A6) ; Push pointer to event record _GetNextEvent ; Get the next event. ignore result ; Pass to action proc if there is one MOVE.L MDfilterProc(A6),D3 ; see if defined eg. not nil BNE.S usersFilter ; Camel Cigarettes(sigh) LEA AlertFilter,A0 ; pass alert filter proc MOVE.L A0,D3 usersFilter _FrontWindow ; "push" window ptr PEA theEvent(A6) ; pass the event MOVE.L MDitemAdd(A6),-(SP) ; pass receiver for item MOVE.L D3,A0 ; get proc ptr JSR (A0) ; call the proc TST.B (SP)+ ; see what reply is BNE.S MDExit ; See if dialog event SUBQ #2, SP ; Save space for boolean result PEA theEvent(A6) ; see if a dialog event _IsDialogEvent TST (SP)+ ; see if it was BEQ.S NotDialogs ; It is a dialog event, so pass it to dialog select to see if we got an item SUBQ #2,SP ; make room for result PEA theEvent(A6) ; Pass the dialog event PEA localPoint(A6) ; pass the fake receiver for dialog ptr MOVE.L MDitemAdd(A6),-(SP) ; pass receiver for item _DialogSelect TST (SP)+ ; see if an item was hit BLE.S ELoop ; if none, go get next event ; An item was hit so return MDExit MOVEQ #8,D0 ; only one long parameter Go5StdExit BRA.S StdExit ; adios for now ; Beep if it was a non-dialog mouse down event NotDialogs MOVE.W theEvent(A6), D0 ; get event type SUBQ #MButDwnEvt, D0 ; Is it mouse down? BNE.S ELoop ; if not, ignore it MOVEQ #1, D0 ; otherwise call sound 1 BSR.S CallSound ; Beep BRA.S ELoop ; ...and loop back ; -------- ; UTILITY StdEntry ; ; Link A6, save regs StdEntry MOVE.L (SP)+, A0 ; Get return address LINK A6, #EvtLink ; link standard stack frame MOVEM.L A2-A4/D3-D7, -(SP) ; Save all regs JMP (A0) ; ...and return. ; -------- ; UTILITY StdExit ; ; Unlink A6, restore regs, pop D0 byte for parameters. StdExit MOVEM.L (SP)+, A2-A4/D3-D7 ; Restore regs UNLK A6 MOVE.L (SP)+, A0 ; Get return address ADD.L D0, SP ; pop off parameters JMP (A0) ; and return. ; -------------------------------- ; ; Procedure DrawDialog(dialog: DialogPtr); ; ; This procedure draws the dialog window. The user may call ; BeginUpdate//EndUpdate to fix the update region, if she so desires. ; DrawDialog BSR.S StdEntry ; Link A6, save regs. MOVE.L 8(A6), A4 ; Get pointer in A0 BSR.S BeginInWind ; Set up registers, stack frame MOVE.L A4, -(SP) ; Push window pointer _DrawControls ; Draw all controls MOVEQ #DrawSw, D4 ; Set draw switch BSR ScanAD ; Scan through items BSR.S EndInWind ; BRA.S DCExit ; use CloseDialog exit. ; -------------------------------- ; ; Procedure CloseDialog(dialog: DialogPtr); ; ; This procedure closes a dialog window. It does not dispose of the space ; allocated or the item list handle--the user must do it explicitly. ; CloseDialog BSR.S StdEntry ; Link A6, save regs. MOVE.L 8(A6), A4 ; Get dialog pointer BSR.S BeginInWind ; Set up registers, stack frame MOVE.L ClipRgn(A4),-(SP) ; zero the clip out _SetEmptyRgn MOVEQ #DispSw, D4 BSR ScanAD ; Dispose of all items BSR.S EndInWind ; restore port ; fake TEDispose ; Can't use because of handle tricks MOVE.L teHandle(A4),A0 ; look in text edit handle for MOVE.L (A0),A0 ; dereference it MOVE.L teHandle(A4),A0 ; dispose text edit handle _DisposHandle MOVE.L A4, -(SP) ; Push window pointer for closeWindow _CloseWindow ; Close the window DCExit MOVEQ #4, D0 ; Pop 4 bytes of parameter Go6StdExit BRA.S StdExit ; and call standard exit ; ---------------- ; ; Utility BeginInWind ; ; Set up registers, stack frame values, port given window in A4. ; Smashes A0-A3, D0-D3. BeginInWind PEA oldPort(A6) ; Push oldPort address _GetPort ; and get it. MOVE.L A4, -(SP) ; Push new port _SetPort MOVE.L Items(A4), A3 ; Get items in A3 BSET #Lock,(A3) ; lock the item list MOVEQ #-1, D3 ; Start item loop MOVE.W EditField(A4), D2 ; Get edit field BMI.S FFLoop ; If there isn't one yet, find one. SRLoop BSR NxtItem ; Get the next item DBRA D2, SRLoop ; Skip to edit field BRA.S GotField ; Set the variables now FFLoop BSR NxtItem ; Get the next item BEQ.S NoEF ; If EQ, no edit field. BTST #EditText, ItmType(A2) ; Is it editable text? BEQ.S FFLoop ; No, loop back GotField MOVEQ #-2,D2 ; select default field (Magic value for ; setnewedit means select all) BRA SetNewEdit ; Set up the edit variables ; implicit return ; ---------------- ; ; Utility EndInWind ; ; Restore port, handle other terminating stuff. (nothing currently) EndInWind BCLR #Lock,(A3) ; unlock the item list MOVE.L oldPort(A6), -(SP) ; Restore port _SetPort NoEF RTS ; and return. ; -------------------------------- ; ; Procedure DisposDialog(dialog: DialogPtr); ; ; This procedure disposes of a dialog window. It does a close, then disposes ; the space allocated. The item list handle is also disposed of. ; DisposDialog MOVE.L 4(SP), A0 ; Get dialog ptr in A0 MOVE.L Items(A0), -(SP) ; Push item list for later MOVE.L A0, -(SP) ; also pointer to close _CloseDialog ; Close the dialog MOVE.L (SP)+, A0 ; Get item list handle _DisposHandle ; dispose of the handle (usually a copy) MOVE.L (SP)+, A1 ; Get return address MOVE.L (SP)+, A0 ; and dialog ptr _DisposPtr ; Dispose of the storage JMP (A1) ; ...and return. ; ---- ; Utility GetLstA2 ; Given the alert handle in A2, get item list handle in A2 ; GetLstA2 MOVE.L (A2), A0 ; Dereference alert SUBQ #4, SP ; Save space for handle MOVE.L #ItmLstType, -(SP) ; Push ItemList resource type MOVE.W AItems(A0), -(SP) ; Push the dialoglist ID GetCmn _GetResource ; Get the resource MOVE.L (SP)+, A2 ; Copy list down RTS ; and return. ; ---------------------------------- ; ; Procedure CouldAlert(alertID: Integer); ; ; This procedure loads the alert item list into memory (if it isn't already) and ; locks it. This is useful for when the application isn't sure that the resource ; file is accessable, such as during a diskette copy. ; ; some code should be saved here? CouldAlert BSR.S StdEntry ; Link A6, save regs. MOVE.W 8(A6), D0 ; Get alert ID BSR.S GetAHndl ; Get the handle in A2. BEQ TwoByteExit ; If no alert, quit. AND.B #$3F,(A2) ; neither locked nor purgeable BSR.S GetLstA2 ; Get the list in A2 CAExit BCLR #6, (A2) ; Don't allow it to be purged MOVE.L A2, A3 ; Copy list to A3 for scan MOVEQ #LoadLockSw, D4 ; Set switch to BSR CmnStuff ; Get the window def proc in A2. BCLR #6, (A2) ; Clear purgable bit. BRA TwoByteExit ; and exit ;jwp ; ---------------------------------- ; ; Procedure CouldDialog(alertID: Integer); ; ; This procedure loads the dialog item list into memory (if it isn't already) and ; locks it. This is useful for when the application isn't sure that the resource ; file is accessable, such as during a diskette copy. ; ; some code should be saved here? CouldDialog BSR.S StdEntry ; Link A6, save regs. MOVE.W 8(A6), D0 ; Get alert ID BSR.S GetDHndl ; Get the handle in A2. BEQ.S TwoByteExit ; If no alert, quit. AND.B #$3F,(A2) ; neither locked nor purgeable BSR.S GetLstD2 ; Get the list in A2 BRA.S CAExit GetLstD2 MOVE.L (A2),A0 SUBQ #4,SP MOVE.L #ItmLstType,-(SP) MOVE.W DItems(A0),-(SP) JMP GetCmn ;jwp ; ---------------------------- ; ; Utility GetAHndl, GetDHndl ; ; This utility returns the handle to the dialog or alert template ; ; D0 contains the alert or dialog ID ; A2 returns the handle, A1 the pointer. The handle is LOCKED on return. ; Smashes D0-D2 GetAHndl MOVE.L #AlertType, A0 ; Set type BRA.S GetADHndl GetDHndl MOVE.L #DilogType, A0 ; Set type GetADHndl SUBQ #4, SP ; Save space for handle MOVE.L A0, -(SP) ; Push type MOVE.W D0, -(SP) ; Push ID number _GetResource ; and get resource handle MOVE.L (SP), A2 ; copy handle in register BSET #Lock, (A2) ; Lock down the template MOVE.L (A2), A1 ; Dereference TST.L (SP)+ ; set CC's for return RTS ; return to caller. ; -------------------- ; Procedure FreeAlert(alertID: Integer); ; ; This unlocks a locked alert and allows purging. If CouldAlert had been ; called previously, this procedure can be called after the critical section ; is passed to allow purging of the alert again. ; FreeAlert BSR StdEntry ; Link A6, save regs. MOVE.W 8(A6), D0 ; Get alert ID BSR.S GetAHndl ; Get the handle in A2 BEQ.S TwoByteExit ; If no alert, quit. BCLR #Lock, (A2) ; Not locked BSR FreeA2 ; make the template purgable BSR.S GetLstA2 ; Get the item list FAExit BSR FreeA2 ; And free it MOVE.L A2, A3 ; Copy list to A3 for scan MOVEQ #FreeSw, D4 ; Set switch to BSR CmnStuff ; Get the window def proc in A2. BSR FreeA2 ; Free the def proc. TwoByteExit MOVEQ #2, D0 Go7StdExit BRA Go6StdExit ;jwp ; -------------------- ; Procedure FreeDialog(alertID: Integer); ; ; This unlocks a locked dialog and allows purging. If CouldDialog had been ; called previously, this procedure can be called after the critical section ; is passed to allow purging of the dialog again. ; FreeDialog BSR StdEntry ; Link A6, save regs. BSR.S GetDHndl ; Get the handle in A2 BEQ TwoByteExit ; If no alert, quit. BCLR #Lock, (A2) ; Not locked BSR FreeA2 ; make the template purgable BSR.S GetLstD2 ; Get the item list BRA.S FAExit ;jwp ; ---- ; Utility CmnStuff ; Get the window def proc for alerts in A2 ; after calling the scan code. Entry: D0 - scan Op code ; CmnStuff BSET #Lock,(A3) ; lock the item list ***** BSR ScanAD ; free items and def procs BCLR #Lock,(A3) ; unlock the item list ***** SUBQ #4, SP ; Save space for handle MOVE.L #WDefRType, -(SP) ; Push window def type MOVE.W #DocumentProc, -(SP) ; Superclass of DBoxProc variant. BRA.S GetCmn ; ---------------------------------- ; ; Procedure ParamText(cite0, cite1, cite2, cite3: String); ; ; This sets the citations ^0, ^1, ^2 and ^3 for use in the next alert or dialog. ; NIL params skip ; ParamText LEA DAStrings+16, A1 ; String list pointer MOVEQ #3, D2 ; Loop 4 times ParamLoop MOVE.L 4(SP), D1 ; Get string ptr off stack MOVE.L -(A1), A0 ; Get old handle BEQ.S nilParam ; on D1 MOVE.L A0, D0 ; Set condition codes--was it NIL? BEQ.S OkPop ; yes, just allocate new one _DisposHandle ; Otherwise dispose of it OkPop MOVE.L D1,A0 ; get the string BSR StrToHandle ; make it into a handle MOVE.L A0,(A1) ; Store it in param location nilParam MOVE.L (SP)+, (SP) ; Pop return onto it DBRA D2, ParamLoop ; loop back RTS ; ...and return ; ---------------------------------- ; ; Procedure ErrorSound(sound: ProcPtr); ; ; This sets the sound procedure pointer for use in the staged alerts. ; ErrorSound MOVE.L (SP)+,A0 ; pop return address MOVE.L (SP)+,DABeeper ; put in low memory JMP (A0) ; adios ; -------------------------------- ; ; Procedure GetDItem(dialog: DialogPtr; 22(A6) ; itemNo: Integer; 20(A6) ; VAR kind: Integer; 16(A6) ; VAR item: Handle; 12(A6) ; VAR box: Rect); 8(A6) ; ; This procedure returns the item in the list. ; ; Stack offsets to parameters GetDItem BSR StdEntry ; Link A6, save regs. MOVE.L 22(A6), A4 ; Get dialogPtr BSR.S BeginInWind ; Set up regs given window pointer MOVE.W 20(A6), D3 ; Get item index. SUBQ.W #1, D3 ; need it zero-based BSR GetDLItem ; point to the item. BEQ.S NoItm1 ; If it can't be found, just return. LEA ItmRect(A2), A0 ; Rectangle source MOVE.L 8(A6), A1 ; Destination rect MOVE.L (A0)+,(A1)+ ; Copy rect in MOVE.L (A0)+,(A1)+ MOVEM.L 12(A6),A0-A1 ; get the var params into A0,A1 MOVE.L itmHndl(A2),(A0) ; Get attribute long MOVEQ #0, D0 ; Clear high bits MOVE.B itmType(A2), D0 ; get kind parameter MOVE.W D0, (A1) ; Save kind in VAR NoItm1 BSR.S EndInWind ; Restore port MOVEQ #18, D0 ; Parms to pop Go8StdExit BRA Go7StdExit ; standard exit ; -------------------------------- ; ; Procedure SetDItem(dialog: DialogPtr; 20(A6) ; itemNo: Integer; 18(A6) ; kind: Integer; 16(A6) ; item: Handle; 12(A6) ; box: Rect); 8(A6) ; This procedure sets the item in the dialogList. ; SetDItem BSR StdEntry ; Link A6, save regs. MOVE.L 20(A6), A4 ; Get dialogptr BSR.S BeginInWind ; Set up regs given windowptr MOVE.W 18(A6), D3 ; Get item index. SUBQ.W #1, D3 ; need it zero-based BSR GetDLItem ; Point to the dialoglist item BEQ.S NoItm2 ; If not found, quit. MOVE.L 8(A6), A0 ; rectangle source LEA ItmRect(A2), A1 ; Destination MOVE.L (A0)+,(A1)+ ; Copy rectangle MOVE.L (A0)+,(A1)+ MOVE.L 12(A6), itmHndl(A2) ; Save handle of object MOVE.B 17(A6), itmType(A2) ; Save low byte of kind word NoItm2 BSR.S EndInWind ; Restore port MOVEQ #16, D0 ; Parms to pop Go9StdExit BRA Go8StdExit ; standard exit ; -------------------------------- ; ; Procedure GetIText(item: Handle; ; VAR text: Str255); ; Given a text handle and a string variable, puts the contents of the handle ; into the string. GetIText MOVEQ #0,D1 ; build a 255 long SUBQ.B #1,D1 ; turn into $FF BSR StdEntry MOVE.L 8(A6), A1 ; Destination address is pointer MOVE.L 12(A6), A0 ; source address is handle _GetHandleSize ; get string length CMP.L D1,D0 ; see if too big BLE.S okStrLength MOVE.L D1,D0 ; max out at 255 okStrLength MOVE.B D0,(A1)+ ; set dest string length MOVE.L (A0), A0 ; Dereference _BlockMove ; Copy those bytes! BRA.S TwoLongExit ; ...and exit ; -------------------------------- ; ; Procedure SetIText(item: Handle; 12(A6) ; text: Str255); 8(A6) ; Given a text handle and a string, sets the contents of the handle ; to be the string. ; Traverse through the window list and find all windows of kind Dialog. ; For each dialog in the list, traverse its item list and find any entries ; which have the same text handle. For each text item of that type, redraw ; the item. SetIText BSR StdEntry ; Link A6, save regs. MOVE.L 8(A6), A0 ; Source is string address MOVEQ #0,D0 ; put in a handle MOVE.B (A0)+,D0 MOVE.L 12(A6), A1 ; Dest is handle MOVE.L A1,D6 ; save for later compare _PtrToXHand ; clone pointer in an existing handle MOVE.L WindowList, A4 ; Get top of window list WLScan CMP.W #DialogKind, WindowKind(A4) ; Is it a dialog window? BNE.S NextWnd ; No, get next window in list GotDWind BSR.S BeginInWind ; Set up registers MOVEQ #-1, D3 ; Start item loop WILoop BSR NxtItem ; Get the next item BEQ.S ExitWnd ; If no more, go to next window CMP.L ItmHndl(A2),D6 ; Check handle and ... @ BNE.S WILoop ; If not, loop back CMP EditField(A4),D3 ; see if edit field. If so, call the recal BNE.S notEditing ; stuff to insure correctness with edit record MOVE.L teHandle(A4),-(SP) ; get teHandle and... _TECalText notEditing PEA CurRect(A6) ; Push the rectangle... MOVE.L (SP), -(SP) ; ... _EraseRect ; and erase the destination rectangle. BSR DrawItem ; Draw it! _ValidRect ; subtract from update region. ; Ignore possible frame, which will be redrawn ExitWnd BSR.S EndInWind ; Restore registers, port. NextWnd MOVE.L NextWindow(A4), A4 ; Get next window pointer MOVE.L A4, D0 ; Set condition codes BNE.S WLScan ; If still more windows, continue. TwoLongExit MOVEQ #8, D0 Go10StdExit BRA Go9StdExit ; -------------------------------- ; ; Procedure SelIText(dialog: dialogPtr; ; itemNo: INTEGER; ; selStart: INTEGER; ; selEnd: INTEGER ); ; Select the given field in the dialog. SITEnd .EQU 8 SITStart .EQU SITEnd+2 SITitem .EQU SITStart+2 SITDialog .EQU SITitem+2 SelIText BSR StdEntry ; Link A6, save regs MOVE.L SITDialog(A6), A4 ; Get dialog pointer BSR.S BeginInWind ; Set up the registers. D3 is edit field. MOVE.W SITItem(A6), D3 ; Get item number of new select field SUBQ #1,D3 BSR GetDLItem ; Point A2 to the new text MOVE.L SITEnd(A6),D2 ; get both start and end but flipped SWAP D2 BSR ChangeField BSR.S EndInWind ; Set old port back MOVEQ #10, D0 Go11StdExit BRA Go10StdExit ;****************************************************************************** ; DMgrAsm2 ; Alert and Dialog Box Manager for the Macintosh User Interface Standard ; ; Written by Bruce Horn 7 October 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains scanning and searching utilities. ; ; MODIFICATION HISTORY: see dmgr ; ; ; ****************************************************************************** ; -------------------------------------------------------------------------------- ; ; Utility ScanAD ; ; Draw each item in its hitRect which is un-initialized. ; On Entry: ; A4 is dialogPtr ; D4 is switch: ; ; -2=Load and lock items down ; -1=Free items which may have been locked. ; 0=Initialize the list for this window ; 1=draw all items ; 2=dispose of items ; ; In Use: ; A2 points to current item in dialogList ; A3 is item list handle; D3 is current item index ; A4 is window pointer. ; D4 has draw/restore message ; Type byte is interpreted as follows: ; ; (7) (6) (5) (4) (3) (2) (1) (0) ; --------------------------------------------------------- ; ! cont ! pic ! icon ! edit ! stat ! ctrl ! ! ! ; !dialog! item ! item ! text ! text ! item ! ! ! ; --------------------------------------------------------- ; ==> (0 = button itm) ; (1 = check item) ; (2 = radio item) ; (3 = resource ) ; ScanAD MOVEM.L D0-D7/A0-A4, -(SP) ; Save all registers MOVEQ #-1, D3 ; Start item loop ItemLoop BSR NxtItem ; Get the next item in A2 BNE.S GotItem ; if non-zero handle, have real item MOVEM.L (SP)+, D0-D7/A0-A4 ; Restore all registers RTS ; and return ; -------- ; Utility GetH(andleT(ype ; Returns handle in A0, type in D0. GetHT MOVE.L ItmHndl(A2), A0 ; Get handle in A0 MOVE.B ItmType(A2), D0 ; Get type in D0 RTS GotItem BSR.S GetHT ; Get handle in A0, type in D0 PEA ItemLoop ; Return to item loop CMP.W #DrawSw, D4 ; Switch: BGT DispItem ; Dispose the item, or BEQ DrawItem ; Draw the item, otherwise init the item TST.W D4 ; Is it init? BEQ.S InitItem ; Yes, otherwise free/lock ; ---------------- ; Utility FreeLock ; Free or lock the item. ; For text items, does nothing. For controls, loads and locks the def proc. ; For icons and pictures, loads and locks the resource. FreeLock BTST #CtrlItem, D0 ; Is it a control item? BNE.S FLCtl ; If so, load/free the def proc. ROXR #7,D0 ; see if icon or picture BMI.S FLIcon ; (IconItem = 5) BCS.S FLPic ; (PicItem=6) NoFL RTS ; If none of these, quit. ; Free or lock a control defproc. FLCtl AND.W #$3, D0 ; And off low bits to get res ID/switch. CMP #ResCtrl, D0 ; Is it a resource control? BNE.S FLStdCtl ; If not, handle standard control. PEA RCCont ; hack to subroutinize FLCmn MOVE.L #CtrlRType, D1 ; Set control type to load/lock template. BRA.S FLIPCmn ; Load ID from data field like icon/pic. RCCont MOVE.L (A2), A1 ; Dereference template handle MOVE.W 16(A1), D0 ; Get this control's def proc ID ; ...and fall through to load/lock it too. FLStdCtl MOVE.L #CDefRType, D1 ; Set control def type. ID in D0. BRA.S FLCmn ; Branch to common code ; Free or lock icon/picture FLIcon MOVE.L #IconRType, D1 ; Get icon type in D1 BRA.S FLIPCmn ; load ID from data field FLPic MOVE.L #PicRType, D1 ; Get picture type in D1 FLIPCmn MOVE.W ItmData+1(A2), D0 ; Get icon/pic/control template ID in D0. FLCmn SUBQ #4, SP ; Save space for handle MOVE.L D1, -(SP) ; Push type MOVE.W D0, -(SP) ; Push ID _GetResource ; Get the picture/icon/defproc handle MOVE.L (SP)+, A2 ; Copy to A2 (smashing pointer) CMP.W #FreeSw, D4 ; Free it or lock it? BEQ.S FreeA2 ; Free the resource and return. BCLR #6, (A2) ; Set purgable to false RTS ; ...and return. ; ---- ; Utility FreeA2 ; Set the purgable bit TRUE if it was originally allowed to be purgable. ; FreeA2 SUBQ #2, SP ; Save space for function result MOVE.L A2, -(SP) ; Push the handle _GetResAttrs ; Get the resource attributes MOVE.W (SP)+, D0 ; In D0 BTST #5, D0 ; Can it be purged? BEQ.S CantFree ; No, can't free it BSET #6, (A2) ; Otherwise set the purgable bit CantFree RTS ; ---------------- ; Utility InitItem ; ; Initialize the item pointed to by A2. Uses handle, type given by ScanAD. ; D0 contains the type of item InitItem MOVE.L A0, D1 ; Set condition codes BEQ.S OKInit ; if equal to zero, ok to init RTS ; Otherwise return OKInit BTST #CtrlItem, D0 ; Is it a control item? BNE.S ICtrlItem ROXR #5,D0 ; see if static or editable text BCS.S ITextItem ; (StatText=3 or EditText=4) BMI.S ITextItem ROXR #2,D0 ; see if icon or picture BMI.S IIconItem ; (IconItem = 5) BCS.S IPicItem ; (PicItem=6) RTS ; If none of these, it's a user field ; Initialize a text item, either static or editable ITextItem LEA ItmData(A2), A0 ; Point to text part of item MOVEQ #0,D0 ; put in a handle MOVE.B (A0)+,D0 _PtrToHand ; clone pointer in a handle MOVE.L A0, -(SP) ; Push the handle for SaveItmHndl BRA.S SaveItmHndl ; Initialize an icon item IIconItem MOVE.L #IconRType, D0 ; Set type for later push BRA.S IIconPicCmn ; Branch to common code ; Initialize a picture item IPicItem MOVE.L #PicRType, D0 ; Set picture type for later push. IIconPicCmn SUBQ #4, SP ; Save space for result MOVE.L D0, -(SP) ; Push icon or picture type type MOVE.W ItmData+1(A2), -(SP) ; Push icon or pic ID, (skip length byte) _GetResource ; Get the picture/icon handle BRA.S SaveItmHndl ; Save it in the item handle location ; Initialize a default control item (button, checkbox, radio button control) ICtrlItem AND.W #$3, D0 ; And off low bits CMP.W #ResCtrl, D0 ; Is it a resource control? BNE.S DefCtrl ; If not, normal default control type SUBQ #4, SP ; Push space for result MOVE.W ItmData+1(A2), -(SP) ; Push control ID MOVE.L A4, -(SP) ; push window _GetNewControl ; Get the control info handle MOVE.L (SP), -(SP) ; Copy control handle for moveControl MOVE.L ItmRect(A2), -(SP) ; Push topLeft of the item rectangle _MoveControl ; Move it to my topLeft+keep size. BRA.S ValidCtrl ; Validate and save it. DefCtrl SUBQ #4, SP ; Push space for result MOVE.L A4, -(SP) ; Push window pointer PEA CurRect(A6) ; Push rectangle PEA ItmData(A2) ; Push address of string ST -(SP) ; Push TRUE (visible flag) CLR.L -(SP) ; Push value=min=0 MOVE.W #1, -(SP) ; Push max=1 MOVE.W D0, -(SP) ; Push control proc ID CLR.L -(SP) ; Push 0 for refcon _NewControl ; Make a new control item ValidCtrl PEA CurRect(A6) ; Push rect _ValidRect ; ...and validate it--(drawn first time!) SaveItmHndl MOVE.L (SP)+, ItmHndl(A2) ; Save control handle RTS ; and return. ; ---------------- ; Utility DrawItem ; ; Draw the item pointed to by A2. Can be called from outside of ScanAD. DrawItem BSR.S GetHT ; Get handle and type in D0 BTST #CtrlItem, D0 ; Is it a control item? BNE.S DCtrlItem ROXR #5,D0 ; see if static or editable text BCS.S DTextItem ; (StatText=3 or EditText=4) BMI.S DTextItem ROXR #2,D0 ; see if icon or picture BMI.S DIconItem ; (IconItem = 5) BCS.S DPicItem ; (PicItem=6) DUserField ; If none of above, draw a user field. MOVE.L ItmHndl(A2), D0 ; Get proc ptr BEQ.S NoUFDraw ; If NIL, don't do anything MOVE.L A4, -(SP) ; Push my window MOVE.W D3, -(SP) ; and item ADDQ.W #1, (SP) ; +1 for one's based MOVE.L D0, A0 ; Get proc ptr JSR (A0) ; call the update procedure NoUFDraw RTS ; ...and return. ; Draw an icon item DIconItem PEA CurRect(A6) ; Push rect MOVE.L ItmHndl(A2), -(SP) ; push the handle MOVE.L (SP),-(SP) ; push twice _LoadResource _PlotIcon ; and draw it RTS ; Draw a picture item DPicItem MOVE.L ItmHndl(A2), -(SP) ; Push picture handle MOVE.L (SP),-(SP) ; push twice _LoadResource PEA CurRect(A6) ; destination rectangle _DrawPicture ; Draw it ; Draw any control item. Ignored, since DrawControls is called by DrawDialog. DCtrlItem RTS ; ...and return. ; ---- ; Draw a text item, either static or editable DTextItem SMI D7 ; D7 = $FF if static text CMP EditField(A4),D3 ; See if this field is the "open" one BNE.S DoStatic ; This part draws the current field open for editing.... PEA curRect(A6) ; Erase whole rectangle MOVE.L (SP),-(SP) ; dup rect ptr for teupdate below _EraseRect ; MOVE.L teHandle(A4),-(SP) ; get teHandle and... _TEUpdate ; redraw the text ; Frame the edittable text items tryFrame TST.B D7 BNE.S DTextAdios ; if <>0 then static text--leave as is. LEA CurRect(A6), A0 ; Get current rectangle in A0 MOVEQ #1, D0 ; 1x1 pensize MOVEQ #1, D1 ; Roundness 1 MOVEQ #3, D2 ; outset by 3x3 BSR FrameOut DTextAdios ; RTS ; This part draws any field not open for editing (eg. static ) DoStatic MOVEM.L A3-A4/D2-D7,-(SP) ; save regs MOVE.L itmHndl(A2),A0 ; get text handle _HandToHand MOVE.L A0,D7 ; save copy handle MOVEQ #2,D5 ; useful number for search(length of "^1") LEA DAStrings,A3 ; point to string handles LEA arrows,A4 ; point to replacement strings MOVEQ #3,D6 ; loop counter pLoop MOVEQ #0,D3 ; search index sloop MOVEQ #0,D0 ; point to replacement string MOVE.L (A3),D4 ; check next parameter in list BEQ.S noParam ; skip if no parameter(sets length to 0) MOVE.L D4,A0 ; get string handle _HLock MOVE.L (A0),A0 ; dereference string handle MOVE.B (A0)+,D0 ; get string length noParam SUBQ #4,SP ; make room for returned index MOVE.L D7,-(SP) ; push text handle MOVE.L D3,-(SP) ; push index to search at MOVE.L A4,-(SP) ; search string address MOVE.L D5,-(SP) ; length of search string MOVE.L A0,-(SP) ; pointer to text MOVE.L D0,-(SP) ; string length _Munger MOVE.L (SP)+,D3 ; pop next index BPL.S sloop ; continue if more TST.L D4 ; check next parameter in list BEQ.S noPar ; skip if no parameter(sets length to 0) MOVE.L D4,A0 ; get string handle _HUnLock noPar ADDQ #2,A4 ; point to next string ADDQ #4,A3 ; point to parameter DBF D6,ploop ; keep going for all three MOVE.L D7,A0 ; get temp text handle BSET #Lock,(A0) ; lock string MOVE.L (A0),-(SP) ; pass ptr to string _GetHandleSize ; get the size of handle MOVE.L D0,-(SP) ; pass length PEA curRect(A6) ; rect to draw it in CLR -(SP) ; flush left format _TextBox MOVE.L D7,A0 ; get temp text handle ;BCLR #Lock,(A0) ; unlock string _DisposHandle ; and get rid of it MOVEM.L (SP)+,A3-A4/D2-D7 ; restore regs BRA.S tryFrame arrows .ASCII '^0^1^2^3' ; replacement macros ; ---------------- ; Utility DispItem ; ; Dispose of the item pointed to by A2. Uses attr, type given by ScanAD ; Could krunch the code a little. DispItem ROR #3,D0 ; see if cntrl item (cntrlItem = 2) BCS.S DCtrlItem ROXR #2,D0 ; see if static or editable text BCS.S RTextItem ; (StatText=3 or EditText=4) BMI.S RTextItem ClrItmHndl ; If icon, picture, user field, just CLR.L ItmHndl(A2) ; Zero the handle RTS ; ...and return ; Restore a text item. Just dispose of the handle and clear the hndl location. RTextItem _DisposHandle ; Get rid of the string handle in A0 BRA.S ClrItmHndl ; Clear the item handle on the way out. ; Remove (dispose) a control item--button/check/radio button/resource control. RCtrlItem MOVE.L A0, -(SP) ; Push the control handle _DisposControl ; Get rid of it. BRA.S ClrItmHndl ; Clear the item handle on the way out. ; ;--------------------------------------------------------------------------- ; ; Section to handle Activate events for dialogs and alerts ; DBActivate MOVE.L eventMsg(A6), A0 ; Get window pointer for update TST EditField(A0) ; Skip if no field there BMI.S NoItemHit MOVE.L teHandle(A0),-(SP) ; get teHandle and... MOVE.L A0,-(SP) ; Set port to this one _SetPort BTST #0,eventMeta+1(A6) ; Was it an activate? BEQ.S deact _TEActivate ; activate it BRA.S portIt deAct _TEDeactivate ; deactivate it portIt MOVE.L A4,-(SP) ; Set port to this one _SetPort BRA.S NoItemHit ; ------------------------------------------------------------------------------- ; ; Utility EventAD ; ; Handle events within the alert or dialog box ; A3 is dialogList handle ; A4 is window pointer ; In use: ; D5 is temp ; Returns D3=index of button which terminated events ; Disabled controls must have been hidden by the application-- ; Call EventAD the first time to handle existing event ; --- EventAD MOVE.W theEvent(A6), D0 ; get event type SUBQ #MButDwnEvt, D0 ; Mouse button down? BEQ DBMouse ; SUBQ #KeyDwnEvt-MButDwnEvt, D0 ; Key down? BEQ.S DBKeyDown ; SUBQ #AutoKeyEvt-KeyDwnEvt, D0 ; Auto Key down? BEQ.S DBKeyDown ; SUBQ #UpdatEvt-AutoKeyEvt, D0 ; Is it an update event? BEQ.S DBUpdate ; if <>, run action proc SUBQ #ActivateEvt-UpdatEvt, D0 ; Is it an activate event? BEQ.S DBActivate ; No real event, just idle on it TST EditField(A4) ; set return code again BMI.S NoItemHit MOVE.L teHandle(A4),-(SP) ; get teHandle and... _TEIdle ; NoItemHit MOVEQ #-1,D3 RTS DBUpdate MOVE.L A4, -(SP) ; Else push the window... MOVE.L (SP), -(SP) ; ... MOVE.L (SP), -(SP) ; ...three times _BeginUpdate ; Begin update BSR DrawDialog ; Draw all contents _EndUpdate ; End update BRA.S NoItemHit ; ----- ; Handle backspace, tab, CR as special (non-printing) characters. ; Handle all printing characters, distinguished as > SPACE. ; Return all command keys, other non-printing characters to application with ; item number 0 and inDialog value TRUE. Application may handle these in any ; way it would like. DBKeyDown MOVE.B eventAscii(A6), D1 ; Get the character typed CMP.B #ChTab, D1 ; Was it the TAB key? BEQ.S SkipToField ; if so, skip to next field MOVE EditField(A4),D3 ; Skip if no field there BMI.S NoItemHit ; If no field set, return to app MOVE D1,-(SP) ; push character MOVE.L teHandle(A4),-(SP) ; get teHandle and... _TEKey ; pass the key BRA.S GoItemHit ; and return item hit with key SkipToField MOVE.W EditField(A4), D3 ; Get the current field index SkipLp BSR.S NxtItem ; Get the next item in the list BEQ.S StartField ; Start over if no more items. CMP.W EditField(A4), D3 ; Is it the same as current? BEQ.S NoItemHit ; Ignore, continue handling events as usual BTST #EditText, ItmType(A2) ; Is it editable text? BEQ.S SkipLp ; If not, try next MOVE.L #$7FFF,D2 ; Tell it to select all of the field. BSR ChangeField ; else change the field GoItemHit BRA.S go2ItemHit ; return new field StartField MOVEQ #-1, D3 ; Start item loop again TST.W EditField(A4) ; Was there a field selected? BPL.S SkipLp ; If so, go ahead and wrap around goNoItemHit BRA.S NoItemHit ; otherwise can't find a field. ; ---------------- ; ; Utility NxtItem ; ; Get the next item or current item ; D3 is item index ; Returns A2 pointer to item ; GetDLItem sets CC for testing whether the item is past the end of the list ; IF EQ then past end, else item is ok NxtItem ADDQ #1, D3 ; Skip to next item BSR GetDLItem ; Get the item, A2 points to it RTS DBMouse MOVE.L eventPosition(A6), LocalPoint(A6) ; save mouse position PEA localPoint(A6) ; Push address of global mouse point _GlobalToLocal ; make it local ; Scan through the items and find out which one the click is in. FindField MOVEQ #-1, D3 ; Start item loop DBCLoop BSR.S NxtItem ; Get next item. BEQ goNoItemHit ; If no more, return SUBQ #2,SP ; make room for result MOVE.L LocalPoint(A6), -(SP) ; and local point PEA CurRect(A6) ; push curRect _PtInRect ; Make the LisaGraf call TST.B (SP)+ ; condition codes set BEQ.S DBCLoop ; if not, loop back, else found item rect. ; in the rectangle: curRect, A2 points to the item hit, D3 the item number BTST #CtrlItem, ItmType(A2) ; is it a control item? BEQ.S NotControl ; If not, check below ; It's a click in a control item SUBQ #2, SP ; Save space for findControl result MOVE.L localPoint(A6), -(SP) ; Push local point MOVE.L A4, -(SP) ; Push my window PEA whichCtrl(A6) ; Push address of which control _FindControl MOVE.W (SP)+, D0 ; Get findControl result BEQ goNoItemHit ; If zero, not in control SUBQ #2, SP ; Save space for integer result MOVE.L whichCtrl(A6), -(SP) ; Push my control MOVE.L LocalPoint(A6), -(SP) ; Push the point MOVE.L MinusOne, -(SP) ; use control's own actionProc, if possible _TrackControl ; Track it TST.W (SP)+ ; Test result BEQ goNoItemHit ; if not up in the control, ignore it ; We know the control was hit, return it to app go2ItemHit BRA.S ItemHit ; The mouse click is in an item defined by curRect and pointed to by A2 NotControl BTST #EditText, ItmType(A2) ; Is it editable text? BEQ.S ItemHit ; If not, simply return CMP.W EditField(A4), D3 ; Is this = current edit field BEQ.S TstContinue ; then don't skip to new one ; Select a new field with D3/A2 pointing to "new" field to open for edits SelField MOVEQ #0,D2 ; Tell it to select default. BSR.S ChangeField ; else change the field TstContinue TST EditField(A4) ; Skip if no field there BMI.S ItemHit MOVE.L LocalPoint(A6),-(SP) ; pass the click point BTST #1,eventMeta(A6) ; shift down? SNE -(SP) MOVE.L teHandle(A4),-(SP) ; Pass edit field to idle proc _TEClick ; An item was hit, if disabled ignore that fact, though ItemHit BTST #ItemDisable, ItmType(A2); is it disabled? BNE goNoItemHit ; If not, escape and don't look back ADDQ.W #1, D3 ; return one's based index RTS ; D3=index of control pushed ; ---------------------------- ; ; Utility ChangeField ; ; Change the field which is currently selected. ; Entry: ; D2 set up for selection range ; A2 points to the new field item. ; A4 dialog ptr ChangeField TST EditField(A4) ; see if a field is "open" BMI.S noneOpen ; MOVE.L teHandle(A4),-(SP) ; get teHandle _TEDeactivate noneOpen BSR.S SetItNew ; set new one if possible TST EditField(A4) ; see if a field is "open" BMI.S stillNone ; MOVE.L teHandle(A4),-(SP) ; get teHandle and... _TEActivate ; activate it stillNone RTS ; ---------------- ; Utility SetNewEdit, SetSelect. ; Given D3 the new edit field and CurRect the new edit rectangle, set the ; new RectEdit, updated TextEdit with the new handle, and set up CaretTicks. ; SetSelect sets up EditSelect, and returns NE if the string is not null, else EQ. ; ; Entry: ; D2 is a long containing selection start/end in high/lo word ; -2 means leave the selection untouched SetNewEdit ; Direct editing to new field if field is defined CMP.W EditField(A4),D3 ; See if different from current BEQ.S noneThere SetItNew MOVE.L ItmHndl(A2),D0 ; See if field defined BLE.S noneThere MOVE.W D3, EditField(A4) ; Save new field as current BMI.S noneThere ; if = -1 then no edit fields in ; this dialog MOVE.L teHandle(A4),A0 ; get teHandle MOVE.L (A0),A1 ; dereference MOVE.L D0,teTextH(A1) ; Point editable text to itmHndl MOVEQ #-2,D0 ; = -2 means default CMP.L D0,D2 ; see if selection specified BEQ.S @1 MOVE.L D2,teSelStart(A1) ; set selection @1 LEA teDestRect(A1),A0 ; assumes order in TEObject record MOVE.L curRect(A6),(A0)+ ; set dest rect MOVE.L curRect+4(A6),(A0)+ MOVE.L curRect(A6),(A0)+ ; set dest rect MOVE.L curRect+4(A6),(A0)+ MOVE D0,teClikLoc(A1) ; remove double click possibility ; by jamming -2 MOVE.L teHandle(A4),-(SP) ; get teHandle (A0 used below) _TECalText noneThere RTS ; ...and return. ;****************************************************************************** ; DMgrAsm3 ; Alert and Dialog Box Manager for the Macintosh User Interface Standard ; ; Written by Bruce Horn 7 October 1982 ; ; Copyright 1982 by Apple Computer, Inc. All Rights Reserved. ; ; This file contains utilities used by the alert and dialog box procedures ; and text ; ; ; MODIFICATION HISTORY: see dmgr ; ;****************************************************************************** ;------------------------------------------------------------------------------ ; UTILITIES ;------------------------------------------------------------------------------ ;---------------------------- ;Utility FrameOut ; ;D0 has a pen size ;D1 has a roundness factor ;D2 has an outset value (i.e. how many bits to outset the rect) ;A0 has the pointer to the rectangle FrameOut MOVE.L A0, -(SP) ; Push rect MOVE D2, -(SP) ; and size for re-insetting. MOVE D2, -(SP) ; and size for re-insetting. MOVE.L A0, -(SP) ; Push rect, MOVE D1, -(SP) ; Push roundness for frameRoundRect MOVE D1, -(SP) ; Push roundness for frameRoundRect MOVE.L A0, -(SP) ; Push rect for outset NEG D2 ; Neg word MOVE D2, -(SP) ; Push size MOVE D2, -(SP) ; Push size MOVE D0, -(SP) ; Push pensize for _PenSize MOVE D0, -(SP) ; Push pensize for _PenSize _PenSize _InsetRect ; Outset rectangle on stack _FrameRoundRect ; Frame it _InsetRect ; Re-inset it. RTS ; ...and return ;---------------------------- ;Utility StrToHandle ; ;A0=original pointer to string, returns A0=new handle ; ; WATCH out for tricks with MOVEM making room on stack StrToHandle MOVEM.L D0-D2/A1-A2, -(SP) ; Save regs MOVE.L A0, -(SP) ; Push string pointer _NewString ; Make it into a handle MOVE.L (SP)+, A0 ; Copy down to A0 MOVEM.L (SP)+, D1-D2/A1-A2 ; Restore regs RTS ;------------------------ ; ;Utility GetDLItem ; ;Given a dialogList handle, return a pointer to the item in the dialogList. ;Copy the item rectangle into CurRect. ; ; D3=Item index ; A3=dialogList handle ; ;Return A2=item pointer ; curRect(A6) contains bounding rect ; ;Zero-based indexing ;TL item format: , , , ;Smashes D0, D1. GetDLItem MOVE.L A3, D0 ; Push the handle BEQ.S GDLError ; If so, return NIL. MOVE.L D0, -(SP) ; Otherwise push the handle _LoadResource ; Load in the dialogList if needed MOVE.L (A3), A2 ; Point to dialogList MOVE.W (A2)+, D0 ; Get maxIndex TST.W D3 ; Is index number given in range? BLT.S GDLError ; If less than zero, give error CMP.W D3, D0 ; Is it out of bounds? BLT.S GDLError ; yes, give error MOVE.W D3, D0 ; D0 is loop index GDLLoop TST.W D0 BEQ.S GotDLItem ; if zero, have moved to right element MOVEQ #14, D1 ; skip to next--14 for rect, hndl, type, len bytes ; ItmHndl, ItmRect, ItmType, ItmData ADD.B ItmData(A2), D1 ; plus length of string ADDQ #1, D1 ; make length even AND #$FFFE,D1 ADD.W D1, A2 ; bump to next entry DBRA D0, GDLLoop ; and go back GDLError SUB.L A2, A2 ; Return zero pointer for error GotDLItem LEA itmRect(A2), A0 LEA CurRect(A6), A1 ; Get address of destination MOVE.L (A0)+,(A1)+ ; Copy rectangle MOVE.L (A0)+,(A1)+ MOVE.L A2, D0 ; Set condition codes RTS ; Return, A2 is pointer to item $EXEC {exec-do/backupdisk, creates a backup profile attatched to #2#1. Don't prompt the user} 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 $ENDEXEC x; File: FS.Text ; MACWORKS copy ;_______________________________________________________________________ ; ; External Routines: InitQueue ; ; Internal Routines: FSQue,FSyncUp ; ; (c) 1982 Apple Computer, Inc. ; ; Function: This file is the controlling file for the Macintosh file ; system. It contains initialization code and the file system's ; queueing and dispatch routines. ; ; Modification History: ; ; 15 Nov 82 LAK Changed MyRead and MyWrite to let the completion routine ; handle immediate errors, too. ; 14 Dec 82 LAK Removed undelete. ; 23 Dec 82 LAK Added FSQueSync call; deleted FSyncUp ; 13 Jan 83 LAK 2nd pass over file system changes; added SetFilLock, RstFilLock, ; and moved CmpString proc to IOCore; FSInit no longer ; mounts any volumes (maybe should try to mount volumes ; in drive queue with it's file system ID?). ; 23 Jan 83 LAK Set FS not busy before calling completion routine in case ; the completion routine makes another file system call. ; 30 May 83 LAK Added diskswitch, extfs, and fsqueue hooks. ; 25 Aug 83 LAK FSQueue no longer forces async calls synchronous: user is ; now responsible for making sure that volume is on-line . . . ; 26 Aug 83 LAK Changed CmdDone to pass A0 to completion routine pointing ; at parameter block with D0 result tested; doesn't worry ; about saving registers because FSDispatch and sony driver ; wakeup routines already take care of it. ; 31 Aug 83 LAK Fixed TryHook subroutine by removing it and calling hook ; routines via A1. ;_______________________________________________________________________ .NoList .INCLUDE tlasm-SysMacs.Text .INCLUDE tlasm-SysEqu.Text .INCLUDE tlasm-SysErr.Text .INCLUDE tlasm-ToolMacs.Text .INCLUDE TLAsm-SonyEqu.Text ; for status, control calls .INCLUDE mac/TLAsm-FSEqu.Text .List .PROC MacFS .REF EnQueue,DeQueue,MakeStkPB .DEF FileOpen,FileClose .DEF FileCreate,FileDelete,Rename,OpenRF .DEF FileRead,FileWrite,GetFPos,SetFPos,FileAlloc .DEF GetEOF,SetEOF,FlushFile .DEF GetFileInfo,SetFileInfo,SetFilType,RstFilLock,SetFilLock .DEF GetVolInfo,Eject,SetVol,GetVol,Offline,OKToOpen .DEF MountVol,UnMountVol,FlushVol,FInitQueue .DEF Gt1stMatch,GtNxtMatch,AdjEOF ;_______________________________________________________________________ ; ; Routine: FSQueSync ; Arguments: D1 = trap ; A0 = parameter list pointer ; Calls: calling routine ; Called by: File system routines which must be executed synchronously ; (MountVol,UnMountVol). ; ; Function: This entry point waits until the file system queue is ; empty, and then calls the calling routine, ensuring ; synchronous (with other file system calls) execution of ; the file system routine at user-level (memory manager calls ; are ok). The called routine calls CmdDon when through. ; ; Modification History: ; ; 02 Dec 82 LAK New today. ; ;_______________________________________________________________________ FSQueueSync BCLR #AsynTrpBit,D1 ; ensure async bit is not on ; fall thru to FSSync ;_______________________________________________________________________ ; ; Routine: FSQueue,CmdDone ; Arguments: D1 = trap ; A0 = parameter list pointer ; Calls: FSQue dispatches to the calling routine unless the call is ; asynchronous and the file system queue is not empty. Pascal ; register save conventions are observed. ; Called by: All file system routines when they may be executed asynchronously ; (i.e., they don't call the memory manager). ; ; Function: This entry point determines whether a call is synchronous ; or asynchronous; it queues up the call, then, if async and ; there is a command already executing, returns immediately. ; Otherwise, it enters the sync wait routine, then goes to ; command dispatch. ; ; All of these commands finish by branching to CmdDon; this ; routine calls any completion routine and dispatches the ; next queued command, if any . . . ; ; Modification History: ; ; 02 Dec 82 LAK New today. ; ; Do async calls have to be forced synchronous if the file system is not busy? ; Make sure interrupts are disabled at this time, though, so another request ; issued at interrupt time doesn't interfere. ;_______________________________________________________________________ FSQueue MOVE.L FSQueueHook,D2 ; queue hook? BLE.S @0 ; br if not . . . MOVE.L D2,A1 JSR (A1) ; D2 and A1 are expendable @0 MOVE.W #1,IOResult(A0) ; set IOResult to "in process" MOVE.W D1,IOTrap(A0) ; save the trap number MOVE.L (SP)+,IOCmdAddr(A0) ; save address to go to when ready MOVE.W #FSQType,IOType(A0) ; say its a file system queueing element BTST #AsynTrpBit,D1 ; async bit on? BEQ.S @1 ; br if not TST.B FrcSync ; force it synchronous? BEQ.S FSAsync ; if not, do it async BRA.S @2 ; BNE.S @2 ; br if so ; ; MOVEM.L D0-D1/A0,-(SP) ; save all regs ; LEA VCBQHdr,A0 ; search VCB queue for an off-line vol ; MOVEQ #VCBDrvNum,D1 ; (marked by a zero drive number) ; MOVEQ #0,D0 ; BSR QWordSearch ; MOVEM.L (SP)+, D0-D1/A0 ; BNE.S FSAsync ; if no off-line volumes, do it async ; BRA.S @2 ; otherwise, make it synchronous @1 CLR.L IOCompletion(A0) ; no completion routines for sync calls @2 MOVE.L A0,-(SP) ; save parameter block ptr BSR.S FSAsync ; queue it up (and maybe call it) MOVE.L (SP)+,A0 SyncWait MOVE.W IOResult(A0),D0 ; get the result code into D0 BGT.S SyncWait ; done when result is zero or negative CMP.W #VolOffLinErr,D0 ; volume off-line error? (detected by file BNE.S @1 ; system) br if not MOVE.L DskSwtchHook,-(SP) ; OffLineVol points to VCB, A0 to request BGT.S @1 ; br if there is a hook proc ADDQ #4,SP ; otherwise, just return @1 RTS ; return to caller FSAsync LEA FSQHdr,A1 ; get pointer to file system queue JSR Enqueue ; queue up the request MOVE SR,-(SP) ; only allow level 3 ORI #$0300,SR ; interrupts for debugging ToFSDispatch BSET #0,FSBusy ; is a request already executing? BEQ.S FSDispatch ; if not, just call it ; The file system is busy so we're done for now. We'll get to it when ; CmdDone is called. (maybe check here for async call with off-line vols, ; since only eject will force something off-line . . .) MOVEQ #0,D0 ; no errors (yet) RTE ; re-enable interrupts and return ; dispatch the next request (also called by CmdDon) FSDispatch MOVE (SP)+,SR ; re-enable interrupts MOVE.L FSQHead,A0 ; get first parameter block MOVE.L IOCmdAddr(A0),A1 ; get address to call MOVEM.L D3-D7/A2-A6,-(SP) ; observe Pascal regsave conventions JSR (A1) ; MOVEM.L (SP)+,D3-D7/A2-A6 ; restore registers RTS ; and return (all commands finish by ; jumping to CmdDone) CmdDone MOVE.L ExtFSHook,D2 ; queue hook? BLE.S @0 ; br if not . . . MOVE.L D2,A1 ; (external fs, debug) JSR (A1) ; leave CmdDone address on the stack @0 MOVE SR,-(SP) ; save interrupt state ORI #$0300,SR ; only debug interrupts allowed CLR.W FSBusy ; clear file system busy boolean ; delete the current request from the queue and post any completion routines MOVE.L FSQHead,A0 ; A0 -> current request CMP #FSQType,IOType(A0) ; it better be an I/O queue element BNE toDSFSErr ; or else die MOVE.L QLink(A0),FSQHead ; get next request, if any BNE.S CkCompletion ; branch if queue not empty CLR.L FSQTail ; clear tail ptr, too CkCompletion MOVE (SP)+,SR ; restore interrupt state MOVE.W D0,IOResult(A0) ; post error code MOVE.L IOCompletion(A0),D1 ; is there a completion routine? BEQ.S ChkForAnother ; skip if there's not MOVE.L D1,A1 ; get the completion routine address TST.W D0 ; test result code JSR (A1) ; call it ; check if there's anything else for this driver to do ChkForAnother MOVE SR,-(SP) ; save interrupt state ORI #$0300,SR ; only debug interrupts allowed TST.L FSQHead ; is a command pending? BNE.S ToFSDispatch ; if so, launch new command RTE ; restore rupt state and return ;_______________________________________________________________________ ; ; Routine: FInitQueue ; Arguments: none ; Calls: ; Function: Clear the queue of all pending commands except for the one ; running (if there is one . . .). ; ; Modification History: ; ; 08 Dec 82 LAK Modified for new queue routines. ; ;_______________________________________________________________________ FInitQueue MOVE SR,-(SP) ; save interrupt state ORI #$0300,SR ; only debug interrupts MOVE.L FSQHead,D0 ; any commands? BEQ.S @1 ; if not, we are done MOVE.L D0,A0 ; get pointer to the current routine CLR.L IOLink(A0) ; get rid of any others MOVE.L A0,FSQTail ; only this one @1 MOVEQ #0,D0 ; this routine has no errors RTE .Include source-FSVol.Text ; include volume level routines .Include source-FSDir1.Text ; include routines using filenames .Include source-FSDir2.Text ; .Include source-FSDir3.Text ; .Include source-FSRfN1.Text ; include routines using refnums .Include source-FSRfN2.Text ; .Include source-FSRfN3.Text ; .End ; FILE FSDir1.Text ; MACWORKS copy ;_______________________________________________________________________ ; ; External Routines: FileOpen,FileCreate ; ; Internal Routines: FndFilSpc,Gt1stFCB,GtNxtFCB,Gt1stMatch,GtNxtMatch ; ; Function: This file contains mostly volume-level routines. ; ; Modification History: ; 08 Dec 82 LAK Reworked all files for new file system data structures. ; 14 Jan 82 LAK Scan-thru changes prior to machine debug: added OpenRF, ; changed for physical length byte count in directory blocks, ; 07 Feb 83 LAK fixed create bug (incr.b of vcb num files -> incr.w) ; 23 Apr 83 LAK added minor change to open to simplify resources. ; 25 May 83 LAK Create no longer needs to incr A4 after FndFilNam. ; 12 Jul 83 LAK Fixed bug in open (see open). ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: Gt1stFCB,GtNxtFCB ; ; (c) 1982,1983, 1984 Apple Computer, Inc. ; ; Arguments: A1.L (input) -- FCB start pointer (GtNxtFCB) ; D1.W (input) -- offset to current FCB (GtNxtFCB) ; A1.L (output) -- unchanged ; D1.W (output) -- offset to next FCB ; CCR set so that BCS will not br when past last FCB ; Called By: FlushVolume,FileOpen,CkFileBusy,AdjEOF ; Function: Scan through the FCBs. ; ; Routine: Gt1stMatch,GtNxtMatch ; ; Arguments: A1.L (input) -- FCB start pointer (GtNxtFCB) ; D1.W (input) -- offset to current FCB (GtNxtFCB) ; A2.L (input) -- VCB pointer to match against ; D2.L (input) -- file number to match against ; A1.L (output) -- unchanged ; D1.W (output) -- offset to next FCB which matches ; CCR set so that BNE will not br when past last FCB ; Called By: FlushVolume,FileOpen,CkFileBusy,AdjEOF,ReName ; Function: Scan through the FCBs. ; ; Modification History: ; 13 Jan 83 LAK broke out from Open code. ;_______________________________________________________________________ Gt1stFCB MOVE.L FCBSPtr,A1 ; get FCB ptr MOVEQ #2,D1 ; index to first FCB RTS GtNxtFCB ADD.W #FCBEntLen,D1 ; go to next FCB CMP.W (A1),D1 ; reached the end yet? RTS ; let the caller test (BCS if not) Gt1stMatch BSR.S Gt1stFCB GNMLoop CMP.L FCBFlNm(A1,D1),D2 ; file numbers match? BNE.S GtNxtMatch CMP.L FCBVPtr(A1,D1),A2 ; on the same volume? BEQ.S GNMRTS GtNxtMatch BSR.S GtNxtFCB BCS.S GNMLoop ; loop thru them all MOVE #0,CCR ; clear Z bit GNMRTS RTS ; BEQ for match, BNE at end . . . ;_______________________________________________________________________ ; ; Routine: OpenRF ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block: uses ; IOFileName,IOPermssn,IOOwnBuf ; D0.W (output) -- 0 if file successfully opened, errcode otherwise ; This call may be executed asynchronously. ; Note that open does not use equates when transferring data from ; the directory entry to the FCB (to save code). ; Calls: FileOpen ; Function: Prepare a file for action. ;_______________________________________________________________________ OpenRF BSR FSQueue CLR.B RegRsrc ; open for resource fork BRA.S FOpen1 ; share code with FileOpen ;_______________________________________________________________________ ; ; Routine: FileOpen ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block: uses IODrvNum, ; IOFileType,IOFileName,IOPermssn,IOOwnBuf ; D0.W (output) -- 0 if file successfully opened, errcode otherwise ; This call may be executed asynchronously. ; Note that open does not use equates when transferring data from ; the directory entry to the FCB (to save code). ; Calls: FSQueue,Gt1stFCB,GtNxtFCB,FndFilNam,CmdDone ; Function: Prepare a file for action. ; ; ; Modification History: ; 02 Dec 82 LAK Got rid of alternate entry point Open2: just don't advertise ; as many FCB's so system level stuff can always get one. ; Removed the code for ALO's 20-Apr 'fix' to set up A1 (it's ; already set up by FndFil). Only put file num in FCB once ; (did it twice . . .). Now checks for write permission ; before opening a file for writing. ; 07 Dec 82 LAK Changed for new file system data structures. Added support ; for resource fork of the file. Own buffer is now passed by ; application. ; 21 Dec 82 LAK Added equates for resource bit in FCB; tag field support. ; Added Gt1stFCB and GtNxtFCB to save code; if opening file ; with write permission, makes sure it's not already open ; with write (checks for fork) ; 14 Jan 83 LAK Integrated with OpenRF; changes for phys length change in Dir. ; 11 Feb 83 LAK If Open fails, refnum field is invalidated for stronger ; checking. ; 20 Apr 83 LAK Changed to return refnum of fcb entry for files which ; are already open with write permissions. ; 02 Jun 83 LAK FCBBfAdr=0 now means use system buffer (adr will change on ; remounts). ; 08 Jun 83 LAK Fixed Open to return conflicting refnum on open error (was ; branching to wrong exit). ; 12 Jul 83 LAK Fixed check for file already open to also compare volume ; pointers. ; 29 Aug 83 LAK Changed to use Gt1stMatch, GtNxtMatch when searching for ; matching FCBs. ; ; The following sequences should suffice for file opening: ; ; Delete,Create,Open - when you want a fresh start ; Open - when you want to continue working with a file ; Create - when you want a new file which shouldn't be there ; ; - a file may be opened with write permission even if the volume is ; write protected; an error is not reported until a write, seteof, or ; allocate call for that file is actually made . . . this makes sense ; since the application which requests the permission does not want to ; change code depending upon the write-protect status of the volume (the ; user may insert it just to use the write functions . . .). ;_______________________________________________________________________ FileOpen BSR FSQueue ; queue up the call, wait for it's turn ST RegRsrc ; open regular part of file ; first, make sure we have an open FCB FOpen1 BSR.S Gt1stFCB ; get (A1,D1) pointing to first FCB @1 TST.L FCBFlNm(A1,D1) ; FCB unused BEQ.S OpnFCB ; br if so BSR.S GtNxtFCB ; get next one until we run out BCS.S @1 MOVEQ #TMFOErr,D0 ; too many files open OpnErrExit CLR.W IORefNum(A0) ; make refnum invalid OpnExit BRA CmdDone ; until we have looked at them all ; now, search the directory by filename for the file we want to open OpnFCB MOVE.W D1,IORefNum(A0) ; refnum is the FCB index! ADD.L A1,D1 ; save FCB ptr in D1 BSR FndFilNam ; go find the file. BNE.S OpnErrExit ; exit if not there, offline, ext fs MOVE.L D1,A3 ; get file's FCB ptr back. ; now get all the info we need for the FCB into regs MOVE.W BufTag(A1),D7 ; blk num the file was found in BCLR #15,D7 ; clear dirty bit MOVE.W (A5),D3 ; set up for FCB flags AND.W #$01FF,D3 ; clear rsrc, dirty bits MOVE.L IOOwnBuf(A0),D0 ; supplying a separate buffer? MOVE.L D0,A4 ; 0 means use system buffer BEQ.S @1 ; if not, use the communal one ADDQ.L #8,A4 ; incr past tag data fields CLR.W (A4)+ ; say the buffer has clean blk 0 in it @1 LEA FlFlNum(A5),A5 ; get past user-defined words in directory MOVE.L (A5)+,D2 ; file number MOVE.W (A5)+,D4 ; file start block (regular fork) MOVE.L (A5)+,D5 ; file logical length (regular fork) MOVE.L (A5)+,D6 ; file physical len (regular fork) ; now check whether we are opening resource or regular part of the file TST.B RegRsrc ; regular part open? BNE.S @2 ; br for regular part BSET #FCBFlgRBit,D3 ; mark this FCB a resource FCB MOVE.W (A5)+,D4 ; file start block (resource fork) MOVE.L (A5)+,D5 ; file logical length (resource fork) MOVE.L (A5)+,D6 ; file physical len (resource fork) @2 CMP.B #1,IOPermssn(A0) ; requesting read/only permission? BNE.S @3 ; br if not (check for write ok) BCLR #FCBFlgWBit,D3 ; set for no write, then BRA.S OKToOpen ; and continue @3 MOVEQ #PermErr,D0 ; assume permissions error BCHG #FCBFlgWBit,D3 ; write ok (if lock bit = 0, it's ok) BNE.S OpnErrExit ; exit if locked (if 0, -> 1=write ok) ; scan the FCB's to see whether this file is already open with write permission BSR.S Gt1stMatch ; find VCB ptr, FileNum match BNE.S OKToOpen ; br if none matched @4 MOVE.W FCBFlags(A1,D1),D0 ; if write perm and rsrc/reg are the same, EOR.W D3,D0 ; it's an error (2 files open with write AND.W #WrRsMask,D0 ; look at 2 bits BNE.S @5 ; br if no match MOVE.W D1,IORefNum(A0) ; return refnum of already opened entry MOVEQ #OpWrErr,D0 ; open with write perm already error BRA.S OpnExit @5 BSR.S GtNxtMatch ; go for the next one BEQ.S @4 ; loop until we've looked at them all ; everything is ok, so move file directory data into the FCB OKToOpen MOVE.L D2,(A3)+ ; file number MOVE.W D3,(A3)+ ; file flags (FCB undirty, res/reg, write) MOVE.W D4,(A3)+ ; start block MOVE.L D5,(A3)+ ; logical length MOVE.L D6,(A3)+ ; physical length CLR.L (A3)+ ; reset current file position to 0 MOVE.L A2,(A3)+ ; ptr to associated VCB. MOVE.L A4,(A3)+ ; addr of buffer MOVE.W D7,(A3) ; disk block directory entry is in MOVEQ #0,D0 ; no error. BRA.S CrExit ; shortcut to CmdDone ;_______________________________________________________________________ ; ; Routine: FileCreate ; Arguments: A0.L (input) -- pointer to I/O parameter block: uses ; IOFileName ; D0 (output) -- error code ; This call may be executed asynchronously. ; Calls: FSQueue,FndFilNam,CVFlgs,FndFilSpc,CmdDone ; Function: Create a new file . . . ; ; Modification History: ; ; 30-Nov-82 LAK removed file type determination; gets time from lomem var ; 01-Dec-82 LAK changed Scn4Spc call to start from beginning of directory, ; instead of where FndFil left off; included this proc in-line ; 08 Dec 82 LAK changed for new file system data structures. ; 29 Aug 83 LAK changed to specifically disallow file names of 0 length. ;_______________________________________________________________________ FileCreate BSR FSQueue ; queue up the call. BSR FndFilNam ; see if present BNE.S @1 ; br if not found MOVEQ #DupFNErr,D0 ; duplicate filename if found @1 CMP.W #FNFErr,D0 BEQ.S CrFile ; if not found, go ahead and create it CrExit BRA CmdDone ; that's all folks. ; file did not already exist. See FndFil to see what regs hold now. Basically: ; A2=ptr to correct VCB, A4=ptr to name, D2=name len CrFile MOVEQ #BdNamErr,D0 ; assume bad name TST.W D2 ; zero-length name? BEQ.S CrExit ; exit if so BSR CVFlgs ; check the volume flags for writability BNE.S CrExit ; (requires A2=VCB ptr) - br if an error BSR.S FndFilSpc ; find space in the directory BNE.S CrExit ; exit if directory full or disk error ; first make sure the entire entry is zeroed MOVE.W D5,D0 ; entry length MOVE.L A5,A1 ; pointer to entry @1 CLR.W (A1)+ ; entry lengths are always even SUBQ.W #2,D0 BHI.S @1 BSET #7,FlFlags(A5) ; in-use bit set MOVE.B IOFileType(A0),FlTyp(A5) ; file type field MOVE.L VCBNxtFNum(A2),FlFlNum(A5) ; use next file number ADDQ.L #1,VCBNxtFNum(A2) ; increment next file number field LEA FlCrDat(A5),A5 MOVE.L Time,(A5) ; created now MOVE.L (A5)+,(A5)+ ; also mod date ; now move in file name: D2 is name length, A4 points to name, (A5) ; points to destination for name . . . MOVE.B D2,(A5)+ BEQ.S @3 ; br if 0 (just in case) @2 MOVE.B (A4)+,(A5)+ ; move byte by byte SUBQ.B #1,D2 BHI.S @2 ; now update VCB and we're done. @3 ST VCBFlags(A2) ; mark volume info changed ADDQ.W #1,VCBNmFls(A2) ; incr VCB file count MOVEQ #0,D0 ; flawless BRA.S CrExit ; all done folks. ;_______________________________________________________________________ ; ; Routine: FndFilSpc ; Arguments: A2.L (input) -- VCB pointer ; D2.W (input) -- new directory entry name length ; D5.W (output) -- entry length ; A5.L (output) -- pointer to directory space ; D0.W (output) -- error code (directory full) ; Preserves: A0,A2,D2,A4 ; Clobbers: other regs ; Also marks this buffer dirty (anticipation) and invalidates the ; volume index,blk num fields ; This routine may return to one level above caller. ; Calls: Rd1stDB,RdNxtDB ; Called By: Create,Rename(via GetSpace) ; Function: Find space in the directory for a new file entry. This routine ; first checks for a directory block in the volume buffer and ; searches it first. If space enough is not found, the directory ; is scanned from the first block. A pointer to the space is ; returned and the next entry is marked empty (directory space ; is always at the end of a block). ; ; Modification History: ; 09 Dec 82 LAK New today. ; 16 Jan 82 LAK Modified for latest changes (no directory entry length byte). ; ; Note that FlNTLen+2 is the minimum size of a directory entry (an entry for ; a file with a 1-byte name). ; ;_______________________________________________________________________ FndFilSpc MOVE.L (SP)+,A3 ; preserve caller's address MOVEQ #FlNTLen+2,D5 ; entry len, no name + length byte +1 ADD.W D2,D5 ; add name length (excluding length byte) BCLR #0,D5 ; make it an even value BSR Rd1stDB ; special case first read BRA.S ScnNxB1 ScnNxB BSR RdNxtDB ; get next dir blk ScnNxB1 BNE.S FndFSExit ; exit on bad reads or dir full ScnSp2 MOVEQ #0,D0 ; init the index @1 TST.B FlFlags(A5,D0) ; free space? BEQ.S BlkOpnng ; br if so BSR.S GtNxEntry ; point (A5,D0) to next entry BCS.S @1 BlkOpnng MOVE.W D0,D4 ; now see if new entry will fit this block ADD.W D5,D4 ; compute new end of entries in the block CMP.W #BufSiz,D4 ; did it fit? BCC.S ScnNxB ; if not, go scan the next block BSET #BufModBit,BufTag(A5) ; mark this block dirty (will soon be) LEA 0(A5,D0.W),A5 ; point right to the entry CLR.L VCBDirIndex(A2) ; invalidate our index,blk num pair MOVEQ #0,D0 ; success FndFSExit JMP (A3) ; return to caller ; routine shared by FndFilSpc, FndFilNam, GetFileInfo, and FClose ; trashes D6, (A5,D0) point to next entry, BCS works if there is more room GtNxEntry MOVEQ #0,D6 MOVE.B FlNam(A5,D0),D6 ; get name length ADD.W D6,D0 ; update to point at next entry ADD.W #FlNTLen+2,D0 ; full entry length including name BCLR #0,D0 ; make sure have an even value CMP.W #,D0 ; only scan thru the block RTS ; BCS branches if not at end ; FILE: FSDir2.Text ; MACWORKS Copy ;_______________________________________________________________________ ; ; External Routines: FileDelete,ReName ; ; Internal Routines: CkFileBusy,RemovEntry ; ; Function: This file contains file-level routines which use filenames. ; ; Modification History: ; 16 Jan 83 LAK Added latest changes; cosmetic and documentation updates. ; 08 Feb 83 LAK Fixed rename bug (A5 trash) ; 20 Apr 83 LAK Minor change to CkFileBusy. ; 29 Aug 83 LAK Changed ReName to update affected FCBs FCBFlPos (rename ; may cause a directory entry to migrate). ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: CkFileBusy ; Arguments: A0.L (input) -- IO parameter block ptr ; A2.L (input) -- VCB pointer ; A5.L (input) -- file directory entry pointer ; D0.W (output) -- result code (0 if file not open) ; D1.W (output) -- FCB index if file busy ; D2.L (output) -- file number ; All other registers are preserved. ; Calls: Gt1stFCB,GtNxtFCB ; Called by: FileDelete,GetFileInfo ; Function: Searches FCB buffer to see if the file directory entry ; pointed to by A5 is already open. ; ; Modification History: ; 10 Dec 82 LAK New today. ; 16 Jan 83 LAK Now calls procs to search thru FCBs. ; 20 Apr 83 LAK Changed to return refnum of entry found when file is ; found to be busy already. ; 29 Aug 83 LAK Changed to use Gt1stMatch instead of Gt1stFCB, GtNxtFCB. ; Now returns file number in D2 (no one uses it tho). ;_______________________________________________________________________ CkFileBusy MOVE.L A1,-(SP) ; preserve A1 MOVE.L FlFlNum(A5),D2 ; get the file number ; scan through the FCBs and look for a match of file number and same volume BSR Gt1stMatch BEQ.S @2 ; fail if any match MOVEQ #0,D0 @1 MOVE.L (SP)+,A1 ; restore A1 RTS @2 MOVE.W D1,IORefnum(A0) ; note the refnum of fcb entry MOVEQ #FBsyErr,D0 ; file is busy BRA.S @1 ;_______________________________________________________________________ ; ; Routine: FileDelete ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block; uses: ; IOFileName,IOFileType ; D0.W (output) -- error code ; This call may be executed asynchronously. ; Calls: FSQueue,DtrmVol,CVFlgs,FndFilNam,CkFileBusy,DAlBlks,RemovEntry, ; CmdDone ; Function: Delete a file. The file is removed from ; the directory and its blocks are freed up (both resource and ; normal fork blocks are deallocated). The entry is zeroed and ; any following entries are moved down. All blocks associated ; with the file are marked free in the block map and the VCB ; info is marked dirty. ; ; Modification History: ; 09 Dec 82 LAK Redid for new file data structures. ; 10 Dec 82 LAK Broke out routine to search FCBs by filenum (share it ; with GetFileInfo). ; 14 Dec 82 LAK Removed undelete scheme. ; 16 Jan 83 LAK Modified RemovEntry for new directory format (no entry ; length); updated documentation. ; 04 Jun 83 LAK No longer calls DtrmVol (this is done by FndFilNam) since ; case of write-protected diskette is a low-percentage hit. ; Uses CkFilMod subroutine to save code. ;_______________________________________________________________________ FileDelete BSR FSQueue ; queue it up BSR CkFilMod ; find it and check if we can mod it ; now see if file is locked or is busy. MOVEQ #FLckdErr,D0 ; assume locked BTST #FlWrtFlag,FlFlags(A5) ; well, is it? BNE.S FDelExit ; br if so BSR.S CkFileBusy ; is the file busy? BNE.S FDelExit ; exit if so . . . SUBQ.W #1,VCBNmFls(A2) ; one less file ST VCBFlags(A2) ; mark VCB info dirty MOVE.W FlStBlk(A5),D3 ; get start block to D3 BSR DAlBlks ; deallocate the blocks MOVE.W FlRStBlk(A5),D3 ; deallocate the resource blocks also BSR DAlBlks BSR.S RemovEntry MOVEQ #0,D0 ; success FDelExit BRA CmdDone GtEntryLen MOVEQ #0,D0 MOVE.B FlNam(A5),D0 ADD.W #FlNTLen+2,D0 BCLR #0,D0 RTS ; delete file pointed to by A5 in block at A1 by moving all following entries ; down and zeroing the rest of the block (shared with Rename) RemovEntry MOVEM.L A0/A1/A5/D0,-(SP) BSET #BufModBit,BufTag(A1) ; mark this buffer dirty CLR.L VCBDirIndex(A2) ; invalidate our index,blk num pair MOVE.L A5,A0 ; start of entry to delete BSR.S GtEntryLen ; length of entry -> D0 ADD.L D0,A5 ; next entry ADD.W #BufSiz,A1 ; point to end of buffer @1 CMP.L A1,A5 ; check for end of buffer BCC.S @2 ; br if done MOVE.W (A5)+,(A0)+ ; move rest of entries down BRA.S @1 @2 CLR.W (A0)+ ; leave rest of block zeroed CMP.L A1,A0 BCS.S @2 MOVEM.L (SP)+,A0/A1/A5/D0 RTS ;_______________________________________________________________________ ; ; Routine: ReName ; Arguments: A0 (input) -- pointer to parameter block: ; D0 (output) -- error code ; Calls: FSQueue,DtrmVol,FndFilNam,CVFlgs,RemovEntry,FndFilSpc, ; GtEntryLen,CmdDone ; Function: Rename a file or a volume. For files, the directory is ; first searched for the target name (to make sure we don't ; get two files with the same name). If not found, the directory ; is then searched for the real file (using the source name); ; the entry is copied onto the stack and removed from the directory ; block. FndFilSpc is used to find space for the changed entry. ; ; Also note that a file may not be renamed with the same name. ; ; Modification History: ; 09 Dec 82 LAK Changed to use a new rename algorithm; updated for new ; file system data structures. ; 16 Jan 83 LAK Updated for final data structures. ; 05 Jun 83 LAK Added change to allow rename of a file to itself (for case ; changes). ; 29 Aug 83 LAK Fixed bug: rename now updates FCBs for the renamed file (opened ; files may be renamed) to point to correct directory block. ; ; Test: no more room in directory case . . . ; renaming a volume ; renaming both file and volume with 0-length name. ;_______________________________________________________________________ ; first a short routine to call FndFilSpc for us (note that FndFilSpc marks ; the buffer dirty for us . . .) GetSpace BSR.S @2 @1 MOVE.W ErCode,D0 MOVE.L FSTemp4,A5 MOVE.L A5,A3 ; also return ptr in A3 BGT.S @1 RTS @2 MOVE.W #1,ErCode ; set semaphore busy BSR FndFilSpc ; and go find us space MOVE.W D0,ErCode ; clr semaphore with error code MOVE.L A5,FSTemp4 ; and return A5 result RTS ; and return to wherever (need a clean ; stack before continuing) ReName BSR FSQueue ; what a queue command! MOVE.L IONewName(A0),D2 ; check out the new name BSR DtrmV2 ; find what volume the new name is on BNE RNmVol ; br if error (may want to rename vol) TST.W D2 ; check name length BEQ RNmVol1 ; if zero, may be volume-conflict rename MOVE.L A2,A1 ; save volume for the destination name MOVE.W D3,D7 ; save vol specified indication MOVEM.L D2/A4,FSTemp8 ; save dest name ptr and length BSR DtrmVol ; det vol source name is on BNE.S RNmExit ; exit if we can't determine volume CMP.L A1,A2 ; are the names on the same volume? BEQ.S RNmOK ; if so, life is beautiful TST.W D7 ; otherwise, if dest didn't specify volume, BEQ.S RNmOK ; it's ok (just use source's volume) BadNewNam MOVEQ #BdNamErr,D0 ; names are on different vols RNmExit BRA CmdDone ; that's all folks. RNmOK BSR CVFlgs ; is the volume locked/write protected? BNE.S RNmExit ; exit if so MOVE.L A0,-(SP) ; preserve parameter block pointer MOVE.W D2,D0 ; old name length MOVE.L A4,A0 ; old name ptr MOVEM.L FSTemp8,D2/A4 ; get back new name ptr and length SWAP D0 MOVE.W D2,D0 ; new name length MOVE.L A4,A1 ; new name ptr _CmpString ; see if it's a case of the same name MOVE.L (SP)+,A0 BEQ.S @2 ; br if so (skip the conflict check) BSR FndFN2 ; look for the dest name in directory BNE.S @1 ; (uses src volume) MOVEQ #DupFNErr,D0 ; duplicate file name if found @1 CMP #FNFErr,D0 ; file should not have been found BNE.S RNmExit ; also exit for ext fs, vol offline errs @2 BSR FndFilNam ; search directory for the source file BNE.S RNmExit ; if not around, bummers ; At this point, A5 points to the file entry, and A1 points to the start of the ; buffer the directory block is in. We save the old entry on the stack. SavOldNtry MOVE.L SP,A4 ; save our stack pointer MOVE.L A5,A3 ; copy pointer to entry BSR GtEntryLen ; entry length -> D0 @1 MOVE.W -2(A3,D0),-(SP) ; move it onto stack, backwards SUBQ #2,D0 BGT.S @1 ; now reclaim the space for the old entry and mark the block dirty BSR.S RemovEntry ; ok, we have stuff on the stack: FndFilSpc may return before it's done, so ; call it one level up MOVE.L FSTemp8,D2 ; get back dest name length BSR.S GetSpace BNE.S NoSpace ; ok, we have the space, so move in the new entry: ; A2.L = VCB ptr ; D2.W = new dir entry name length ; D5.W = entry total length ; A4.L = old stack pointer ; A5.L = pointer to space to put entry MOVEQ #-1,D0 ; move this much of the old entry @3 MOVE.W (SP)+,(A5)+ ; move word by word DBRA D0,@3 MOVE.L A4,SP ; get our old stack pointer back MOVEM.L FSTemp8,D2/A4 ; retrieve dest name ptr and length MOVE.B D2,(A5)+ ; name length @4 MOVE.B (A4)+,(A5)+ ; move byte by byte, including len byte SUBQ.W #1,D2 BGT.S @4 BSR.S AdjFlPos ; adjust file position for any FCBs RNmExOK MOVEQ #0,D0 ; no errors RNmEx1 BRA.S RNmExit ; the directory didn't have enough space, so at least try to get back to ; where we started . . . NoSpace MOVE.B FlNam(SP),D2 ; get back src name length BSR.S GetSpace ; we should be able to get back at least BEQ.S @2 MOVE.L A4,SP ; get our old stack pointer back MOVEQ #FSDSErr,D0 ; some days are like this BRA.S RNmEx1 @2 MOVE.W (SP)+,(A5)+ ; move word by word CMP.L SP,A4 ; get back to where we started BNE.S @2 BSR.S AdjFlPos MOVEQ #DirFulErr,D0 ; directory was too full BRA.S RNmEx1 ; so exit . . . AdjFlPos MOVE.L FlFlNum(A3),D2 ; file number of this file BSR GetCurBlk ; get block number in D0 (trashes A5) BSR Gt1stMatch ; update any matching files BNE.S @2 ; exit if none @1 MOVE.W D0,FCBFlPos(A1,D1) ; update file block BSR GtNxtMatch BEQ.S @1 @2 RTS ; User is trying to rename a volume. Give it a try . . . RNmVol CMP.W #NSVErr,D0 ; make sure it's no-such-vol error BNE.S RNmEx1 TST.W D2 ; name length should be zero BNE.S BadNewNam RNmVol1 TST.W D3 ; volume name should be non-zero BEQ.S RNmEx1 ; br if nil BSR DtrmVol ; check source volume BNE.S RNmEx1 ; exit if no such volume BSR CVFlgs ; see if can do. BNE.S RNmEx1 ; br if no modification allowed TST.W VCBDrvNum(A2) ; make sure it's on-line, too BEQ.S RNmEx1 ; exit if not . . . ST VCBFlags(A2) ; mark VCB info changed LEA VCBVN(A2),A2 ; point to name destination MOVE.L IONewName(A0),A1 ; get new name pointer MOVE.B (A1)+,D0 ; get length of new name SUBQ.B #1,D0 ; decrease length by colon CMP.B #VCBMaxNam,D0 ; trim it to max size BLS.S @1 MOVEQ #VCBMaxNam,D0 @1 MOVE.B D0,(A2)+ ; move in modified length @2 MOVE.B (A1)+,(A2)+ ; move it in byte by byte SUBQ.B #1,D0 ; including name length byte, minus colon BHI.S @2 BRA.S RNmExOK ; we're done and ok ; File: FSDir3.Text ;_______________________________________________________________________ ; ; External Routines: GetFileInfo,SetFileInfo ; ; Internal Routines: CkFilMod,FndFil,Rd1stDB,RdNxtDB,GetCurB1,ExtOffLinCk ; ; Function: This file contains routines using file names to specify file. ; ; Modification History: ; 13 Dec 82 LAK Reworked all files for new file system data structures. ; 14 Dec 82 LAK Undelete removal changes ; 12 Jan 82 LAK Made string compare proc an OS call; code is in IOCore. ; 16 Jan 83 LAK Final pass changes . . . added SetFilLock,RstFilLock,SetFilType. ; 25 May 83 LAK Adapted FndFilNam to new CmpString interface. ; 06 Jun 83 LAK Added changes to support ext fs, offline volumes. ; 31 Aug 83 LAK Changed GetFileInfo to return current file info for opened ; files (from FCB information). ; 01 Sep 83 LAK Changed BPL to BCC in file name transfer to support file ; names > 127 chars in GetFileInfo. ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: SetFilType ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block, uses ; IOFileName,IODrvNum,IOFileType,IONewType ; D0.W (output) -- 0 if file was found and set correctly. ; Calls: FndFilNam ; Function: Set the directory type field for a file. The directory is first ; searched for a file with the same name and new type fields to ; avoid naming conflicts. ; ; Modification History: ; 16 Jan 83 LAK New today. ; 06 Jun 83 LAK Changed to use CkFilMod subroutine. ;_______________________________________________________________________ SetFilType BSR FSQueue ; wait our turn MOVE.B IOFileType(A0),D1 ; save original type MOVE.B IONewType(A0),IOFileType(A0) ; set for destination type BSR FndFilNam ; does it exist already? BNE.S @1 ; br on error (incl not fnd) MOVEQ #DupFNErr,D0 ; duplicate file name if found @1 MOVE.B D1,IOFileType(A0) ; (restore original state) CMP.W #FNFErr,D0 ; file should not have been found BNE.S SRFLXit3 ; br if not ok (also ext fs, offline errs) BSR.S CkFilMod ; look for file and see if we can mod it ; (doesn't return on errors) MOVE.B IONewType(A0),FlTyp(A5) ; ring in the new BRA SRFLXit1 ; exit, marking buffer modified ;_______________________________________________________________________ ; ; Routine: SetFilLock,RstFilLock ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block, uses ; IOFileName,IOFileType,IODrvNum ; D0.W (output) -- 0 if file was found and set correctly. ; Calls: FndFilNam ; Function: Set or reset the file lock bit. ; ; Modification History: ; 16 Jan 83 LAK New today. ; 06 Jun 83 LAK Broke out CkFilMod as a subroutine. ; ; Note: if the file is locked after it has already been opened with write ; permission, the lock will not affect that opened file. If the lock bit ; is not changed by this command, the directory buffer is not marked dirty ; (allows quick lock/unlocking of a group of file, regardless of their current ; status). ;_______________________________________________________________________ SetFilLock BSR FSQueue ; wait our turn ST FLckUnlck ; want to lock it BRA.S SRFLck RstFilLock BSR FSQueue CLR.B FLckUnlck SRFLck BSR.S CkFilMod ; look for file and see if we can mod it ; (doesn't return on errors) TST.B FLckUnlck BEQ.S @1 ; br if we want to unlock it BSET #FlWrtFlag,FlFlags(A5) ; lock it BNE.S SRFLXit2 ; br if already locked BRA.S SRFLXit1 ; otherwise, it's been modified @1 BCLR #FlWrtFlag,FlFlags(A5) ; unlock it BEQ.S SRFLXit2 ; br if already unlocked SRFLXit1 BSET #BufModBit,BufTag(A1) ; buffer modified (also setfilinfo exit) SRFLXit2 MOVEQ #0,D0 SRFLXit3 BRA CmdDone ;_______________________________________________________________________ ; ; Routine: SetFileInfo ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to File Info I/O parameter block, uses ; IOFileName,IOFlAttrib,IOFlUsrWds ; D0.W (output) -- 0 if entry was found, error code otherwise ; File's entry is modified to reflect changes ; Calls: FndFilNam ; Function: Sets the user-definable fields in a file's directory entry. ; The caller specifies the filename and type, and the eight ; user-defined words. The file lock bit does not inhibit this ; command. ; ; Modification History: ; 13 Dec 82 LAK Modified for new file system data structures. ; 17 Jan 83 LAK Latest changes: no longer used to change lock, type fields; ; there are now 16 user-defined bytes. ; 06 Jun 83 LAK Adapted to new offline, ext fs problems. Uses CkFilMod ; and SRFLXit1 to save code. ;_______________________________________________________________________ SetFileInfo BSR FSQueue BSR.S CkFilMod ; look for modifiable file ; (doesn't return on errors) MOVEM.L IOFlUsrWds(A0),D1-D4 ; get 16 user words MOVEM.L D1-D4,FlUsrWds(A5) ; transfer 16 user-defined bytes BRA.S SRFLXit1 ; exit ok, marking buffer modified ; short code-saving routine called by Delete, SetFilType, SRFLck, SetFileInfo CkFilMod MOVE.L (SP)+,FSTemp4 ; strip the stack (FndFilNam is async) BSR.S FndFilNam ; find the file in the directory BNE.S SRFLXit3 ; br if error (also ext fs, offline vols) BSR CVFlgs ; see if we can change it BNE.S SRFLXit3 ; br if locked or write-protected MOVE.L FSTemp4,A3 JMP (A3) ; go back to caller upon success ;_______________________________________________________________________ ; ; Routine: Rd1stDB,RdNxtDB ; Arguments: A2.L (input) -- VCB ptr ; D0.W (output) -- error code ; A5.L (output) -- ptr to buffer ; Clobbers: D4,A6 ; Preserves: D1,D2,D5,D6,D7,A0,A1,A2,A3,A4 ; others: see MyReadDB ; Calls: MyReadDB,GetCurBlk ; Called By: FndFilNam,FndFilSpc ; Function: These two routines are used when scanning through a volume ; directory for a filename or for space. Rd1stDB must be ; called for the first block as it sets up certain lomem ; variables (CkdDirBlk,MaxDB,NxtDB). RdNxtDB is called for ; remaining blocks (if any). ; ; MyReadDB is branched to; the only error these routines ; may return is directory full error, when all blocks have been ; scanned (when searching directory by name, this should be ; turned into a file not found error). ; ; If there is already a directory block in the volume buffer, ; that block is returned by Rd1stDB; after that, the directory ; is scanned from the start, skipping the checked block, if any. ;_______________________________________________________________________ Rd1stDB MOVE.W VCBBlLn(A2),D4 ; len in blocks of dir MOVE.W VCBDirSt(A2),D3 ; starting block of dir on disk ADD.W D3,D4 ; make D4=ptr to last dir blk+1 BSR.S GetCurBlk CMP.W D3,D0 ; less than starting block? BCS.S @1 CMP.W D4,D0 ; if less than this, it's a dir blk BCS.S @2 @1 MOVEQ #0,D0 ; failure @2 MOVE.W D3,NxtDB ; next directory block to check is 1st one MOVE.W D4,MaxDB ; max DB block + 1 MOVE.W D0,CkdDB ; mark the checked directory block BEQ.S RdNxtDB ; if not a DB, go read the first one MOVEQ #0,D0 ; no errors RTS ; otherwise, return this one . . . ; A2=VCB ptr ; CkdDirBlk=checked blk, 0 if none ; MaxDB=last DB+1 ; NxtDB=next DB to check RdNxtDB MOVE.W NxtDB,D3 ; next block to read ADDQ.W #1,NxtDB ; and increment this field CMP.W MaxDB,D3 ; past the last block? BCC.S @1 ; then say directory is full? CMP.W CkdDB,D3 ; going after the one we checked? BEQ.S RdNxtDB ; skip it if so and go again BRA MyReadDB ; read next dir block @1 MOVEQ #DirFulErr,D0 ; the directory may be full RTS ; Routine to return current block number in D0, buffer address in A5 GetCurBlk MOVE.L VCBBufAdr(A2),A5 ; get buffer address GetCurB1 MOVE.W BufTag(A5),D0 ; get block number AND.W #$7FFF,D0 ; less the dirty bit RTS ;_______________________________________________________________________ ; ; Routine: FndFilNam,FndFN2 ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block, uses: ; IOFileName,IOFileType,IODrvNum ; D0.W (output) -- 0 if file was found, error code otherwise. ; D2.W (output) -- name length excluding any volume prefix ; A1.L (output) -- points to volume buffer (if name found) ; A2.L (output) -- pointer to appropriate VCB ; A4.L (output) -- pointer to input file name. If a volume was ; specified will point to the colon, thereby ; always pointing one byte ahead of the true ; file name. ; A5.L (output) -- pointer to file directory entry in buffer. ; Preserves: D1,D5,A0 ; Clobbers: all other registers (D3,D4,D6,D7,A3,A6) ; Note that the routine will return to one level above ; caller during the asynchronous reads. ; FndFN2 entry point for Rename (alternate volume may be used) ; Calls: DtrmVol,MyReadDB(via Rd1stDB and RdNxtDB) ; Called By: Create,Delete,FileOpen,Rename,GetFileInfo,SetFileInfo ; Function: Locate a filename within the directory on a diskette ; Search algorithm: if dir blk there, search it first; ; if not found, start from beginning, skipping searched blk. ; ; Modification History: ; 08 Dec 82 LAK Added FndFilNam entry point. ; 13 Dec 82 LAK Rewrote. Removed index search mode. ; 14 Dec 82 LAK Removed check for delete file. ; 17 Jan 83 LAK Changes for last data structure mods: now checks for both ; filename and filetype match. ; 05 Jun 83 LAK Added checks for offline volume, external file system vols ;_______________________________________________________________________ FndFilNam BSR DtrmVol ; determine the volume to be used BEQ.S FndFN2 ; (set up A4,D2,A2) FndFNRTS RTS ; exit on vol errs FndFN2 BSR.S ExtOffLinCk ; make sure it's online, internal fs BNE.S FndFNRTS ; exit if not MOVE.L (SP)+,A3 ; get the caller's addr MOVE.W VCBNmFls(A2),D7 ; number of files on this volume BEQ.S FNFound ; if none, can't find this one. BSR.S Rd1stDB ; go for the first one FFNLoop BEQ.S Scn4Nam ; br if ok CMP.W #DirFulErr,D0 ; reached end of directory? BNE.S FFNExit ; then file not found FNFound MOVEQ #FNFErr,D0 ; file not found! BRA.S FFNExit ; have a block at (A5) - look for the file in this dir block Scn4Nam MOVEQ #0,D0 ; clear for index MOVE.L A4,A1 ; requested string @1 MOVE.W D0,D4 ; save D0 TST.B FlFlags(A5,D4) ; flags=0 means end of entries this blk BEQ.S @3 ; br if no more entries this block MOVE.B FlTyp(A5,D4),D6 ; check for type match CMP.B IOFileType(A0),D6 ; BNE.S @2 ; br if no match MOVE.L A0,-(SP) ; preserve A0 LEA FlNam(A5,D4),A0 ; directory string MOVEQ #0,D0 MOVE.B (A0)+,D0 ; first string length SWAP D0 MOVE.W D2,D0 ; second string length _CmpString ; do they match? MOVE.L (SP)+,A0 BEQ.S @4 ; br if so @2 SUBQ.W #1,D7 ; decr # files we checked BEQ.S FNFound ; exit if we checked them all . . . MOVE.W D4,D0 ; restore D0 BSR GtNxEntry ; (A5,D0) point to next entry, D6 trashed BCS.S @1 ; br if not finished with this block @3 BSR.S RdNxtDB ; read next directory block BRA.S FFNLoop ; we have our man . . . @4 MOVE.L A5,A1 ; A1 = buffer pointer LEA 0(A5,D4),A5 ; A5 = entry ptr, D0 = 0 here FFNExit JMP (A3) ; short routine to save code (used by GetFilInfo, FndFilNam) ExtOffLinCk BSR CkExtFS ; ext file system volume? BNE.S @1 ; br if so TST.W VCBDrvNum(A2) ; volume offline? BNE.S @1 ; br if not MOVEQ #VolOffLinErr,D0 ; otherwise, note the error @1 TST.W D0 RTS ;_______________________________________________________________________ ; ; Routine: GetFileInfo ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to File Info I/O parameter block, uses ; all fields? ; D0.W (output) -- 0 if info was found, error code otherwise ; Calls: FndFilNam(if searching by name) ; Function: Get information about a specific file from the file directory. ; User can specify search by either filename or by index, ; where the index specifies the file's offset from the beginning ; of the directory. ; ; An index of zero means search by filename and type. ; ; Modification History: ; 10 Dec 82 LAK Modified for new file system data structures. ; 13 Dec 82 LAK Removed delete file support. ; 17 Jan 83 LAK Another passover. ; 06 Jun 83 LAK Added changes to detect offline volumes, ext fs calls. ; 01 Sep 83 LAK Changed BPL to BCC in file name transfer to support file ; names > 127 chars. ;_______________________________________________________________________ GetFileInfo BSR FSQueue MOVE.W IOFDirIndex(A0),D1 ; get file index, if there is one BNE.S GFIByIndx ; br if searching by index BSR.S FndFilNam ; search by name BNE.S GFIExit ; exit if not found, offline, ext fs BRA.S XferFInfo ; br if ok GFIByIndx BSR DtrmV1 ; figure out which volume by drvnum/vrefnum BNE.S GFIExit ; br if vol spec err BSR.S ExtOffLinCk ; make sure it's on-line, internal fs BNE.S GFIExit CMP.W VCBNmFls(A2),D1 ; requesting an index larger than exists? BHI.S GFINFErr ; return file not found error? ; we are searching by index here: it's a bit tricky since we want to try ; to continue where we left off, if possible . . . MOVE.W VCBBlLn(A2),D5 ; len in blocks of dir MOVE.W VCBDirSt(A2),D3 ; assume start from the beginning ADD.W D3,D5 ; max blk num + 1 MOVE.W VCBDirIndex(A2),D7 ; useful file index, blk number pair BEQ.S @0 ; br if set back to zero CMP.W D7,D1 ; is our index >= this? BCS.S @0 ; br if not . . . MOVE.W VCBDirBlk(A2),D3 ; block number for this index BRA.S @1 @0 MOVEQ #1,D7 ; index of first file this block ; at this point, D7=index of first file in current block ; D3=current block ; D5=max blk number + 1 ; D1=requested index @1 CMP.W D5,D3 ; have we reached max yet? BCC.S GFINFErr ; br if so MOVE.W D3,VCBDirBlk(A2) ; form index,block pair to help next MOVE.W D7,VCBDirIndex(A2) ; GetFileInfo indexed search BSR MyReadDB ; get the block BNE.S GFINFErr MOVEQ #0,D0 ; index into block @2 MOVEQ #0,D6 TST.B FlFlags(A5,D0) ; valid entry? BEQ.S @3 ; br if no more entries CMP.W D7,D1 ; is it the requested one? BEQ.S GotIndFile ADDQ.W #1,D7 ; increment file index BSR GtNxEntry BCS.S @2 ; br if haven't reached end of block @3 ADDQ.W #1,D3 ; try next block BRA.S @1 GFINFErr MOVEQ #FNFErr,D0 ; file not found . . . hmmmm GFIExit BRA CmdDone GotIndFile LEA 0(A5,D0),A5 ; get pointer to entry . . . ; searched by index, so return the name . . . LEA FlNam(A5),A3 ; file entry name ptr MOVE.L IOFileName(A0),D0 ; parameter block filename ptr BEQ.S XferFInfo ; skip it if it's zero MOVE.L D0,A4 MOVE.B (A3),D0 ; name length @1 MOVE.B (A3)+,(A4)+ ; name length, then name, byte by byte SUBQ.B #1,D0 BCC.S @1 XferFInfo LEA IOFlAttrib(A0),A4 ; info destination MOVEQ #/2-1,D0 ; number of words to copy - 1 LEA FlFlags(A5),A3 ; start of regular info to transfer @1 MOVE.W (A3)+,(A4)+ ; move byte by byte DBRA D0,@1 BSR CkFileBusy ; see if file is currently open BNE.S @3 ; br if so BCLR #7,IOFlAttrib(A0) ; note that it's not busy @2 MOVEQ #0,D0 ; no errors BRA.S GFIExit ; the file is opened, so we update the return information appropriately in case ; the file has been modified . . . @3 LEA IOFlStBlk(A0),A3 ; info destination, regular fork MOVE.L FCBSPtr,A1 ; CkFileBusy does not return this . . . BSR UpDatFlNtry ; update info (shared with close) BSR GtNxtMatch ; any more opened entries (in case of BEQ.S @3 ; both forks open) - loop if so BRA.S @2 ; otherwise, we're through ; File: FSRfN1.Text ;_______________________________________________________________________ ; ; External Routines: FileRead,FileWrite,SetFPos,GetFPos ; ; Internal Routines: MyWrite,Seek,CVFlgs,MyReadBk,MyReadDB,MyReadIP ; ; Function: This file contains routines using refnums. ; ; Modification History: ; 14 Dec 82 LAK Rewrote for new file system data structures. ; 17 Jan 83 LAK Latest changes for final structures. Brought SetFPos and ; GetFPos here from FSRFN2. Made GetFPos and SetFPos call ; FileRead with zero bytes requested. ; 9 Feb 83 LAK Fixed MyWriteIP bug (now invalidates file's buffer if ; it happens to write that block . . .) ; 10 Feb 83 LAK Fixed bugs in Read with EOL character. ; 02 Jun 83 LAK Changes for FCBBfAdr=0 meaning use volume buffer. ; 11 Aug 83 LAK Added support for a read-verify mode. ; 15 Aug 83 LAK Flushes file buffer now if block is dirty and falls ; within read-in-place . . . saved some bytes, too. ; 30 Aug 83 LAK Removed redundant set of FCB dirty bit in FileWrite (gets ; set by AdjEOF). ; 02 Aug 83 LAK Fixed read bug (read count 0 with position past EOF). ; Changed file positioning mode 2 (relative to end of file) ; by defining IOPosOffset to be either positive or ; negative . . . ; 20 Mar 85 KWK Changed MyRd2 routine to do synchronous read and fall ; through to the former completion routine. Fixes blown ; stack w/synchronous drivers. ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: Seek ; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IOPosMode, ; IOPosOffset ; (A1,D1) (input) -- FCB ptr for the file in question ; D0.W (output) -- error code ; D2.W (output) -- distance past EOF (0 or - if not past EOF) ; D5.L (output) -- current file position ; D7.L (output) -- set to IOReqCount ; IOActCount cleared ; trashes D4 ; Called By: FileRead,FileWrite ; Function: Seek is a utility routine that interprets the positioning ; parameters in an read or write call, updating the current ; file position in the FCB. ; ; There are 4 positioning modes: ; ; mode 0 -> no positioning ; mode 1 -> relative to beginning (absolute) ; mode 2 -> relative to end of file ; mode 3 -> relative to current ; ; If the file positioning would cause the current position to ; become negative, it is not changed and an error ; (PosErr) is returned; otherwise, result code 0 ; returned. Read and Write must check for positioning past ; current EOF since they handle that case differently (read ; reports an error, write extends the file). This routine will ; clip the position to the end-of-file but returns a remainder ; in D2. ; ; The IOReqCount field must be positive; IOPosOffset may be ; positive or negative . . . ; ; Modification History: ; 14 Dec 82 LAK Rewrote to reflect new file system data structures. Changed ; to handle positioning past end-of-file differently, reports ; error when positioning to before file start. ; 15 Aug 83 LAK Now trashes D4 and passes back file position in D5. ; ; USoft memo Aug 10: A read with position past end of file should return an ; error; a write should allocate space on the disk up to the specified ; position. Read and write should return the current file position in the ; in the parameter block after reads and writes regardless of positioning mode. ; ;_______________________________________________________________________ Seek CLR.L IOActCount(A0) ; haven't actually done any bytes yet. MOVEQ #ParamErr,D0 ; assume bad count MOVE.L IOReqCount(A0),D7 ; # bytes to read/write BMI.S SkExitRTS ; exit with error if negative MOVE.L IOPosOffset(A0),D2 ; position value the user supplied MOVE.L FCBCrPs(A1,D1),D5 ; current file position MOVE.L FCBEOF(A1,D1),D4 ; get the current EOF MOVE.B IOPosMode+1(A0),D0 ; the position mode is in bits 0,1 ROXR.B #2,D0 ; get position bits in carry, sign BCC.S PM01 ; br if 0 or 1 BMI.S PM3 ; br if 3 ; relative to the end of the file (mode 2) MOVE.L D4,D5 ; current EOF ; relative to current position (mode 3) PM3 ADD.L D2,D5 BRA.S SkExit PM01 BPL.S SkExit ; br if no position operation (mode 0) ; relative to start (mode 1) MOVE.L D2,D5 ; relative to start is absolute SkExit BPL.S @1 ; did D5 go negative? br if not MOVEQ #PosErr,D0 ; report positioning error BRA.S SkExitRTS ; and don't change current position @1 MOVE.L D5,D2 ; new position SUB.L D4,D2 ; distance past end of file (pos-EOF) BLE.S @2 ; br if not past the end MOVE.L D4,D5 ; pin to EOF @2 MOVE.L D5,FCBCrPs(A1,D1) ; set the new current position MOVEQ #0,D0 ; no error SkExitRTS RTS ;_______________________________________________________________________ ; ; Routine: GetFPos ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block; uses: ; IORefNum,IOPosOffset ; D0.W (output) -- 0 if file was found, error code otherwise. ; Calls: ; Function: Get current file position for specified opened file ; ; Modification History: ; 10 Dec 82 LAK Modified for new file system data structures. ; 17 Jan 83 LAK Modified to call FileRead via SetFPos to do the work. ; ;_______________________________________________________________________ GetFPos CLR.B IOPosMode+1(A0) ; no file positioning ; Fall thru to SetFPos ;_______________________________________________________________________ ; ; Routine: SetFPos ; ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to I/O parameter block; uses: ; IORefNum,IOPosOffset ; D0.W (output) -- 0 if file was found, error code otherwise. ; Calls: ; Function: Set current file position for specified opened file. Will ; not set the position past end of file. Uses positioning modes ; like FileRead. ; ; Modification History: ; 10 Dec 82 LAK New Today. ; 17 Jan 83 LAK Modified to call fileread. ; ;_______________________________________________________________________ SetFPos CLR.L IOReqCount(A0) ; no bytes ; fall thru to FileRead ;_______________________________________________________________________ ; ; Routine: FileRead ; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IORefnum, ; IOBuffer,IOReqCount,IOActCount,IOPosMode,IOPosOffset ; D0.W (output) -- error code ; Calls: FSQueue,RFnCall,Seek,Lg2Phys,MyRead,CmdDone ; Function: General purpose file read call. ; ; Modification History: ; 15 Dec 82 LAK Rewrote to reflect new file system data structures; ; Separated reading with EOL mode from regular reading (now ; checks for EOL character whenever specified). ; ; IOPosMode(A0).W = Newline char in bits 8-15, newline flag (1=enabled) in ; bit 7, bits 0-1 are position information ; ; ; - if positions to EOF and 0 bytes are requested, EOF is not reported. ; (reading exactly to EOF will not raise EOF condition unless byte count ; is not satisfied because EOF hit) ;_______________________________________________________________________ FileRead BSR FSQueue ; wait our turn BSR RFnCall ; map the reference number BNE FRdExit1 ; exit on errors (don't set IOPosOffset) ; (also exit for offline, ext fs calls) BSR.S Seek ; do any positioning - D5 gets cur pos, ; IOActCount cleared, D7 set to ; IOReqCount BNE FRdExit0 ; exit if positioned to before file start ; figure min(requested count,distance to EOF) NEG.L D2 ; EOF-current pos (from seek) BMI FRdEOFXit ; if negative, report end of file CMP.L D2,D7 ; number of bytes desired within EOF? BLE.S @1 ; br if so MOVE.L D2,D7 ; just read distance to EOF @1 TST.L D7 ; byte count 0? BEQ.S FRdOK ; all done if none to read! MOVE.W IOPosMode(A0),D2 ; get newline stuff LSR.W #8,D2 ; newline char in low byte, enb in carry BCS FRNewLine ; if newline enabled, do it differently MOVE.W D5,D2 ; current file position (low word) AND.W #$01FF,D2 ; check lower 9 bits for blk bndry read BEQ.S RDMain ; br if so (see if we can read into place) ; must read a funny amount of data (not a full block's worth) RdPart BSR MyReadBk ; read the data (asynchronously) BNE.S FRdExit ; exit on errors ; the data block is at (A5). Move it into user buffer byte by byte. BTST #6,IOPosMode+1(A0) ; read verify only? SNE D0 ; $00 if not, $FF if so BSR DoPart ; share code with similar write code BNE.S FRdExit ; exit on verify errors ; read multiple blocks into place in user's buffer RdMain MOVE.L D7,D4 ; number of bytes we want. BEQ.S FRdOK ; all done if no more bytes. CMP.L #512,D4 ; must want at least 512 bytes BCS.S RdPart ; br if not . . . AND.W #$FE00,D4 ; only read whole blocks BSR Lg2Phys ; convert to physical stuff...sets up D6 MOVE.W D3,D4 ; preserve D3 BSR CkFilBuf ; get file buffer block in D3 BPL.S @1 ; br if not dirty CMP.W D4,D3 ; is buffer block < read start block BCS.S @1 ; br if so (ok) CMP.W D0,D3 ; is buffer block >= last read block +1 ? BCC.S @1 ; br if so ; at this point, we know we have a dirty buffer block right in the middle of ; the block of data we wish to read - so we simply flush that buffer . . . MOVE.L D6,FSTemp4 ; preserve initial result BSR MyWrite ; flush block before reading BNE.S FRdExit ; exit on disk error MOVE.L FSTemp4,D6 ; back step a bit @1 MOVE.W D4,D3 ; recover D3 MOVE.L IOBuffer(A0),A5 ; user buffer address ADD.L IOActCount(A0),A5 ; buffer start + count so far = target BSR MyReadIP ; read in place BNE.S FRdExit ; disk errors deserve an exit BSR UpdteCntPos ; update D7, D5, and IOActCount(A0) BRA.S RdMain ; loop. FRdOK MOVEQ #0,D0 ; no errors (except maybe EOF) FRdExit TST D0 ; other error? BNE.S FRdExit0 ; br if so MOVE.L IOActCount(A0),D2 ; if actual count is not equal to CMP.L IOReqCount(A0),D2 ; requested count, and no other errors, BEQ.S FRdExit0 ; (br if not true) FRdEOFXit MOVEQ #EOFErr,D0 ; then we must have hit end of file . . . FRdExit0 MOVE.L D5,FCBCrPs(A1,D1) ; update current position in FCB MOVE.L D5,IOPosOffset(A0) ; always return current pos FRdExit1 BRA CmdDone ; bye now . . . ; reading while looking for end of line characters . . . do it a block at a time FRNewLine BSR MyReadBk ; read the data (asynchronously) BNE.S FRdExit ; exit on errors ; the data block is at (A5). Move it into user buffer byte by byte. MOVE.L D5,D2 ; current file position AND.W #$01FF,D2 ; get the index MOVE.L IOBuffer(A0),A3 ; user buffer MOVE.L IOActCount(A0),D4 ; where to start continuing read @1 MOVE.B 0(A5,D2.W),D0 ; get byte from the buffer MOVE.B D0,0(A3,D4.L) ; put it into user's area ADDQ.L #1,IOActCount(A0) ; actually read another byte! ADDQ.L #1,D4 ; incr destination byte offset ADDQ.W #1,D2 ; incr source byte offset ADDQ.L #1,D5 ; incr current position CMP.B IOPosMode(A0),D0 ; is it the EOL character? BNE.S @2 MOVEQ #0,D0 ; no error, we found EOL! BRA.S FRdExit0 @2 SUBQ.L #1,D7 ; decr byte count BEQ.S FRdOK ; exit, checking for EOF condition CMP.W D6,D2 ; reached end of buffer? BCS.S @1 ; loop if not BRA.S FRNewLine ; get another block ;_______________________________________________________________________ ; ; Routine: MyReadBk,MyReadDB,MyReadIP ; Arguments: A2.L (input) -- VCB ptr ; D3.W (input) -- physical block to read ; D0.W (output) -- error code ; D6.L (output) -- const 512 (byte count) ; A5.L (output) -- buffer pointer ; Clobbers: A6 ; All other registers are preserved ; Alternate entry MyReadIP supplies A5=buf adr, D6=byte count ; This routine returns to one level above caller during the ; read(caller must not have anything on the stack when ; issuing the call!) ; Called By: FileRead(MyReadBk,MyReadIP),FileWrite(MyReadBk), ; Function: General purpose disk driver block read call. Read a block ; of data asynchronously and return to caller with registers ; restored after read. If the buffer contains the requested ; block, this routin returns immediately; if the buffer has been ; modified, it is written out before another block is read in. ; ; Note that buffers are 522 bytes long; the pointer to the buffer points ; to where the data belongs, whereas the word at -2(A5) contains the ; block in the buffer, and bit 15 set indicates that this data should ; be written out. The longword at -10(A5) is used for the filenumber ; field, the word at -6(A5) for the file block number, and the word at ; -4(A5) for the file tag field in the disk tag field. ; ; ; Modification History: ; 15 Dec 82 LAK Rewrote to reflect new file system data structures; ; Now used just to read single data blocks to a buffer. ; 16 Dec 82 LAK Added code to read in place for FileRead. ; 17 Dec 82 LAK Added support for file tags. ; 17 Jan 83 LAK Uses drive number instead of block offset for call now. ; 06 Jun 83 LAK Now changes all disk io errors to IOErr. ; ; - check that param blk is not in use before using it? ; - check that file block number of file block read in matches tag data? ;_______________________________________________________________________ ; code-saving routine used by MyReadBk, MyWriteIP, FileClose GetFilBuf MOVE.L FCBBfAdr(A1,D1),-(SP); check file's buffer for a valid block BNE.S @1 MOVE.L VCBBufAdr(A2),(SP) ; use volume buffer if FCBBfAdr=0 @1 MOVE.L (SP)+,A5 ; file buffer RTS MyReadBk MOVEQ #1,D4 ; only get one block BSR Lg2Phys ; sets D3=disk block BSR.S GetFilBuf ; set A5 to file buffer BRA.S MyRead ; don't branch here . . . MyReadDB MOVE.L VCBBufAdr(A2),A5 ; always use volume buffer . . . MyRead MOVEQ #2,D6 ; bytes to r/w (always 1 block) ASL.L #8,D6 ; (cheap way to make BufSiz) MOVE.L (SP)+,A6 ; save caller's addr BSR MyRWSub0 ; save regs, start filling r/w param blk BSR GetCurB1 ; -2(A5) -> D0, 0 -> bit15(D0) CMP.W D0,D3 ; is it the block we are after? BNE.S MyRd0 ; br if not MOVEQ #0,D0 ; no error MyRWExit BSR.S GetRegs ; get back regs MyRWEx1 TST.W D0 ; any errors? BEQ.S @1 ; br if not MOVEQ #IOErr,D0 ; transform it to generic IO error @1 JMP (A6) ; return to caller with error code MyRd0 BCLR #BufModBit,BufTag(A5) ; is the buffer dirty? BEQ.S MyRd2 ; br if not . . . MOVE.W D0,D3 ; write this buffer out LEA MyRd1,A1 ; completion routine BSR.S MyRWSub MOVEM.L -10(A5),D0-D1 ; preserve the file tags MOVEM.L D0-D1,TagData+2 ; (filenumber,fileflags,fileblock) _Write ,ASYNC ; issue a write call RTS MyRd1 BSR.S GetRegs ; get back regs BNE.S MyRWEx1 ; exit on errors MyRd2 LEA Params,A0 ; fixed file system I/O param block LEA MyRd3,A1 ; completion routine BSR.S MyRWSub ; set up IOCompletion, IOPosOffset ; _Read ,ASYNC ; read the new block ; RTS _Read ; read in new block SYNCHRONOUSLY (kwk) MyRd3 BSR.S GetRegs ; get back regs BEQ.S @1 ; br if read was good MOVEQ #0,D3 ; say nothing is in the buffer @1 MOVE.W D3,-2(A5) ; note the block in the buffer MOVE.L TagData+2,-10(A5) ; tag data goes into the buffer, too MOVE.L TagData+6,-6(A5) ; (filenumber,fileflags,fileblock) BRA.S MyRWEx1 ; and exit MyReadIP MOVE.L (SP)+,A6 ; save caller's addr MOVE.B IOPosMode+1(A0),D0 ; save verify mode bit BSR.S MyRWSub0 ; save regs, start filling r/w param blk LSL.B #1,D0 ; verify mode? BPL.S @1 ; br if not BSET #6,IOPosMode+1(A0) ; pass it on to the disk driver if so @1 LEA MyRWExit,A1 ; completion routine is just the exit BSR.S MyRWSub ; fill in comp routine, start disk blk _Read ,ASYNC ; read blocks into place RTS ; short pieces of common code . . . GetRegs MOVEM.L RgSvArea,D1-D7/A0-A6 ; get back regs RTS MyRWSub0 MOVEM.L D1-D7/A0-A6,RgSvArea ; save all the regs too LEA Params,A0 ; fixed file system I/O param block MOVE.W VCBDRefNum(A2),IORefNum(A0) ; driver refnum MOVE.L A5,IOBuffer(A0) ; buffer address MOVE.L D6,IOReqCount(A0) ; block size MOVE.W #1,IOPosMode(A0) ; position mode 1 (from disk start) RTS MyRWSub MOVE.L A1,IOCompletion(A0) MOVEQ #0,D6 ; clear high bytes MOVE.W D3,D6 ; convert from block to byte address ASL.L #1,D6 ASL.L #8,D6 ; mult by 512 . . . MOVE.L D6,IOPosOffset(A0) ; absolute address MOVE.W VCBDrvNum(A2),IODrvNum(A0) ; tell it which drive we want RTS ;_______________________________________________________________________ ; ; Routine: MyWriteDB,MyWrite,MyWriteIP ; Arguments: A2.L (input) -- VCB ptr ; D3.W (input) -- physical disk block to write to ; D0.W (output) -- error code ; D6.L (output) -- const 512 (byte count) ; A5.L (output) -- buffer pointer ; Clobbers: A6 ; All other registers are preserved ; ; MyWriteIP ; (A1,D1) (input) -- FCB ptr ; D5.L (input) -- current file pos ; D6.L (input) -- write byte count ; A5.L (input) -- write buffer ; ; This routine returns to one level above caller during the ; write (caller must not have anything on the stack when ; issuing the call!) ; Called By: FileWrite,FlushVol,etc ; Function: Write some data asynchronously and return to caller after ; write is done with registers restored. ; ; Note that buffers are 522 bytes long; the pointer to the buffer points ; to where the data belongs, whereas the word at -2(A5) contains the ; block in the buffer, and bit 15 set indicates that this data should ; be written out. The longword at -10(A5) is used for the filenumber ; field, the word at -6(A5) for the file block number, and the word at ; -4(A5) for the file tag field in the disk tag field. ; ; ; Modification History: ; 17 Dec 82 LAK Rewrote to reflect new file system data structures; ; Unique entrypoints to write single data blocks from ; a buffer. Added support for file tags. ; 9 Feb 83 LAK Added code to invalidate file's block buffer if the ; corresponding physical block is being written by ; MyWriteIP. ; 06 Jun 83 LAK Now changes all disk io errors to IOErr. ;_______________________________________________________________________ MyWriteDB MOVE.L VCBBufAdr(A2),A5 ; always use volume buffer . . . MyWrite MOVEQ #2,D6 ; bytes to r/w (always 1 block) ASL.L #8,D6 ; (cheap way to make BufSiz) MOVE.L (SP)+,A6 ; save caller's addr BSR.S MyRWSub0 ; save regs, start filling r/w param blk MOVEM.L BufFNum(A5),D0-D1 ; preserve the file tags MOVEM.L D0-D1,TagData+2 ; (filenumber,fileflags,fileblock) LEA MyWr1,A1 ; completion routine MyWr0 BSR.S MyRWSub ; set up IOCompletion, IOPosOffset _Write ,ASYNC ; write out the block RTS MyWr1 BSR.S GetRegs ; get back regs MOVE.W D3,BufTag(A5) ; block in the buffer is no longer dirty BRA MyRWEx1 MyWriteIP MOVE.L (SP)+,A6 ; save caller's addr BSR.S MyRWSub0 ; save regs, start filling r/w param blk MOVE.L FCBFlNm(A1,D1),TagData+2 ; filenumber for tag MOVE.W FCBFlags(A1,D1),TagData+6 ; file flags for tag MOVE.L D5,D0 ; current file position LSR.L #8,D0 ; form current file block LSR.L #1,D0 MOVE.W D0,TagData+8 ; start file block for tag BSR GetFilBuf ; file buffer ptr -> A5 BSR GetCurB1 ; -2(A5) -> D0, 0 -> bit15(D0) CMP.W D3,D0 ; buffer blk less than start write blk? BCS.S @1 ; br if so . . . LSR.L #8,D6 ; figure number of blocks we are writing LSR.L #1,D6 ADD.W D3,D6 ; last block we are writing + 1 CMP.W D6,D0 ; buffer blk greater than last blk? BCC.S @1 ; br if so CLR.W BufTag(A5) ; otherwise, invalidate it (we write it) @1 LEA MyRWExit,A1 ; completion routine is just the exit BRA.S MyWr0 ; share some code ;_______________________________________________________________________ ; ; Routine: FileWrite ; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IORefnum, ; IOBuffer,IOReqCount,IOActCount,IOPosMode,IOPosOffset ; D0.W (output) -- error code ; Calls: FSQueue,TstMod,RFnCall,Seek,Lg2Phys,MyWriteIP,MyWriteDB,CmdDone ; Function: Write user data to a file, extending the file as necessary. ; ; Modification History: ; 17 Dec 82 LAK Rewrote to reflect new file system data structures; ; When file is initially positioned past end-of-file, the ; file is automatically extended enough for the write. ; ;_______________________________________________________________________ FileWrite BSR FSQueue ; don't do the call until time. BSR TstMod ; test if we may modify this file. BNE FWrExit1 ; br if not. BSR Seek ; share this routine with read - D5 gets ; cur file position, IOActCount cleared, ; D7 set to IOReqCount BNE.S @2 ; br if tried to position before start ; (to FWrExit0) TST.L D2 ; positioned past EOF? BMI.S @1 ; br if not ADD.L D2,D5 ; extend added to cur pos ; see if file has to be made physically longer @1 MOVE.L D5,D4 ; current position after positioning ADD.L D7,D4 ; end byte for write + 1 CMP.L FCBEOF(A1,D1),D4 ; will it fit within the old EOF? BLS.S SameLen ; if so, leave file at same phys length SUB.L FCBPLen(A1,D1),D4 ; is it past the phys end? BLS.S @3 ; no, just reset the eof ; ran off the physical end of the file--need to allocate more blocks ; Call alloc with D4=number of extra bytes we need, A2=addr of VCB, ; (A1,D1) ptr to FCB . . . BSR Alloc ; do it to it (sets FCB PEOF, start blk) @2 BNE.S FWrExit0 ; exit if didn't get it (return with ; cur file pos = EOF) ; now just update the logical len. @3 MOVE.L D5,D4 ; current position after positioning ADD.L D7,D4 ; plus # bytes to write MOVE.L D4,FCBEOF(A1,D1) ; is new logical end-of-file ; the data will fit into the file. At this point: ; A0=ptr to params, ; (A1,D1) = ptr to file's FCB ; A2 = ptr to VCB ; D7 = # bytes to write total ; D5 = current file position. SameLen MOVE.W D5,D0 ; check if writing to a block boundary AND.W #$01FF,D0 ; if so, low 9 bits will be 0 BEQ.S WrMain ; if so, try writing full blocks out ; must write some funny amount of data WrPart BSR MyReadBk ; read the block into buffer FCB dictates BNE.S FWrExit ; exit on errors ;jwp Patch code for WrPart from MSFileFix 1/14/84 MOVE.L FCBFlNm(A1,D1),BufFNum(A5) ; fill in appropriate buffer tag info MOVE.W FCBFlags(A1,D1),BufFlags(A5) ; in case this is a new file block MOVE.L D5,D0 ; file byte position LSR.L #8,D0 LSR.L #1,D0 ; file block number MOVE.W D0,BufFBlk(A5) ;jwp ; the destination data block is at (A5). Move it out of user buffer byte by byte. MOVEQ #1,D0 ; D0=1 for write BSR.S DoPart ; share code with fileread BSET #BufModBit,BufTag(A5) ; block in the buffer is now dirty ; This section tries to write out entire blocks and/or groups of blocks WrMain MOVE.L D7,D4 ; number bytes we want. BEQ.S FWrOK ; all done if no more bytes. CMP.L #512,D4 ; must want at least 512 bytes BCS.S WrPart ; br if not . . . AND.W #$FE00,D4 ; only write whole blocks BSR Lg2Phys ; convert to physical stuff...sets up D6 MOVE.L IOBuffer(A0),A5 ; user buffer address ADD.L IOActCount(A0),A5 ; buffer start + count so far = write data BSR MyWriteIP ; write straight from user buffer BNE.S FWrExit ; disk errors deserve an exit BSR.S UpdteCntPos BRA.S WrMain ; loop. FWrOK MOVEQ #0,D0 ; no errors FWrExit BSR AdjEOF ; adjust other FCBs for this file MOVE.L D5,FCBCrPs(A1,D1) ; update current position in FCB FWrExit0 MOVE.L FCBCrPs(A1,D1),IOPosOffset(A0) ; always return current pos FWrExit1 BRA CmdDone ; command is done. UpdteCntPos SUB.L D6,D7 ; adjust for number of bytes read ADD.L D6,IOActCount(A0) ; actually read this many more ADD.L D6,D5 ; update current position RTS ; code sharing routine . . . D6 = 512 on entry DoPart MOVE.L D5,D2 ; current file position AND.W #$01FF,D2 ; get the index SUB.W D2,D6 ; bytes in blk from cur pos CMP.L D6,D7 ; more than we want? BCC.S @1 MOVE.L D7,D6 ; if so, only r/w num requested @1 MOVE.L IOBuffer(A0),A3 ; user buffer ADD.L IOActCount(A0),A3 ; where to start continuing read/write BSR.S UpdteCntPos MOVEM.L A0-A1,-(SP) ; preserve blockmove registers MOVE.L A3,A0 ; src for writes MOVE.L A5,A1 ADD D2,A1 ; dest for writes TST.B D0 ; read or write? BGT.S @2 ; br for write BMI.S @5 ; br for read verify EXG A0,A1 ; switch src, dest for reads @2 MOVE.L D6,D0 ; byte count _BlockMove @3 MOVEQ #0,D0 ; no errors @4 MOVEM.L (SP)+,A0-A1 ; restore regs RTS @5 MOVEQ #IOErr,D0 ; assume error @6 CMPM.B (A0)+,(A1)+ ; compare next byte BNE.S @4 ; exit on errors SUBQ.W #1,D6 ; decr byte count BNE.S @6 ; loop if not done BRA.S @3 ; and take the good route if done ; File: FSRfn2.Text ; MACWORKS Copy ;_______________________________________________________________________ ; ; External Routines: FileClose,FlushFile,SetEOF,GetEOF,FileAlloc ; ; Internal Routines: RfNCall,AdjEOF,TstMod ; ; Function: This file contains routines which operate on open files using ; refnums. ; ; Modification History: ; 08 Dec 82 LAK Reworked all files for new file system data structures. ; Changed refnum validity check in RFnCall. ; 17 Jan 83 LAK Last pass changes. ; 06 Mar 83 LAK Fixed bug in SetEOF with IOLEOF = 0 (now deallocates ; the disk blocks . . .) ; 30 Aug 83 LAK Fixed bug in ADJEOF (A1) -> (A0) ; 31 Aug 83 LAK Subroutined part of FClose to share with GetFileInfo fix. ; 7-Sep 84 KWK Added Sidhu's patch for SetEOF bug with shortening file ; when blocks to be de-allocated are still in file buffer. ; ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: FlushFile ; Arguments: A0.L (input) -- I/O parameter block: uses ; IORefnum: file to be flushed ; D0.W (output) -- error code ; Calls: RfNCall,FClose ; ; Function: Flushes the buffers associated with a file and writes any ; changed FCB info out to the disk directory. ; ; Modification History: ; 07 Dec 82 LAK Changed to support mutiple VCB queue; only flushes files - ; FlushVol is used to flush a volume. ; 21 Dec 82 LAK Changed to share code with fileclose . . . ; ; - the volume block map is not flushed; should it be? ;_______________________________________________________________________ FlushFile BSR FSQueue ST FlushOnly ; only flush BRA.S FlCls1 ;_______________________________________________________________________ ; ; Routine: FileClose ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to parameter block, uses: ; IORefNum = refnum of file to close ; D0.W (output) -- 0 if file closed ok, errcode otherwise ; This call may be executed asynchronously. ; Calls: FSQueue,RFnCall,FClose,CmdDone ; Function: Remove a file from active duty. ; ; ; Modification History: ; ; 02 Dec 82 LAK A3 and A5 set up by FClose, so removed a line. ; Uses DisposBuf to get rid of any allocated buffer. ; 20 Dec 82 LAK Modified for new file system data structures; no longer ; returns buffer memory (buffer supplied by caller at open time); ; checks for resource/regular part of a file. ; 06 Jun 83 LAK Made Flush, Close ok for offline files. ; ; Sharable routines: routine to search a directory block for a file number. ;_______________________________________________________________________ FileClose BSR FSQueue ; wait until ready... CLR.B FlushOnly ; flush and close the file FlCls1 BSR RFnCall ; detect if file open, get FCB pointer BEQ.S @1 ; br if ok CMP.W #VolOffLinErr,D0 ; was it just off-line? exit for ext fs here BNE.S ClsExit ; (close is ok for offline files cause ; they've been flushed) @1 BSR.S FClose ; actually do flush and close now ClsExit BRA CmdDone ; that's all folks. ;_______________________________________________________________________ ; ; Routine: FClose ; ; Arguments: Enter with (A1,D1) pointing at the FCB. ; FlushOnly (input) -- low memory flag: if 1, then flush only, ; do not close . . . ; A0,A1,D1 preserved ; Clobbers all other registers? ; Calls: MyWriteBk,MyReadBk ; Called by: FileClose,FlushVolume,FlushFile ; Function: Called when file system wants to close or flush a file. ; Returns to one level above caller during execution. ; ; Modification History: ; ; 02 Dec 82 LAK Set up A2,A3 and A5 early to save bytes; reworked format. ; Uses Time global for modification date. ; 20 Dec 82 LAK Modified for latest file system changes . . . no longer ; writes directory block out, but marks it dirty . . . ; 21 Dec 82 LAK Uses low memory variable FlushOnly for flush-only flag (if ; zero, then close file, too) ; 17 Jan 83 LAK Modified for new directory entry data structure (no entry ; length byte, physical length in bytes) ;_______________________________________________________________________ ; first a short routine shared with FileRead to check for a dirty buffer block, ; returning block number in D3. BPL branches is block wasn't dirty . . . CkFilBuf BSR GetFilBuf ; file's buffer ptr -> A5 MOVE.W BufTag(A5),D3 ; is buffer dirty? set N bit accordingly BCLR #15,D3 ; clearing dirty bit gives us blk number RTS FClose MOVE.L (SP)+,A4 ; save caller's addr BCLR #FCBModBit,FCBMdRByt(A1,D1) ; in case the file is unmodified, BEQ.S FClose1 ; we need not flush anything (no ; write to the file has been done) ; if file's own buffer is dirty, write it out . . . BSR.S CkFilBuf ; is buffer dirty? BPL.S @1 ; br if not BSR MyWrite BNE.S FClsDn ; when disk errors occur, get out @1 MOVE.W FCBFlPos(A1,D1),D3 ; file block number put here at open BSR MyReadDB ; read in the block to the volume's buffer BNE.S FClsDn ; this could be a real problem for user. ; now find file entry within this dir block. MOVEQ #0,D0 ; init index (should be zero anyway) MOVE.L FCBFlNm(A1,D1.W),D3 ; look for file with same file num @2 CMP.L FlFlNum(A5,D0.W),D3 ; this file? BEQ.S GtFlCl ; yes, got it to close it. BSR GtNxEntry BCS.S @2 ; ; if file disappears... MOVEQ #FNFErr,D0 BRA.S FClsDn ; exit if we can't find it . . . GtFlCl LEA FlStBlk(A5,D0),A3 ; destination for regular fork BSR.S UpDatFlNtry ; code saving routine (trashes A3,D3) MOVE.L Time,FlMdDat(A5,D0) ; use time global var for mod time BSET #BufModBit,BufTag(A5) ; mark this buffer dirty . . . ; see if we are only flushing or if the file should be closed . . . FClose1 TST.B FlushOnly ; should we close this file? BNE.S FClsOK ; br if flush only . . . LEA 0(A1,D1),A3 ; point to FCB entry MOVEQ #-1,D0 ; number of words to zero - 1 @1 CLR.W (A3)+ ; first longword zero actually marks it DBRA D0,@1 ; but zero it all for aesthetics FClsOK MOVEQ #0,D0 ; no errors FClsDn JMP (A4) ; go to caller with CCR reflecting D0 ; code sharing routine also used by GetFileInfo . . . A3 is reg fork destination UpDatFlNtry MOVE.L A4,-(SP) ; preserve A4 LEA FCBMdRByt(A1,D1),A4 MOVE.W (A4)+,D3 ; FCBMdRByt LSL.W #7,D3 ; get FCBRscBit into carry BCC.S @1 ; br if not the resource fork ADD #10,A3 ; destination for rsrc fork is 10 later @1 MOVE.W (A4)+,(A3)+ ; file start block (FCBSBlk) MOVE.L (A4)+,(A3)+ ; file logical length (FCBEOF) MOVE.L (A4)+,(A3)+ ; file physical length (FCBPLen) MOVE.L (SP)+,A4 ; restore A4 RTS ;_______________________________________________________________________ ; ; Routine: GetEOF ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to parameter block, uses: ; IORefNum,IOLEOF ; D0.W (output) -- 0 if file open, errcode otherwise ; This call may be executed asynchronously. ; Calls: FSQueue,RfNCall,CmdDone ; Function: Get logical length information for an opened file. ; ; Modification History: ; ; 21 Dec 82 LAK Modified for new file system data structures. ; 17 Jan 83 LAK No longer returns physical length information. ;_______________________________________________________________________ GetEOF BSR FSQueue BSR.S RfNCall ; is refnum for a valid open file? BNE.S @1 ; br if not . . . (D0=0 if so) MOVE.L FCBEOF(A1,D1),IOLEOF(A0) ; return the logical EOF @1 BRA CmdDone ;_______________________________________________________________________ ; ; Routine: RfNCall ; ; Arguments: A0.L (input) -- pointer to parameter block ; D0.W (output) -- file not open, bad refnum errs ; D1.W (output) -- offset from (A1) to FCB requested ; (i.e., (A1,D1) points at requested FCB) ; D2.L (output) -- file number ; A1.L (output) -- pointer to FCB buffer start ; A2.L (output) -- VCB pointer ; Status flags set according to D0. ; All other registers are preserved. ; Called by: FileClose,FileRead,GetEOF,TstMod ; Function: Get the refnum from the param list and return a pointer to ; the FCB. ; ; Modification History: ; ; 02 Dec 82 LAK Reworked format. ; 08 Dec 82 LAK Changed refnum validity check. Modified for new FCB structure. ; Status flags set according to D0. ; 20 Dec 82 LAK Changed to return VCB pointer in A2 if all is cool. ; 06 Jun 83 LAK Changed to call ExtOffLinCk (support for offline, ext fs vols) ;_______________________________________________________________________ RfNCall MOVE.L FCBSPtr,A1 ; get ptr to FCBs CLR.L D1 ; clear D1 high word MOVE.W IORefNum(A0),D1 ; get refnum MOVE.L D1,D2 ; and copy MOVEQ #RFNumErr,D0 ; in case of bad refnum DIVU #FCBEntLen,D2 ; see if it's a valid refnum SWAP D2 ; the remainder should be 2 SUBQ.W #2,D2 BNE.S @1 ; if not, error . . . CMP.W (A1),D1 ; is refnum too large? BCC.S @1 ; br if so. MOVEQ #FNOpnErr,D0 ; in case file isn't open MOVE.L FCBFlNm(A1,D1),D2 ; get the file's filenum BEQ.S @1 ; br if this FCB is not in use MOVE.L FCBVPTR(A1,D1),A2 ; get VCB pointer, too BSR ExtOffLinCk ; make sure it's on-line and for us @1 TST D0 ; zero if refnum is ok . . . RTS ; that's all folks... ;_______________________________________________________________________ ; ; Routine: TstMod ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to parameter block, uses: ; IORefNum ; D0.W (output) -- 0 if file writable, errcode otherwise ; This call may be executed asynchronously. ; Called By: FileWrite,SetEOF,FileAlloc ; Function: Test if a file is open, and if so, if it may be written to. ; ; Modification History: ; ; 21 Dec 82 LAK Modified for new file system data structures. ; 17 Jan 83 LAK Changed for write bit location change. ;_______________________________________________________________________ TstMod BSR.S RFnCall ; first see if it's open BNE.S TMExit ; if not, all done MOVEQ #WrPermErr,D0 ; check if file opened for write BTST #FCBWrtBit,FCBMdRByt(A1,D1.W) BEQ.S TMExit ; exit if no write permission BSR CVFlgs ; check if vol locked or wr protected TMExit TST.W D0 ; return status flags set on error RTS ;_______________________________________________________________________ ; ; Routine: FileAlloc ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to parameter block, uses: ; IORefNum,IOReqCount,IOActCount ; D0.W (output) -- 0 if file closed ok, errcode otherwise ; This call may be executed asynchronously. ; Calls: ; Function: Allocate more blocks to a file. ; ; ; Modification History: ; ; 21 Dec 82 LAK Modified for new file system data structures; allocates ; space up to the amount of free space on the disk. ; ;_______________________________________________________________________ FileAlloc BSR FSQueue ; wait our time BSR.S TstMod ; test if may modify this file BNE.S FAlExit ; (also exit for ext fs, offline vols) ;jwp MOVEQ #0,D6 ; assume we get none MOVE.L IOReqCount(A0),D4 ; get request count BEQ.S @1 ; br on zero request TST.W VCBFreeBk(A2) ; any free? BEQ.S @1 ; br if so MOVEQ #0,D0 ; only really need this much . . . BSR AllocSt ; allocate as much space as we can BNE.S FAlExit ; (shouldn't get any error) @1 ;jwp MOVE.L D6,IOActCount(A0) ; report number actually allocated BSR AdjEOF ; adjust everybody's phys&logical EOF's MOVEQ #DskFulErr,D0 ; assume we didn't get what we wanted CMP.L IOReqCount(A0),D6 ; did we? BCS.S FAlExit ; exit if not MOVEQ #0,D0 ; no error FAlExit BRA CmdDone ;_______________________________________________________________________ ; ; Routine: SetEOF ; (c) 1983 Apple Computer, Inc. ; ; Arguments: A0.L (input) -- pointer to parameter block, uses: ; IORefNum,IOLEOF ; D0.W (output) -- 0 if EOF set ok, errcode otherwise ; This call may be executed asynchronously. ; Calls: FSQueue,TstMod,GtNxBlk,AdjEOF,DAlBlks,Alloc,CmdDone ; Function: Set a new logical EOF for a particular file. The physical ; EOF is set to the next block boundary and the current file ; position will be pinned at this value (this may cause ; blocks to be deallocated or allocated). ; ; This call will reclaim space a file is not using. If the ; LEOF is set beyond the current physical EOF and there is ; not enough space on the disk, no change to the file's EOF ; parameters will be made, and a disk-full error will be reported. ; ; ; Modification History: ; ; 22 Dec 82 LAK Modified for new file system data structures; now only ; takes a logical EOF argument; corrected file shorten ; code. ; 06 Mar 83 LAK Fixed bug in SetEOF with IOLEOF = 0 (now deallocates ; the disk blocks . . .) ;_______________________________________________________________________ SetEOF BSR FSQueue BSR.S TstMod ; test if we may modify this file. BNE.S SEOFXit ; exit if cannot mod, or not open. ; (also for ext fs, offline vols) MOVE.L IOLEOF(A0),D7 ; get requested logical end of file MOVE.L D7,D4 ; and copy it SUB.L FCBPLen(A1,D1.W),D4 ; compare with current PEOF BCS.S ShrtnFil ; it's shorter. dealloc blks if we can BHI LngthnFil ; it's longer. alloc blks ; same length needs no extra treatment SEOFOK MOVEQ #0,D0 MOVE.L D7,FCBEOF(A1,D1.W) ; set the new LEOF CMP.L FCBCrPs(A1,D1.W),D7 ; is current position set past this? BCC.S @1 ; br if not MOVE.L D7,FCBCrPs(A1,D1.W) ; if so, pin to this @1 BSR.S AdjEOF ; adjust any other FCBs for this file SEOFXit BRA CmdDone ; Shorten the file. If setting length to zero, zero start block, phys EOF fields ; Else mark last block with 001, and dealloc the following blks ShrtnFil MOVEQ #0,D6 ; used to figure new physical length MOVE.W FCBSBlk(A1,D1),D3 ; start block for file MOVE.L VCBAlBlkSiz(A2),D2 ; get alloc blk size in bytes MOVE.L D7,D4 ; new logical end-of-file BNE.S @2 ; br if not setting the file to zero CLR.W FCBSBlk(A1,D1) ; zero start block for file BRA.S ShFlFin @1 MOVE.W D5,D3 ; follow block chain to last block @2 ADD.L D2,D6 ; increase our physical length BSR GtNxBlk ; get block following D3 blk SUB.L D2,D4 ; file log byte cnt - alloc blk size BHI.S @1 ; loop until at the new last file block CMP.L FCBPLen(A1,D1.W),D6 ; did it actually decrease? BCC.S SEOFOK ; exit if not MOVE.W D5,-(SP) ; save start block of chain to dealloc MOVEQ #1,D5 ; mark new last block BSR SubMrk MOVE.W (SP)+,D3 ShFlFin BSR DAlBlks ; deallocate from block D3 onwards. MOVE.L D6,FCBPLen(A1,D1.W) ; set new physical length ; *** GSS/KWK *** 8/3/84 patch from MSFileFix label SetEOFix. If the buffer has a deleted ; block then it is invalidated so that a flushfile etc., won't write it ; out to disk. ; Note: I think there is a quicker way to do this, since we have D3 with info about ; the last good block in the shortened file. But this version should work for ; now JSR GetFilBuf ; get A5 -> file's buffer MOVEQ #0,D3 ; clear high word MOVE.W BufTag(A5),D3 ; see what block is there AND.W #$7FFF,D3 ; ignore dirty bit SUB.W VCBAlBlSt(A2),D3 ; normalize it BMI.S @1 ; just exit if not an alloc blk MOVE.L VCBAlBlkSize(A2),D2 ; bytes in an alloc block LSR.L #8,D2 ; change into cnt of 512-byte blks LSR.L #1,D2 ; in an alloc blk DIVU D2,D3 ; ordinal alloc blk number CMP.W VCBNmAlBlks(A2),D3 ; past the end? BGE.S @1 ; then exit ADDQ.W #2,D3 ; first alloc blk is blk 2 JSR GtNxBlk ; set D5=block link . . . BNE.S @1 ; exit if it's not free CLR.W BufTag(A5) ; invalidate buffer otherwise @1 ; *** End of patch BRA SEOFOK ; finish up, no errors ; lengthen the file. LngthnFil BSR Alloc ; need difference from current phys EOF BNE.S SEOFXit ; exit if we couldn't get that much BRA.S SEOFOK ; otherwise, finish up ;_______________________________________________________________________ ; ; Routine: AdjEOF ; ; Arguments: (A1,D1) (input) -- FCB pointer ; All registers are preserved. ; Calls: Gt1stFCB,GtNxtFCB ; Called By: FileWrite,SetEOF,FileAlloc ; Function: Scan through the FCBs and update any entries with the ; same filenumber and resouce/regular bit to the same ; start blk, physical EOF, and logical EOF as the FCB ; pointed to by (A1,D1). The FCB is marked as modified. ; ; Modification History: ; ; 22 Dec 82 LAK Modified for new file system data structures; now only ; takes an FCB pointer and preserves all registers. ; 29 Aug 83 LAK Rewrote to take advantage of FCB field order and ; Gt1stMatch, GtNxtMatch subroutines. ;_______________________________________________________________________ AdjEOF MOVEM.L D0-D6/A0-A2,-(SP) ; preserve all registers ADD D1,A1 ; point to entry MOVE.L (A1)+,D2 ; FCBFlNm BSET #FCBModBit,(A1) ; mark file modified MOVE.W (A1),D0 ; save FCBFlags MOVEM.L (A1)+,D3-D6/A2 ; FCBSBlk,FCBEOF,FCBPLen,(FCBCrPs),FCBVPtr MOVE.W D0,D6 ; FCBMdRByt (in high byte . . .) ; now scan thru all FCBs and update those for the same file (we update ; our own file's fields with the same information) BSR Gt1stMatch ; scan from the beginning BNE.S @3 ; exit if no matches found @1 LEA FCBFlags(A1,D1),A0 ; MOVE.W (A0)+,D0 ; same resource/regular flag bit? EOR.W D6,D0 LSL.W #7,D0 ; get FCBRscBit in carry BCS.S @2 ; br if they don't match MOVE.W D3,(A0)+ ; FCBSBlk MOVE.L D4,(A0)+ ; FCBEOF MOVE.L D5,(A0)+ ; FCBPLen @2 BSR GtNxtMatch ; go after the next entry BEQ.S @1 ; until we've done 'em all @3 MOVEM.L (SP)+,D0-D6/A0-A2 ; leave registers unchanged RTS ; that's all folks. ; File: FSRfN3.Text ;_______________________________________________________________________ ; ; External Routines: ; ; ; Internal Routines: Lg2Phys,GtNxBlk,Alloc,DAlBlks,SubMrk ; ; Function: This file contains file system utility routines which ; use the volume block map. ; ; Modification History: ; 23 Dec 82 LAK Added incr, decr of volume free block count in DAlBlks, ; Alloc; mark volume dirty in these routines instead of ; SubMrk. ; 17 Jan 83 LAK Changed for final volume format (alloc blks offset) ; 15 Aug 83 LAK Fixed bug in block allocation (last free block bug). ; 18 Aug 83 LAK Fixed same bug again (3 more to go). ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: Lg2Phys ; (c) 1983 Apple Computer Inc. ; ; Arguments: A2.L (input) -- VCB ptr ; (A1,D1.W) (input) -- FCB pointer ; D4.L (input) -- number of bytes desired ; D5.L (input) -- current file position ; D3.W (output) -- physical block start for current position ; D0.W (output) -- physical end block + 1 ; D6.L (output) -- number of consecutive bytes available, up to D4 ; All other registers are preserved ; Note that Lg2Phys assumes we won't run off the end of a file. ; Called By: FileRead,FileWrite,MyRead ; Calls: GtNxBlk ; Function: Transforms file block number to volume block number and ; determines max number of consecutive disk bytes available. ; ; Modification History: ; 16 Dec 82 LAK Rewrote to reflect new file system data structures; ; Now passes back block parameter; takes allocation size ; into account. ; 22 Dec 82 LAK Changed to use alloc blk size in bytes. ; 17 Jan 83 LAK Changed to add alloc blk offset in. ; 15 Aug 83 LAK Added safety check to prevent directory overwrites; ; D0 now returns end block + 1. ;_______________________________________________________________________ Lg2Phys MOVEM.L D1-D2/D4-D5/D7,-(SP) ; preserve dem regs MOVE.L VCBAlBlkSiz(A2),D7 ; number of bytes in an alloc blk MOVE.W FCBSBlk(A1,D1),D3 ; start alloc block for file ADD.L D5,D4 ; current file pos + bytes desired SUBQ.L #1,D4 ; actual ending byte. MOVEQ #9,D1 ; shift count. MOVEQ #0,D6 ; no bytes available so far LSR.L D1,D4 ; end file (512-byte) block LSR.L D1,D7 ; num of 512-byte blks in alloc blk MOVE.L D5,D2 ; current file byte position LSR.L D1,D2 ; current (512-byte) file block BRA.S @2 ; br to end condition test @1 BSR.S GtNxBlk ; get block linked to by this block MOVE.W D5,D3 ; next linked blk -> cur block SUB.W D7,D4 ; one alloc blk closer to the end block SUB.W D7,D2 ; reached the desired alloc block? @2 CMP.W D7,D2 ; less than alloc blk size? BCC.S @1 ; keep looping until found MOVE.W D3,D0 ; cur alloc blk SUB.W #2,D0 ; adjust (1st alloc blk is blk 2) MULU D7,D0 ; convert to 512-byte blocks ADD.W D2,D0 ; add offset within alloc blk ADD.W VCBAlBlSt(A2),D0 ; add offset to alloc blk space on disk MOVE.W D0,-(SP) ; starting block (save for return) L2PPrt2 MOVE.W D7,D0 ; alloc blk size (mult of 512-bytes) SUB.W D2,D0 ; number of 512-byte blks avail this ADD.W D0,D6 ; add incremental 512-byte blks available SUB.W D0,D4 ; done enough blocks? BMI.S @0 ; yup. BSR.S GtNxBlk ; no, look into the next block MOVEQ #0,D2 ; entire alloc block ADDQ.W #1,D3 ; check for consecutive blocks CMP.W D3,D5 ; is it the next block? BEQ.S L2PPrt2 ; yes, keep going ; All done- give back needed info @0 MOVE.W (SP)+,D3 ; start physical 512-byte block MOVE.W D3,D0 ; block start ADD.W D6,D0 ; last block in string (return) ; this is important data, capable of trashing a diskette if wrong, so ; make sure it's in the allocation block range . . . MOVE.W VCBAlBlSt(A2),D4 ; first 512-byte alloc block MULU VCBNmAlBlks(A2),D7 ; number of alloc blocks ADD.W D4,D7 ; last 512-byte alloc block + 1 CMP.W D7,D0 ; last block in range? BHI.S toDSFSErr ; br if not CMP.W D4,D3 ; is start block > alloc start blk? BCS.S toDSFSErr ; br if not ok ASL.L D1,D6 ; num blocks avail*512 = available bytes MOVEM.L (SP)+,D1-D2/D4-D5/D7 ; restore regs CMP.L D4,D6 ; don't allow D6 larger than D4 BLS.S @3 MOVE.L D4,D6 @3 RTS toDSFSErr MOVEQ #DSFSErr,D0 _SysError ;_______________________________________________________________________ ; ; Routine: GtNxBlk ; (c) 1983 Apple Computer Inc. ; ; Arguments: A2.L (input) -- VCB pointer ; D3.W (input) -- current block number (alloc blk size) ; D5.W (output) -- block pointed to by D3 ; Preserves all registers ; Sets CCR according to D5 value ; Called By: Alloc,DAlBlks,Lg2Phys,SetEOF,SuckEmUp ; Function: Figures next block in link. ; ; Modification History: ; 16 Dec 82 LAK Reformatted ; 22 Dec 82 LAK Changed to take VCB pointer as input ; 13 Jan 82 LAK Modified for allocation blocks offset from start of disk. ; ; - note the 32K size limitation for block maps? ;_______________________________________________________________________ GtNxBlk MOVEM.L D0/A2,-(SP) ; preserve all regs used MOVE.L VCBMAdr(A2),A2 ; get volume block map address MOVE.W D3,D0 ; current alloc block SUBQ.W #2,D0 ; first alloc block is number 2 MULU #BtsPrBlk,D0 ; cur blk's bit position in block map ROR.L #4,D0 ; word bound, save bit pos in hi word ASL.W #1,D0 ; for indexing by words MOVE.L 0(A2,D0.W),D5 ; get the long word the block is in CLR.W D0 ; prepare for shift count ROL.L #4,D0 ; get shift cnt from hi word ADD.W #12,D0 ; make into a rotate left count ROL.L D0,D5 ; rotates proper block into low 12 bits AND.W #$0FFF,D5 ; only give 12 bits MOVEM.L (SP)+,D0/A2 RTS ;_______________________________________________________________________ ; ; Routine: Alloc,Alloc1 ; (c) 1983 Apple Computer Inc. ; ; Arguments: A2.L (input) -- VCB ptr ; (A1,D1.W) (input) -- FCB pointer ; D4.L (input) -- number of bytes extra to allocate ; D0.W (output) -- Error code, 0 if no error. ; (only error: dskfull) ; D6.L (output) -- number of bytes actually allocated (always ; a multiple of 512-bytes). ; All other registers are preserved ; Called By: FileWrite,SetEOF,FileAlloc(Alloc1) ; Calls: GtNxBlk,SubMrk ; Function: Allocate new blocks to a file. (Does not check lock bits!) ; Alloc entry point does nothing if it can't get all the bytes ; requested; Alloc1 entry gets what it can. ; ; Modification History: ; 22 Dec 82 LAK Rewrote to reflect new file system data structures; ; No reclamation of space is now needed from undeleted ; files; always checks number of free bytes available ; on the volume before proceeding; now has 2 entry points. ; 17 Jan 83 LAK Changes for alloc blks offset from diskette start. ; ;_______________________________________________________________________ Alloc MOVE.L D4,D0 ; flag to get requested bytes or none AllocSt MOVEM.L D1-D5/D7/A0-A3,-(SP) ; preserve all but D0, D6 MOVE.L VCBAlBlkSiz(A2),D7 ; number of bytes in an alloc blk MOVE.L D7,D6 MULU VCBFreeBk(A2),D6 ; number of free bytes available CMP.L D6,D4 ; pin it at number of free blocks avail BLS.S @1 MOVE.L D6,D4 ; actual @1 CMP.L D0,D4 ; is this enough? BCC.S FigEndBlk ; br if so . . . MOVEQ #DskFulErr,D0 ; report disk full AllocXit MOVEM.L (SP)+,D1-D5/D7/A0-A3 ; restore regs used RTS ; D4 now contains the number of extra bytes needed, and we know they are out there FigEndBlk MOVE.L FCBPLen(A1,D1.W),D2 ; current physical length MOVEQ #0,D6 ; bytes allocated so far MOVE.W FCBSBlk(A1,D1),D3 ; start block for file BNE.S @2 ; br if there is a first block BSR.S GetFreeBk ; find a free block (put in D3) MOVE.W D5,FCBSBlk(A1,D1.W) ; only place we set this in file system MOVE.W D5,D3 ; now the current block MOVEQ #1,D5 ; mark this as the ending block BSR SubMrk BRA.S AlCkDone ; and join in with regular alloc code @1 MOVE.W D5,D3 ; next alloc blk in list @2 BSR.S GtNxBlk ; get block linked to by this block CMP.W #1,D5 ; terminal block? BNE.S @1 ; notice at this point, ; D3=current last block (alloc size) ; D2=physical file length (eventually) ; D4=bytes extra we need ; D6=# allocated already ; D7=num bytes in an alloc blk ; Now at the end of the file. start allocating AlNxtBlk CMP.W VCBNmBlks(A2),D3 ; > # blocks on the disk? BHI.S AlNtAtEnd ; br if so. (remember, 1st blk is #2) ADDQ.W #1,D3 ; next blk free? BSR GtNxBlk SUBQ.W #1,D3 ; back up to block that is in file TST.W D5 ; is it avail? BNE.S AlNtAtEnd ; br if not MOVE.W D3,D5 ; mark it for this file ADDQ.W #1,D5 ; AlLinkIn BSR SubMrk ; link block D3 to D5 MOVE D5,D3 ; now mark block D5 as 1 (EOF) MOVEQ #1,D5 BSR.S SubMrk AlCkDone ADD.L D7,D6 ; another block added ADD.L D7,D2 ; extends physical length by alloc size SUBQ.W #1,VCBFreeBks(A2) ; decrease num of free alloc blks on vol CMP.L D6,D4 ; have we added enough? BHI.S AlNxtBlk ; no, allocate more at the end ; have allocated all we need. restore regs, calc params, go home MOVE.L D2,FCBPlen(A1,D1.W) ; set new physical length ST VCBFlags(A2) ; mark volume block map modified MOVEQ #0,D0 ; no errors BRA.S AllocXit AlNtAtEnd BSR.S GetFreeBk ; find a free one with some room BRA.S AlLinkIn ; then link it in . . . ; Comes here when we need to allocate a block but the next block in the ; file is not available (or when there are no blocks allocated to a file) GetFreeBk MOVEM.L D0-D4/D6/A1/A3,-(SP) SUB.L D6,D4 ; bytes needed - bytes allocated so far CMP.L VCBClpSiz(A2),D4 ; is this more than minimum clump? BCC.S @1 ; br if so MOVE.L VCBClpSiz(A2),D4 ; make this our minimum search size @1 MOVE.L VCBMAdr(A2),A1 ; block map address MOVE.W VCBNmBlks(A2),A3 ; number of alloc blocks ADDQ #1,A3 ; last alloc block number (1st one is 2) MOVEQ #0,D5 ; odd/even counter MOVEQ #2,D0 ; current block number MOVEQ #2,D2 ; last string block number start MOVEQ #0,D3 ; contiguous byte count MOVEQ #0,D6 ; max contiguous byte count CLR.W -(SP) ; block number of max string start ; we have 2 cases for the 12 bits we are after in the bit map: ; ; case 0: A1 => [nib0][nib1] [nib2][xxxx] ; ; case 1: A1 => [xxxx][nib0] [nib1][nib2] Scan4Free MOVE.B (A1)+,D1 ; get next bitmap byte LSL.W #8,D1 ; make room for 4-8 more bits BCHG #0,D5 ; are we odd or even? BNE.S @1 ; branch if we were in the middle of a byte MOVE.B (A1),D1 ; get the next 4 bits, don't increment ptr LSR.W #4,D1 ; move it back down to ground-zero BRA.S @2 ; go check for free . . . @1 MOVE.B (A1)+,D1 ; so get the other 8 . . . AND.W #$0FFF,D1 ; we only got 4 bits so far for this case @2 BNE.S NotFree ; br if not zero (i.e., not free) ADD.L D7,D3 ; incr contiguous free byte count CMP.L D6,D3 ; is this greater than our max so far? BLS.S @3 ; br if not MOVE.L D3,D6 ; if so, make this our new max MOVE.W D2,(SP) ; max string block start CMP.L D4,D3 ; is it more than we need? BCC.S GtFreeXit ; then we are done @3 ADDQ.W #1,D0 ; increment current block number BRA.S NF1 NotFree ADDQ.W #1,D0 ; increment current block number MOVE.W D0,D2 ; assume it starts a new string of freebees MOVEQ #0,D3 ; zero current contiguous byte count NF1 CMP.W A3,D0 ; past the end? BLS.S Scan4Free ; br if not GtFreeXit MOVE.W (SP)+,D5 ; get the max so far MOVEM.L (SP)+,D0-D4/D6/A1/A3 ; restore all registers except D5 result RTS ;_______________________________________________________________________ ; ; Routine: SubMrk ; (c) 1983 Apple Computer Inc. ; ; Arguments: D3.W (input) -- block (alloc size) to mark ; D5.W (input) -- mark value to put into the 12-bits ; A2.L (input) -- VCB pointer ; All registers are preserved ; Called by: SetEOF,Alloc ; Function: Set a value into a particular location in the block map: ; puts D5 into (D3). (effectively linking D3 to D5) ; ; Modification History: ; ; 22 Dec 82 LAK Reformatted; now takes VCB ptr as input; saves all regs; ; 17 Jan 83 LAK Changed for alloc blks diskette offset. ;_______________________________________________________________________ SubMrk MOVEM.L D0/D6/A2,-(SP) ; preserve all regs MOVE.L VCBMAdr(A2),A2 ; need addr of disk map. MOVE.W D3,D0 ; current alloc block SUBQ.W #2,D0 ; first alloc block is number 2 MULU #BtsPrBlk,D0 ; cur blk's bit position in block map ROR.L #4,D0 ; put bit shift count into high 4 bits ASL.W #1,D0 ; make word index, don't disturb HO byte MOVE.L 0(A2,D0.W),D6 ; get old stuff SWAP D0 ; get shift count into low word LSR.W #8,D0 ; get count into low order bits LSR.W #4,D0 ; all the way down--- ADD #12,D0 ; make into a rol shift count ROL.L D0,D6 ; get the block into the 12 ls bits AND.W #$F000,D6 ; make it 0, OR D5,D6 ; put new value in it. ROR.L D0,D6 ; put it back in order SWAP D0 ; get word index back MOVE.L D6,0(A2,D0.W) ; stuff it back into the table MOVEM.L (SP)+,D0/D6/A2 ; restore regs RTS ; that's all folks ;_______________________________________________________________________ ; ; Routine: DAlBlks ; (c) 1983 Apple Computer Inc. ; ; Arguments: D3.W (input) -- start of chain of blocks (of alloc size) ; A2.L (input) -- VCB pointer ; D0.W (output) -- error code ; All registers are preserved. ; Calls: GtNxBlk,SubMrk ; Called by: Delete,SetEOF ; Function: ; ; Modification History: ; ; 22 Dec 82 LAK Reformatted; preserves regs (necessary?) ; 17 Jan 83 LAK Don't preserve regs cause we don't have to. ; ; - note that deallocated blocks still exist on the diskette until allocated ; to new files and rewritten. ;_______________________________________________________________________ ; DAlBlks- deallocate blocks. Enter with D3=start of chain ; exits with all blocks marked 0 in map table. does not write to disk DAlBlks TST.W D3 ; any blocks? BEQ.S DAlDone ; if no blocks, all done. @1 BSR GtNxBlk ; get the next in the chain. MOVE.W D5,-(SP) ; save the link CLR.W D5 ; link cur blk to 00 BSR.S SubMrk ; and mark it. ADDQ.W #1,VCBFreeBks(A2) ; increase num of free alloc blks on vol MOVE.W (SP)+,D3 ; next linked block CMP.W #1,D3 ; last block? BNE.S @1 ; no, do whole list. ST VCBFlags(A2) ; mark volume block map modified DAlDone RTS ; that's all folks. ; FILE FSVol.Text ; MACWORKS copy ;_______________________________________________________________________ ; ; External Routines: MountVol,UnMountVol,GetVol,SetVol, ; FlushVol,GetVolInfo,Eject,OffLine ; ; Internal Routines: DtrmVol,FindDrive, ; QWordSearch,GetVCBRfn,GetVCBDrv,CVFlgs,CkExtFS ; ; Function: This file contains mostly volume-level routines. ; ; Modification History: ; ; 03 Dec 82 LAK Added FindDrive and Eject; Eject calls UnmountVol. ; 06 Dec 82 LAK Modified for VCB, Drive queues; added QWordSearch; Changed ; file number to a non-recycled longword. ; 07 Dec 82 LAK Made FlushVol external; FlushFile no longer does a volume ; flush with refnum 0; FCB memory block now starts with word ; offset to end of block. Changed drive block offset to a ; longword variable. ; 10 Dec 82 LAK DtrmVol now looks at drive param if no vol prefix; uses ; global string compare proc. ; 13 Dec 82 LAK Added entry point DtrmVol1 to DtrmVol. RdMstDirBlk calls ; MyReadDB now (doesn't init A5) ; 17 Dec 82 LAK Added support for file tags; made the eject control call ; asynchronous. ; 13 Jan 82 LAK Added status call to get write protect status when mounting ; a drive; drive number is used for disk driver requests ; instead of block offset; internal, external twiggys are ; now drives 1 and 2; updated param lists, documentation. ; Removed DisposBuf; added checks in SuckEmUp. Eject, UnmountVol, ; and FlushVol can take a volume name prefix in the filename ; field to specify the volume BUT the IODrvNum field is ; filled in . . . ; 23 Jan 83 LAK MountVol ejects diskette if the directory is bad; changed ; GetVolInfo and GetVol to do as documented. ; 24 Jan 83 LAK GetVolInfo looks at name if index is negative; switched ; around DtrmVol some. ; 07 Feb 83 LAK Don't eject after unsuccessful mount . . . ; 14 Feb 83 LAK Now searches thru directory on a mount to see if number ; of files and next file number are consistent with volume ; master info. ; 15 Feb 83 LAK Added a consistency check on mount between block map ; and file directory lengths . . . ; 25 May 83 LAK Changed interface to CmpString. ; 02 Jun 83 LAK Made many changes supporting off-line mounted volumes. ; 24 Aug 83 LAK Added timestamp of volume info at flush time. ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; Routine: MountVol ; Arguments: A0 (input) -- pointer to volume parameter block, uses: ; IOVDrvNum (name not allowed) ; D0 (output) -- error code ; This call is executed synchronously. ; Calls: FSQueueSync,GetVCBDrv,GetVCBRfn,FindDrive,CmdDone, ; MyRead(via RdMstDirBlk) ; ; Function: The VCB pointers are checked to see if a volume in the ; specified drive is already on-line (error if so). Reads in the ; directory master block and block map and allocates memory for ; them; a buffer for the volume is also allocated. The VCB is ; added to the VCB queue. (For remounts, VCB is not reallocated ; or requeued). ; ; Modification History: ; ; 03 Dec 82 LAK Calls ConvDrvNum to get the driver refnum and blk offset. ; 06 Dec 82 LAK Changed to use VCB queue. ; 13 Jan 83 LAK Changes: no drive blk offset; better documentation. ; 30 May 83 LAK Rewrote to support off-line mounted volumes. Integrated ; SuckEmUp into mainline of this routine. ; ?? XXX 83 JWP Added patch to check for mem-full errors, handle them. ; 28 Jun 84 KWK Changed BSR MakeStkPB to JSR (routine in starthooks) ; 6 Feb 85 KWK Put preamble to MountVol for first-time call to it, which ; always occurs in the BootBlocks code while mounting the ; boot device. This ensures that the hard disk (if it's ; there) will be visible (shown by Finder) even if the boot ; was forced to be from the Sony. ;_______________________________________________________________________ ; This mountvol routine is only the entry point for the first call to MountVol. ; The purpose is to make sure that the MacWorks hard disk is mounted even if ; the Sony is the boot device. MountVol BSR.S RealMountVol ; do the mount vol call MOVEM.L D0/A0-A1,-(SP) ; save result, A-regs ; see if the hard disk volume is on line MOVE.L VCBQHdr+QHead,D0 ; get ptr to 1st VCB entry @0 BLE.S @2 ; last one, exit (must handle -1) MOVE.L D0,A1 CMP.W #4,VCBDrvNum(A1) ; is it the hard disk drive? BEQ.S @4 ; yes, bail out MOVE.L QLink(A1),D0 ; get ptr to next VCB BRA.S @0 ; and loop ; Looped through every VCB, didn't find hard disk. ; Now mount the hard disk. If no driver, we'll just get an error back @2 MOVEQ #-1,D0 @3 CLR.W -(SP) ; clear space for IO param block on stack DBRA D0,@3 MOVE.L SP,A0 ; set up A0 MOVE.W #4,IOVDrvNum(A0) ; mount the hard disk BSR.S RealMountVol ; input is A0 = param blk ptr ADD.W #IOVQElSize,SP ; flush param block @4 MOVEM.L (SP)+,D0/A0-A1 ; restore regs RTS ; and return RealMountVol MOVEM.L A0-A1/D1-D2,-(SP) ; preserve these BSR MVol1 ; do the synchonous call (A0-> param blk) MOVEM.L (SP)+,A0-A1/D1-D2 ; restore regs CMP.W #MemFullErr,D0 ; memory full? BEQ.S @1 ; br and do something about memfull errs RTS ; else just return ; Now try and make some memory so we can mount the volume ; (D1 preserves original trap) @1 MOVE.L VCBQHdr+QHead,D0 ; go thru VCBs to find one to force offline @3 BEQ.S @5 ; fail when we get to the end MOVE.L D0,A1 ; VCB ptr TST.W VCBFSID(A1) ; our file system? BNE.S @2 ; br if not . . . MOVE.W VCBDrvNum(A1),D2 ; on-line? BNE.S @4 ; br if so @2 MOVE.L QLink(A1),D0 ; check next one if not . . . BRA.S @3 @4 MOVE.L A0,-(SP) ; don't mess with user's pblk . . . JSR MakeStkPB ; esp. IOFileName . . . MOVE.W D2,IODrvNum(A0) ; drive number for volume to take off-line _OffLine ADD #IOVQElSize,SP ; clean up stack MOVE.L (SP)+,A0 BNE.S @2 BSR.S RealMountVol ; try mounting again BEQ.S @1 ; try again for memfull errs . . . BRA.S @6 ; otherwise, exit . . . @5 ; try unmounting idle volumes here . . . MOVEQ #MemFullErr,D0 ; give up and return mem full error @6 RTS MVol1 BSR FSQueueSync ; sync it since it calls mem mgr ST NewMount ; assume now that it's a new volume ; first see if the disk is already mounted and drive parameter is positive MOVEQ #ParamErr,D0 MOVE.W IOVDrvNum(A0),D2 ; drive number BLE.S MtVolDone ; br if none specified MOVE.W D2,D0 ; see if there's a vol already mounted BSR GetVCBDrv ; BNE.S MtVol1 ; br if not MOVEQ #VolOnLinErr,D0 ; report an error MtVolDone BRA CmdDone ; return to command finish ; make sure such a drive exists MtVol1 BSR FindDrive ; get driver refnum in D1 BNE.S MtVolDone ; br if not found or for external FS ; get memory for the VCB and volume buffer MOVE.L A0,A2 ; preserve A0 a bit MOVEQ #VCBLength,D0 ; get memory for VCB _NewPtr ,SYS,CLEAR ; off the system heap, zeroed ; note:VCBFSID,VCBDirIndex,VCBFlags are 0 BNE.S MtVolDone ; exit if we got no memory EXG A0,A2 ; VCB ptr MOVE.W D2,VCBDrvNum(A2) ; put drive number MOVE.W D1,VCBDRefNum(A2) ; and driver refnum into VCB @0 SUBQ.W #1,D0 ; assign a refnum to this new volume MOVEM.L D0/A2,-(SP) BSR GetVCBRfn ; get one nobody else has (start with MOVEM.L (SP)+,D0/A2 ; -1 and get smaller) BEQ.S @0 ; br if already assigned MOVE.W D0,VCBVRefNum(A2) ; note for us MOVE.L #,D0 ; also get memory for a buffer _NewPtr ,SYS,CLEAR ; off the system heap, zeroed BNE.S MtVolEr2 ; get rid of VCB memory if memory full ADD #HdrSiz,A0 ; bump past buffer header MOVE.L A0,VCBBufAdr(A2) ; and save the buffer pointer ; now try reading in the master directory block MOVE.W DrMstrBlk,D3 ; read in master block BSR MyReadDB ; (uses volume's buffer) BNE.S MtVolEr1 ; exit on errors (dump VCB, block buffers) MOVEQ #NoMacDskErr,D0 ; in case it's not a MAC diskette CMP.W #SigWord,DrSigWord(A5) BNE.S MtVolEr1 ; exit on errors (dump VCB, block buffers) MOVEQ #BadMDBErr,D0 ; in case master directory block is bad MOVE.L DrAlBlkSiz(A5),D1 ; make sure this is non-zero, 512 multiple BEQ.S MtVolEr1 ; exit if 0 AND.W #$01FF,D1 ; 512-byte multiple? BNE.S MtVolEr1 ; exit if not ; transfer all directory info from buffer into VCB MOVEQ #-1,D0 ; number of words to transfer MOVE.L A5,A1 ; source is the buffer LEA VCBDInfoSt(A2),A0 ; destination is VCB directory info @1 MOVE.W (A1)+,(A0)+ ; move it in DBRA D0,@1 ; now check to see whether we happen to be remounting a volume . . . MOVE.L VCBQHdr+QHead,D0 ; search the queue of VCBs @2 BEQ.S GetVolMap ; br at end of queue MOVE.L D0,A0 MOVE.B VCBAtrb+1(A0),VCBAtrb+1(A2) ; (only field that need not match) LEA VCBSigWord(A0),A1 ; already mounted volume LEA VCBSigWord(A2),A3 ; volume being mounted MOVEQ #-1,D0 ; check 64 master directory blk bytes @3 CMPM.L (A1)+,(A3)+ ; field match? BNE.S @4 ; br if not (not this volume) DBRA D0,@3 TST.W (A1) ; should be off-line (0 drive num) BNE.S @4 ; br if not (we can mount 2 identical) ; diskettes on different drives) ; remount here . . . so move in the new drive and driver number CLR.B NewMount ; not a new volume MOVE.L (A3)+,(A1)+ ; drive and driver number MOVE.L VCBBufAdr(A2),VCBBufAdr(A0) ; new buffer address EXG A0,A2 ; flip VCB _DisposPtr ; get rid of newly created one BRA.S GetVolMap ; and go on @4 MOVE.L QLink(A0),D0 BRA.S @2 ; here are some exit error routines MtVolErr BSR.S DsposMap ; return map table memory MtVolEr1 BSR.S DsposBuf ; get rid of data buffer MtVolEr2 TST.B NewMount ; only deallocate VCB for new mounts BEQ.S @2 BSR.S DsposVCB ; get rid of VCB memory @1 BRA.S MtVolDone @2 ADD #VCBDrvNum,A2 ; point to drive number field MOVE.W (A2),D3 CLR.W (A2)+ ; it should be zeroed MOVE.W D3,(A2)+ ; and put back into driver refnum field ADDQ #4,A2 ; bump to buffer pointers CLR.L (A2)+ ; both should be cleared CLR.L (A2)+ BRA.S @1 DsposMap MOVE.W D0,-(SP) ; preserve error code MOVE.L VCBMAdr(A2),A0 ; return map table memory _DisposPtr BRA.S DsposExit DsposBuf MOVE.W D0,-(SP) ; preserve error code MOVE.L VCBBufAdr(A2),A0 ; get rid of data buffer SUBQ #8,A0 SUBQ #2,A0 ; first point to memory start _DisposPtr BRA.S DsposExit DsposVCB MOVE.W D0,-(SP) ; preserve error code MOVE.L A2,A0 ; and VCB on errors _DisposPtr DsposExit MOVE.W (SP)+,D0 ; restore error code RTS ; figure the number of bytes in the block map and get space for the map table GetVolMap MOVEQ #BtsPrBlk/4,D2 ; # bits per block in map table/4 MULU VCBNmBlks(A2),D2 ; compute number of bytes in table ADDQ.L #1,D2 ; first add 1 to round up for odd blks LSR.L #1,D2 ; #bytes=#bits/2 (D2 was # of bits/4) MOVE.W D2,VCBMLen(A2) ; save in our volume structure MOVEQ #MpTblStrt,D7 ; index into directory block MOVE.L D2,D0 ; request bytes _NewPtr ,SYS ; get it BNE.S MtVolEr1 ; exit if there's not enough memory MOVE.L A0,VCBMAdr(A2) ; put addr into VCB ; at this point: ; A0 points to where map table starts ; A5 points to disk buffer ; D3 is current master directory disk block ; D2 is # bytes in map table ; D7 is index to map table in disk buffer MvMapIn MOVE.W 0(A5,D7),(A0)+ ; move the map in. ADDQ.W #2,D7 ; next word SUBQ.W #2,D2 ; only move this many bytes BLE.S MpTblIn ; br if table moved in CMP.W #512,D7 ; have we moved this whole block? BCS.S MvMapIn ; no, keep moving ; read next block of map in from disk-- ADDQ.W #1,D3 BSR MyReadDB BNE.S MtVolErr ; exit on master directory read errors MOVEQ #0,D7 ; start at beginning of this block BRA.S MvMapIn ; the map table is in, so verify it by comparing the number of free blocks ; against the value in the master directory block . . . MpTblIn MOVEQ #2,D3 ; first alloc block is block 2 MOVEQ #0,D1 ; start with 0 free blocks @1 BSR GtNxBlk BNE.S @2 ; loop if unavailable ADDQ.W #1,D1 ; one more available @2 CMP.W VCBNmAlBlks(A2),D3 BHI.S @3 ; end if we looked at them all ADDQ.W #1,D3 ; look at next block BRA.S @1 @3 MOVEQ #BadMDBErr,D0 ; in case free block count is bad CMP.W VCBFreeBks(A2),D1 BNE.S MtVolErr ; exit if so (bad problem) ; now make sure we are consistent with the number of files and next free ; file number by scanning the directory blocks DirConChk MOVEQ #0,D1 ; number of entries MOVEQ #0,D2 ; max used file number MOVEQ #0,D7 ; inconsistency tab (->VCBAttrib+1) BSR Rd1stDB ; get the first directory block BNE.S MtVolErr ; exit if we can't read the first one . . . ConChkLoop MOVEQ #0,D0 ; init index into directory block @1 TST.B FlFlags(A5,D0) ; flags=0 means end of entries this blk BEQ.S @3 ; br if no more entries this block ADDQ.W #1,D1 ; incr number of files CMP.L FlFlNum(A5,D0),D2 ; check the file number for this file BHI.S @2 ; br if less than current max MOVE.L FlFlNum(A5,D0),D2 ; otherwise it becomes the new max @2 LEA FlStBlk(A5,D0),A3 BSR CkFilLen ; check file length against map table LEA FlRStBlk(A5,D0),A3 BSR.S CkFilLen ; and resource fork, too BSR GtNxEntry ; (A5,D0) point to next entry, D6 trashed BCS.S @1 ; br if not finished with this block @3 BSR RdNxtDB ; get next directory block BEQ.S ConChkLoop DScanDone CMP.W #DirFulErr,D0 ; was it really the end we reached? BNE.S MtVolErr ; exit if not . . . CMP.L VCBNxtFNum(A2),D2 ; is VCB next file number greater? BCS.S @1 ; br if so . . . ADDQ.L #1,D2 MOVE.L D2,VCBNxtFNum(A2) ; otherwise, use next number ADDQ.B #1,D7 ; bit 0 of attributes notes this problem @1 CMP.W VCBNmFls(A2),D1 ; how about the file count? BEQ.S @2 MOVE.W D1,VCBNmFls(A2) ; make it consistent ADDQ.B #2,D7 ; bit 1 of attributes notes this problem @2 TST.B D7 ; found any inconsistencies? SNE VCBFlags(A2) ; mark VCB info dirty if so ; now get the write protect status . . . GetWrPrSts LEA Params,A0 ; general purpose I/O param block MOVE.W #DrvStsCode,CSCode(A0) ; drive status! _Status ; refnum and drivenum set up by read call TST.B WriteProt+CSParam(A0) BPL.S @1 ; br if not write-protected BSET #VCBWrProt,D7 ; it's write-protected . . . @1 MOVE.B D7,VCBAtrb+1(A2) ; write-protect, consistency status ; everything is groovy, so add this VCB to our queue if it's not a remount MOVE.L FSQHead,A0 ; A0 -> current request MOVE.W VCBVRefNum(A2),IOVRefNum(A0) ; return volume refnum to user TST.B NewMount ; check for remount (VCB already queued) BEQ.S @3 ; br if so MOVE.L A2,A0 ; VCB pointer LEA VCBQHdr,A1 ; VCB queue header TST.L QHead(A1) ; if the queue is empty, make this BNE.S @2 ; VCB our default volume MOVE.L A2,DefVCBPtr @2 JSR EnQueue ; @3 MOVEQ #0,D0 ; no errors BRA MtVolDone ; so exit (finally) CkFilLen MOVEM.L D2-D6,-(SP) MOVEQ #0,D6 ; will contain phys len of file via map MOVE.L VCBAlBlkSize(A2),D2 ; disk alloc block size MOVE.W (A3),D3 BEQ.S @2 ; br if no start block @1 BSR GtNxBlk MOVE.W D5,D3 ; next block BEQ.S @2 ; if it points to 0, don't count it ADD.L D2,D6 ; otherwise, add an alloc block CMP.W #$001,D3 ; last entry? BNE.S @1 ; go again if not . . . @2 MOVE.W (A3)+,D3 ; entry start block MOVE.L (A3)+,D4 ; entry logical length MOVE.L (A3)+,D5 ; entry physical length CMP.L D5,D6 ; does entry info jive with map table? BEQ.S @5 ; exit if so MOVE.L D6,-(A3) ; otherwise, use the len from map table BNE.S @3 ; br if length is non-zero CLR.W D3 ; zero start blk if zero length @3 CMP.L D6,D4 ; logical length greater than phys? BLS.S @4 ; br if not MOVE.L D6,D4 ; otherwise, pin it at the phys len @4 MOVE.L D4,-(A3) ; adjusted logical length MOVE.W D3,-(A3) ; and file start block BSET #BufModBit,BufTag(A5) ; mark this block dirty BSET #2,D7 ; bit 2 notes we found a file length prob @5 MOVEM.L (SP)+,D2-D6 ; restore registers RTS ;_______________________________________________________________________ ; ; Routine: QWordSearch ; Arguments: A0.L (input) -- pointer to queue header ; D0.W (input) -- word parameter to look for ; D1.W (input) -- field offset for compare ; A0.L (output) -- pointer to queue element which matched ; D0.W (output) -- 0=success, -1=failure ; D1, other regs are preserved ; Called By: FindDrive,GetVCBDrv,GetVCBRfn ; Function: General queue search routine. ; ; Modification History: ; 06 Dec 82 LAK New today. ;_______________________________________________________________________ QWordSearch MOVE.L D2,-(SP) ; save D2 MOVE.L QHead(A0),D2 ; get first element @1 BEQ.S @4 ; exit if no match MOVE.L D2,A0 ; next element pointer CMP.W 0(A0,D1),D0 ; match? BEQ.S @2 ; then take the good exit . . . MOVE.L QLink(A0),D2 BRA.S @1 @2 MOVEQ #0,D0 @3 MOVE.L (SP)+,D2 ; restore D2 TST.W D0 RTS @4 MOVEQ #-1,D0 BRA.S @3 ;_______________________________________________________________________ ; ; Routine: FindDrive ; Arguments: D2.W (input) -- drive number ; D1.W (output) -- driver refnum for this drive number ; D2.W (output) -- drive number ; D0.W (output) -- error code (no such drive) ; All other registers are preserved. ; Calls: QWordSearch ; Called By: Eject,MountVol ; Function: Given a drive number, this routine returns the RefNum ; for the driver by searching the system drive queue. ; ; Modification History: ; 06 Dec 82 LAK New today. ; 13 Jan 83 LAK Removed block offset - now returns drive number in D2 ; 01 Jun 83 LAK Checks added DQFSID field, reporting ExtFSErr if nonzero ;_______________________________________________________________________ FindDrive MOVE.L A0,-(SP) ; preserve A1 MOVE.W D2,D0 ; search using this drive number as the key LEA DrvQHdr,A0 ; search the queue of drive numbers MOVEQ #DQDrive,D1 ; for drive number BSR.S QWordSearch BNE.S @2 ; exit if not found MOVE DQRefNum(A0),D1 ; return refnum MOVE.W DQFSID(A0),D0 ; is it for us? BEQ.S @1 ; br if so MOVEQ #ExtFSErr,D0 ; otherwise, report an error @1 MOVE.L (SP)+,A0 ; restore A0 RTS @2 MOVEQ #NSDrvErr,D0 ; no such drive number BRA.S @1 ;_______________________________________________________________________ ; ; Routine: OffLine,Eject ; Arguments: A0 (input) -- pointer to volume parameter block, uses IODrvNum, ; IOFileName ; D0 (output) -- error code ; This call is executed synchronously until the eject call is ; actually made. ; Calls: _FlushVol,_Control ; ; Function: The volume in the drive is flushed and its block map and buffer ; block are deallocated. A control call to the disk driver ; is made to eject the diskette. ; ; Modification History: ; 06 Dec 82 LAK New today. ; 13 Jan 83 LAK Disk control call now uses drive number parameter. Completion ; routine is now just CmdDone. ; 01 Jun 83 LAK Changed to just flush the volume before ejecting; VCB ; drive number and driver number are zeroed. ; ; Notes: ; - CmdDone gets result code from D0 and assumes it is being called when the ; first routine on the queue is satisfied, so it all works . . . ; - Should the diskette still be ejected if the flush does not work? ; Should directory writes be verified for paranoid users? ;_______________________________________________________________________ ;jwp OffLine ST NoEject ; eject but don't eject BRA.S Eject1 ; share some code . . . Eject CLR.B NoEject ; want to really eject here . . . Eject1 _FlushVol ; sync up and make sure we're flushed BSR FSQueue ; go through the FS dispatcher for ext fs BSR DtrmV3 ; see if volume is around BEQ.S @2 ; br if so MOVE.W IOVDrvNum(A0),D2 ; drive number @0 BSR FindDrive ; get disk driver refnum in D1 BEQ.S EjectIt @1 BRA CmdDone ; exit if no mapping for this drive ; or vol not on-line or ext fs drive @2 BSR CkExtFS ; external fs? BNE.S @1 ; exit if so . . . LEA VCBDrvNum(A2),A1 MOVE.W (A1),D2 ; on-line, non-ejected? BNE.S @3 ; br if so MOVE.W 2(A1),D2 ; is it already ejected? BPL.S @1 ; just exit if so TST.B NoEject ; no eject? BNE.S @1 ; just exit if so NEG.W D2 ; must be a special on-line-ejected disk NEG.W 2(A1) ; make it not so special BRA.S @0 ; and eject it @3 CLR.W (A1)+ ; zero drive number to mark it offline TST.B NoEject ; offline or eject call? BEQ.S @4 ; br for eject NEG.W D2 ; negate drive number to mark non-eject @4 MOVE.W (A1),D1 ; get driver refnum . . . MOVE.W D2,(A1)+ ; store it here for disk-switch ADDQ #4,A1 ; skip over VCBFSID and VCBVRefNum MOVE.L (A1),A0 ; VCBMAdr _DisposPtr ; dispose volume block map CLR.L (A1)+ MOVE.L (A1),A0 ; VCBBufAdr SUB #10,A0 ; point to start _DisposPtr ; dispose volume buffer CLR.L (A1) ; (neither pointer should be nil already) TST.B NoEject ; eject or offline? BEQ.S EjectIt ; go eject it if eject call MOVE.L A2,A0 ; VCB queue element LEA VCBQHdr,A1 ; VCB queue header _DeQueue ; DeQueue then EnQueue to force VCB to _EnQueue ; the end for round-robin offline scheme BRA.S @1 ; offline br to command done ;jwp EjectIt LEA Params,A0 ; general purpose I/O param block LEA IODrvNum(A0),A1 ; prepare to fill in 3-in-a-row MOVE.W D2,(A1)+ ; IODrvNum (the drive) MOVE.W D1,(A1)+ ; IORefNum (driver handling this drive) MOVE.W #EjectCode,(A1) ; CSCode (eject it!) LEA CmdDone,A1 ; completion routine just says done MOVE.L A1,IOCompletion(A0) _Control ,ASYNC ; asynchronous call RTS ; always return at this level ;_______________________________________________________________________ ; ; Routine: UnMountVol ; Arguments: A0 (input) -- pointer to volume parameter block, uses IODrvNum, ; IOFileName ; D0 (output) -- error code ; This call is executed synchronously. ; Calls: FlushVolume ; ; Function: All files on the volume in the drive are closed and any changed ; directory information is written out to the diskette. Memory ; for the VCB, volume buffer, and block map is deallocated. ; ; Modification History: ; 06 Dec 82 LAK No longer ejects the diskette (This combination function ; is now done by calling Eject). If the default volume is ; unmounted, the default volume pointer is set to the first ; VCB in the VCB queue. ; 21 Dec 82 LAK Rewrote to share code with FlushVol; now calls DtrmVol to ; figure out the VCB pointer. ; ;_______________________________________________________________________ UnMountVol BSR FSQueueSync ; wait until all current calls are done CLR.B FlushOnly ; flushing, closing, and unmounting BRA.S FlUnMnt ; share code with FlushVol ;_______________________________________________________________________ ; ; Routine: FlushVolume ; Arguments: A0 (input) -- VCB pointer to volume to flush ; D0 (output) -- error code ; Calls: FClose,MyWriteDB ; Called By: UnMountVol ; Function: All file buffers on the volume are flushed and any changed ; directory information is written out to the diskette. ; ; Modification History: ; 20 Nov 82 LAK Clears the modified bit after writing out the VCB. ; Removed logic to get rid of a file's own buffer after closing ; it (should be done by close routine; also changing open to ; get a pointer to a buffer from the user -> so user would ; deallocate after a close or an eject . . . ; Removed logic to deallocate the VCB buffers: this is now done ; by unmount volume . . . ; 06 Dec 82 LAK Modified for new file system data structures . . . ; 07 Dec 82 LAK Made into an external procedure. ; 21 Dec 82 LAK Changed to flush file buffers but not close the files; now ; combines code of both unmountvol and flushvol. ; 13 Jan 83 LAK Zeros rest of last block map block before writing it out; ; some cosmetic and minor changes. Fills in IODrvNum field. ; 26 Jan 83 LAK Doesn't read in the master block first anymore; always ; flushes dirty volume buffer. ; 02 Jun 83 LAK Made flush of off-line volume a no-op; also allows unmount ; of an off-line volume. ;_______________________________________________________________________ FlushVolume BSR FSQueue ; wait our turn ST FlushOnly ; only flushing . . . ; first, find the appropriate VCB in our queue FlUnMnt BSR DtrmV3 ; check name, drive number, etc. BNE.S toFlVolXit ; exit if no such volume BSR CkExtFS ; see if it's for an external fs BNE.S toFlVolXit ; exit if so ; scan through the FCBs and flush any files on this volume that are open ; (also close them if we are unmounting the volume . . .) BSR Gt1stFCB CkNxtFCB TST.L FCBFlNm(A1,D1) ; file open? BEQ.S CkNxtF1 ; br if not CMP.L FCBVPtr(A1,D1),A2 ; pointing at this VCB? BNE.S CkNxtF1 ; nope. BSR FClose ; file is on this volume so flush it toFlVolXit BNE.S FlVolExit ; report any errors CkNxtF1 BSR GtNxtFCB ; look at next file BCS.S CkNxtFCB ; until we have looked at them all ; All files on this volume are flushed now. See if volume's info should be ; updated. (map table, number of files) TST.W VCBDrvNum(A2) ; is this volume off-line? BEQ.S FlVolDone ; then no need to flush MOVE.L VCBBufAdr(A2),A5 ; volume buffer address MOVE.W BufTag(A5),D3 ; check our buffer for cleanliness BCLR #15,D3 ; dirty? BEQ.S @2 ; br if not BSR MyWriteDB ; write it out first BNE.S FlVolExit @2 TST.W VCBFlags(A2) ; VCB dirty bit set? BPL.S FlVolDone ; go on, if not WRMDir MOVE.W DrMstrBlk,D3 ; start master block CLR.L BufFNum(A5) ; zero tag fields for master DBs CLR.L BufFBlk(A5) ; We need to write out the block map and relevant VCB info: first ; transfer all directory info from VCB MOVEQ #-1,D0 ; number of words to transfer MOVE.L A5,A1 ; destination is the buffer LEA VCBDInfoSt(A2),A0 ; source is VCB start of directory info MOVE.L Time,VCBLsBkUp(A2) ; use time global var for mod time @1 MOVE.W (A0)+,(A1)+ ; move it DBRA D0,@1 ; now write out the block map: first compute the number of bytes in the table MOVE.L VCBMAdr(A2),A0 ; map address MOVE.W VCBMLen(A2),D2 ; map length MOVEQ #MpTblStrt,D7 ; index into directory block MvMpOut MOVE.W (A0)+,0(A5,D7) ; transfer into buffer area. ADDQ.W #2,D7 ; next word SUBQ.W #2,D2 ; count of how many to go BLE.S MpTblOut ; all done. CMP.W #512,D7 ; at end of disk block? BCS.S MvMpOut ; if not, keep movin' ; write out this block before continuing with rest of map. BSR MyWriteDB ; D3=disk block BNE.S FlVolExit ; ADDQ.W #1,D3 ; next block for table CLR.L D7 ; start at beginning of block BRA.S MvMpOut ; move it out FlVolExit BRA CmdDone ; map is almost out. just need to write this last block (may be the only block) MpTblOut CMP.W #512,D7 ; at end of disk block? BCS.S @1 ; if so, write it out CLR.W 0(A5,D7) ; zero to the end of the block for looks ADDQ.W #2,D7 ; next word BRA.S MpTblOut ; if not, keep movin' @1 BSR MyWriteDB BNE.S FlVolExit FlVolDone CLR.B VCBFlags(A2) ; VCB and block map are no longer dirty TST.B FlushOnly ; only flushing? BNE.S FlVolOK ; br if so ; dispose the block map, VCB, and volume buffer memory TST.W VCBDrvNum(A2) ; are we off-line? BEQ.S @1 ; br if so (just deallocate VCB) BSR DsposMap ; dispose the block map memory BSR DsposBuf ; and the buffer memory @1 MOVE.L A2,A0 ; dequeue this VCB LEA VCBQHdr,A1 ; ptr to VCB queue header JSR DeQueue CMP.L DefVCBPtr,A2 ; did we unmount our default? BNE.S @2 ; br if not CLR.L DefVCBPtr ; if so, no more default @2 _DisposPtr ; dispose VCB and buffer memory FlVolOK MOVEQ #0,D0 ; no error BRA.S FlVolExit ;_______________________________________________________________________ ; ; Routine: CkExtFS ; Arguments: A2.L (input) -- VCB pointer ; D0.W (output) -- 0=belongs to us, ExtFSErr=belongs to another ; Called By: ; Function: Simple routine to separate out calls to be handled by an ; external file system. ; ; Modification History: ; 06 Dec 82 LAK New today. ;_______________________________________________________________________ CkExtFS MOVE.W VCBFSID(A2),D0 ; is this VCB for us? BEQ.S @1 ; br if so MOVEQ #ExtFSErr,D0 @1 RTS ;_______________________________________________________________________ ; ; Routine: GetVCBRfn,GetVCBDrv ; Arguments: D0.W (input) -- IODrvNum(A0) = IOVRefNum(A0) ; D0.W (output) -- error code (no such volume) or 0 ; A2.L (output) -- VCB pointer ; All other regs are preserved ; Calls: QWordSearch ; Called By: DtrmV3,MountVol(make sure no volume already mounted on a drive) ; Function: Determine VCB from DriveNum/VRefNum field. ; ; Modification History: ; 01 Jun 83 LAK New today. ;_______________________________________________________________________ GetVCBRfn MOVEM.L D1/A0,-(SP) MOVEQ #VCBVRefNum,D1 ; looking for VCB by volume refnum BRA.S GetVCB1 GetVCBDrv MOVEM.L D1/A0,-(SP) MOVEQ #VCBDrvNum,D1 ; looking for VCB by drive number GetVCB1 LEA VCBQHdr,A0 ; search the queue of VCBs BSR QWordSearch BNE.S @2 MOVE.L A0,A2 ; return VCB ptr in A2 @1 MOVEM.L (SP)+,D1/A0 RTS @2 MOVEQ #NSVErr,D0 ; no such volume BRA.S @1 ;_______________________________________________________________________ ; ; Routine: DtrmVol,DtrmV1,DtrmV2,DtrmV3 ; Arguments: A0.L (input) -- IOFileName(A0) = ptr to file name ; D0.W (output) -- error code (no such volume) or 0 ; D2.W (output) -- filename length (not including any vol prefix) ; D3.W (output) -- 0 if no vol specified, non-zero otherwise ; A2.L (output) -- VCB pointer ; A4.L (output) -- filename ptr (not including any vol prefix) ; All other regs are preserved ; Makes use of the fact that all IO parameter blocks have ; their filename pointers and drive numbers in the same spot. ; Calls: _CmpString,QWordSearch,GetVCBDrv,GetVCBRfn ; Called By: SetVol,UnMountVol,FlushVol,Open,Create,Delete,Rename, ; GetFileInfo(DtrmV1),GetVolInfo(DtrmV1,DtrmV3) ; Function: Determine what volume is being used in a name. Main entry ; requires a name pointer; DtrmV2 also does, but the pointer is ; passed in D2. DtrmV1 just looks at drive number; DtrmV3 ; looks at name field if not nil, otherwise drive number. ; ; Modification History: ; 02 Dec 82 LAK Changed to set CCR when done. Changed to reflect that volumes ; off-line have a zero VCB pointer. Preserves non-interface ; regs. Searches thru VCB queue as mountVol, unmountVol do. ; 10 Dec 82 LAK Changed to look at drive number field when there is no ; volume prefix. Now uses global string compare proc. ; 13 Dec 82 LAK Added entry point DtrmVol1 for GetFileInfo. ; 13 Jan 83 LAK Calls OS _CmpString proc. Don't ignore parity bit when looking ; for colon in name. ; 23 Jan 83 LAK Changed DtrmV2 entrypoint to have pointer already; report ; error at DtrmVol and DtrmV2 entrys if name ptr is nil; ; deleted DtrmV1 (just call GetDrvVCB). ; 25 May 83 LAK A4 points to first filename byte now. ; 02 Jun 83 LAK Changed to support VRefNum: GetDrvVCB has been factored ; out and made into GetVCBDrv, GetVCBRfn ; ; Test: no vol name, very short vol names, vol name which is only a colon, ; internal-external-no volume,only drive specified ; ;_______________________________________________________________________ ; first, a short routine shared between MountVol, DtrmVol, and GetFileInfo ; (searches VCB queue for an entry with the correct drive number) ; only D0 and A2 are changed . . . DtrmV3 TST.L IOFileName(A0) ; entry pt for flush,set,eject,unmountvol BNE.S DtrmVol ; if ptr non-nil, check for vol prefix DtrmV1 MOVE.W IOVRefNum(A0),D0 ; volume refnum BEQ.S @4 ; br if no drive/vrefnum specified BMI.S @3 ; br if volume refnum BSR.S GetVCBDrv ; try to figure VCB by drive number @1 BNE.S @2 MOVE.L A2,ReqstVol ; save in case of offline, ext fs vols TST.W D0 ; set condition code @2 RTS @3 BSR.S GetVCBRfn ; try to figure VCB by volume refnum BRA.S @1 @4 MOVE.L DefVCBPtr,D0 ; try default VCB pointer BEQ.S @5 ; unless there is no default . . . MOVE.L D0,A2 MOVEQ #0,D0 ; default vol is ok BRA.S @1 @5 MOVEQ #ParamErr,D0 ; no name, no drive, no refnum, no default RTS DtrmVol MOVE.L IOFileName(A0),D2 ; get pointer to 'volume:filename' DtrmV2 MOVEM.L D4-D6/A0-A1/A3/A6,-(SP) ; save regs BEQ.S NoNameErr ; need a name this entrypoint MOVE.L D2,A4 MOVEQ #0,D2 ; clear index into name (long for later add) MOVE.B (A4)+,D2 ; len of file name MOVE.L D2,D3 ; and a copy as a temp BEQ.S NoNameErr ; no zero-length names @1 MOVE.B -1(A4,D3),D4 ; watch for colons in the name CMP.B #$3A,D4 ; ':'? BEQ.S VolSpcfd ; a volume was specified SUBQ.W #1,D3 ; scan entire name from end to front BNE.S @1 ; no volume prefix in the name, so use drvnum/vrefnum field BSR.S DtrmV1 ; go for it (sets up A2,D0) ; exit if vol found or not DtrmDone MOVEM.L (SP)+,D4-D6/A0-A1/A3/A6 ; restore regs TST D0 ; set condition codes RTS NoNameErr MOVEQ #BdNamErr,D0 ; zero name pointer BRA.S DtrmDone ; A volume name was specified. figure out which one (if any) VolSpcfd SUB.L D3,D2 ; file name len excluding volume part MOVE.L A4,A1 ; ptr to 1st char of vol name string ADD.L D3,A4 ; ptr to first char of file name SUBQ.W #1,D3 ; length of vol name less the colon MOVE.L VCBQHdr+QHead,D0 ; search the VCB queue CkVol BEQ.S @2 ; br if end of queue reached MOVE.L D0,A2 ; next VCB pointer LEA VCBVN(A2),A0 ; string pointer MOVEQ #0,D0 MOVE.B (A0)+,D0 ; length of (A0) string SWAP D0 MOVE.W D3,D0 ; length of (A1) string _CmpString ; compare strings @A0, @A1 BNE.S @1 ; br if not equal MOVE.L A2,ReqstVol ; note the requested volume BRA.S DtrmDone ; br if it compares @1 MOVE.L QLink(A2),D0 BNE.S CkVol @2 MOVEQ #NSVErr,D0 BRA.S DtrmDone ;_______________________________________________________________________ ; ; Routine: SetVol ; Arguments: A0.L (input) -- ptr to volume parameter block: volume may be ; specified by either IODrvNum or IOFileName ; D0.W (output) -- error code ; All other regs are preserved ; Calls: DtrmV3 ; Function: Set the current default volume. The volume must be mounted ; and may be specified by either name or drive number. If neither ; is specified, the default volume is set to the current value ; if there is a current default (otherwise an error is reported). ; The volume name may be followed by a filename (for convenience). ; ; Modification History: ; 06 Dec 82 LAK Rewrote to reflect file system data structure changes. ; 14 Jan 83 LAK Minor rework. ; ; Test: existing vol name, illegal vol name, etc. ;_______________________________________________________________________ SetVol BSR FSQueue ; wait for my turn BSR.S DtrmV3 ; determine the volume being asked for BNE.S StVDone MOVE.L A2,DefVCBPtr ; set the (new?) default StVDone BRA CmdDone ;_______________________________________________________________________ ; ; Routine: GetVol ; Arguments: A0.L (input) -- ptr to volume parameter block, uses IOVNPtr, ; IOVDrvNum ; D0.W (output) -- error code ; Calls: FSQueue,CmdDone ; Function: Find out what the name of the default volume is. The volume ; name is stored at IOVNPtr(A0). The maximum length ; of a volume name is 27 bytes. The drive number for the volume ; is also returned. ; ; Modification History: ; 06 Dec 82 LAK Rewrote to reflect file system data structure changes. ; Changed to return name of nth volume (0=default). ; 21 Dec 82 LAK Returns drive number, too. ; 23 Jan 83 LAK Changed to just return the name of the default volume. ; 02 Jun 83 LAK Changed to return volume refnum instead of drive number. ; ; Test: existing vol name, illegal vol name, etc. ;_______________________________________________________________________ GetVol BSR FSQueue ; wait for my turn MOVE.L DefVCBPtr,D1 ; ptr to default VCB BEQ.S NSVErrXit ; error if no default volume MOVE.L D1,A2 BSR.S XferVName GetVolDone BRA CmdDone NSVErrXit MOVEQ #NSVErr,D0 BRA.S GetVolDone XferVName MOVE.W VCBVRefNum(A2),IOVRefNum(A0) ; return volume refnum LEA VCBVN(A2),A2 ; ptr to vol name MOVE.L IOVNPtr(A0),D0 ; ptr to return name buffer BEQ.S @2 ; don't return name if pointer is nil MOVE.L D0,A1 MOVE.B (A2),D0 ; len of volume name @1 MOVE.B (A2)+,(A1)+ ; move a char of name SUBQ.B #1,D0 BCC.S @1 @2 MOVEQ #0,D0 RTS ;_______________________________________________________________________ ; ; Routine: GetVolInfo ; Arguments: A0.L (input) -- I/O volume parameter block: uses all volume ; fields. ; D0.W (output) -- error code ; Calls: FSQueue,CmdDone ; ; Function: Return information about the volume in a mounted drive. ; If the IOVolIndex field is 0, ; the name of the default volume is returned; if non-zero, the ; name of the nth mounted volume is returned. The maximum length ; of a volume name is 27 bytes. The drive number for the volume ; is also returned. ; ; Modification History: ; 07 Dec 82 LAK Changed to support new file system data structures. ; 16 Dec 82 LAK Removed backup file lgth subtract in free blk determination. ; 21 Dec 82 LAK Changed to call DtrmVol to figure the volume name. Free ; blocks comes from VCB info already stored. ; 14 Jan 83 LAK The latest changes. ; 23 Jan 83 LAK Changed to use the volindex field, call XferVName. ; 03 Jun 83 LAK Added in-use bit: true if any files on the volume are open; ; returns volume refnum now instead of drive number. ; ; - if this was the twin of GetFileInfo, the volume really shouldn't have to ; be mounted . . . if it is, get the info from the VCB and block map: if ; not, read the drive's master block into a stack buffer (would have to ; read the block map, tho, to determine the number of free blocks) . . . ; ; - set a bit somewhere if this volume is the default? ;_______________________________________________________________________ IOVDirLen .EQU 34 ; len of GetVolInfo data straight ; from VCB GetVolInfo BSR FSQueue ; queue up the request MOVE.W IOVolIndex(A0),D2 ; if positive, BGT.S @3 ; go search by index BEQ.S @1 ; if zero, go by drive number/default BSR DtrmV3 ; if negative, go by name BRA.S @2 @1 BSR DtrmV1 ; figure by drvnum, vrefnum, or default @2 BNE.S GVIDone BRA.S RetVolInfo @3 MOVE.L VCBQHdr+QHead,D1 ; we want nth VCB in queue @4 BEQ.S NSVErrXit ; exit with err at end of queue SUBQ.W #1,D2 ; the one we want? MOVE.L D1,A2 BEQ.S RetVolInfo ; br if so MOVE.L QLink(A2),D1 ; if not, keep traversing the queue BRA.S @4 ; first copy the heart of the VCB into the parameter block RetVolInfo BSET #6,VCBAtrb+1(A2) ; set if any files are opened BSR.S Gt1stFCB ; get (A1,D1) pointing to first FCB @1 CMP.L FCBVPtr(A1,D1),A2 ; file open on this volume? BEQ.S @2 ; br if so BSR.S GtNxtFCB ; get next one until we run out BCS.S @1 BCLR #6,VCBAtrb+1(A2) ; 0 if no open files match @2 MOVEQ #IOVDirLen-2,D0 ; number of bytes to straight copy @3 MOVE.W VCBCrDate(A2,D0.W),IOVCrDate(A0,D0.W) SUBQ #2,D0 BPL.S @3 ; next, copy the name into the name buffer and get drive number BSR.S XferVName GVIDone BRA CmdDone ;_______________________________________________________________________ ; ; Routine: CVFlgs ; Arguments: A2.L (input) -- VCB pointer ; D0.W (output) -- error code (volume locked, wr prot error) ; Called By: ; Function: Check the volume's flags to see if modify requests are allowed. ; ; Modification History: ; 08 Dec 82 LAK Changed to support new file system data structures. ; ;_______________________________________________________________________ CVFlgs MOVEQ #VLckdErr,D0 ; assume volume locked TST.W VCBAtrb(A2) ; volume locked? BMI.S CVExit ; br if so MOVEQ #WPrErr,D0 ; assume diskette is write protected TST.B VCBAtrb+1(A2) ; bit 7=1 means write protected BMI.S CVExit ; br if so MOVEQ #0,D0 ; otherwise, no error CVExit RTS ; that's all folks (BEQ FOR OK) x; FILE IntHnd.text ; MACWORKS Copy ; .NOLIST .INCLUDE Tlasm-SYSEQU.TEXT .INCLUDE Tlasm-SYSMACS.TEXT .INCLUDE Tlasm-SysErr.Text .INCLUDE Tlasm-Grafequ.Text .LIST .PROC INTHND .DEF VBLHandler .DEF LVL2INT .DEF ExtAInt,ExtBInt .DEF LVL2RTS,Spurious .REF Enqueue,InitQueue .REF DEQUEUE ;_______________________________________________________________________ ; ; Interrupt Dispatchers ; ; This module contains the receivers for all interrupts. Lisa/Macintosh ; uses Rick's drivers to call these routines whenever an interrupt occurs. ; ; MODIFICATION HISTORY: ; ; 13-May-81 AJH added Level 1 Interrupt Handler ; 29-Apr-82 LAK adapted to the SCC hardware, included mouse button handler ; from KBD. ; 04-Jun-82 LAK changed VBL handler to call keyboard task only every 32 interrupts. ; 10-Aug-82 LAK brought deferred task handler here from Dispatch ; 26-Aug-82 LAK updated for 512-dot machine ; 05-Nov-82 LAK only high bit of MBState set now (just a little bit nicer) ; 15-Nov-82 LAK no more deferred task manager ; 16 Feb 83 LAK VBL interrupt dispatcher calls keyboard manager thru KybdTask ; vector. ; 10 Jun 83 LAK Changed level 2 interrupt handler (see comments there). ; 06 Aug 83 LAK Increments random number seed when mouse button changes. ; Saved some code by pointing Lvl1RTS and Lvl2RTS at an ; RTS and Spurious at an RTE. ; 17 Aug 83 LAK Now calls cursor task via a lowmem vector. ; 19 Aug 83 LAK StkLowPt now managed by VBL handler; calls SysError in case ; of stack crashing into heap. ; 30 Jul 84 SC/KWK Doesn't update Mouse if decoupled (journaling fix) ; 11 Oct 84 SC/KWK Forces cursor de-coupling for Rick's drivers ;_______________________________________________________________________ ; ; Level 1 Interrupt Dispatch Table: ; ; LVL1DT -> (00) Old Vertical Blanking Handler ; (04) Old KeyBoard Handler ; (08) Old NMI Handler ; (0C) unused ; (10) unused ; (14) unused ; (18) unused ; (1C) unused ; ; Vertical retrace interrupts are generated once per video frame at the start ; of vertical blanking. These cause the level 1 interrupt receiver to pass ; control to the vertical blanking task manager (VBL manager) which does ; three things: ; ; 1) increment TICKS (tick count of time passage). ; 2) checks mouse button state every other interrupt. ; 3) calls all VBL tasks in the VBL queue which are due ; to execute. ; ; Address registers A0-A3 and data registers D0-D3 are preserved by the interrupt ; handler. Vertical retrace tasks and other Level 1 interrupt handlers may trash ; these registers; if they use any other registers they must preserve them. ; The base address of the VIA is passed in A1 to level 1 interrupt receivers. ; ; If the combined service times of all tasks on a level 1 interrupt ; exceed a video frame time (16 ms), LVL1INT itself may be interrupted by a ; retrace interrupt; in this case the second interrupt will be cleared but ; VBL tasks ignored (i.e., the service routines won't be called). ; ; The interrupting sources are scanned in the order of the table entries, and ; only one dispatch is made per interrupt (even if a lower priority ; source was also interrupting). ; ; The reason for not processing more than one request at a time is ; to allow LVL1INT to be re-entrant. This allows the service routines ; to lower the processor priority as soon as possible in order to ; process other pending interrupts. Of course, this also means that ; a "higher priority" interrupting bit (lower order bit in this case) ; could conceivably lock out processing of the higher order bits if it ; interrupted at a sufficiently high rate, so be careful. ; ; ; Written by Andy Hertzfeld 05-May-81 ; ; MODIFICATION HISTORY: ; ; BLT 22-May-81 - put queue pointer in A0 ; bmw 05-oct-81 - put in deferred stuff ; AJH 31-Oct-81 - hardwired in cursor and keyboard tasks ; LAK 30-Apr-82 - converted to SCC hardware (changed to dispatch all ; VIA interrupts); moved mouse button handler from ; kybd task to here; modified VBL dispatcher to use less code. ; LAK 04-Jun-82 - changed VBL handler to call keyboard task only every 1/2 sec. ; LAK 12-Jan-82 - added 1 second interrupt support (increments Time) ; JWP XX-XXX-83 - extensively re-written for MacWorks ; KWK 18-Jun-84 - fixed VBLHandler reg saving and re-entry exiting probs ; ; to do: there is a potential problem ; if a VBL control block was VREMOVEd while the VBL manager was ; executing - this is only a problem for interrupt routines which ; do VREMOVEs so it can be avoided by making these routines remove ; a VBL block only by letting it lapse. ; ;_______________________________________________________________________ VBLHandler MOVEM.L D0-D3/A0-A3,-(SP) ; preserve registers (kwk) MOVE.L A1,-(SP) ; D0,A0 already saved by Rick. MOVE.L LVL1DT,A0 ; address of Rick's VBL handler JSR (A0) ; go to it ADDQ.L #1,Ticks ; update the 1/60 sec counter ; update the 1-sec counter if necessary CMPI.B #60,Tocks ; has it been a sec? BPL @1 ; ADDQ.B #1,Tocks ; bump tick count BRA WheresMouse @1 CLR.B Tocks ; yes - reset the counter ADDQ.L #1,Time ; another second in a lifetime . . . ; the following stuff is new to accomodate patches to the ROM made by the Mac ; group for the "Alarm Clock" (jwp). BCLR #5,AlarmState ; flag GNEFilter to flash if enabled ; we support alarm clock flash here in the system code: other functions may ; start the flash simply by clearing bit 1 of AlarmState ; ; AlarmState bit 7 = flash parity ; bit 6 = 1 to enable initial beep ; bit 5 = set to 0 every second by this code (flag to GNEFilter) ; bit 1 = 0 to start flashing ; bit 0 = 0 for global flash enable TST.B SPVolCtl ; bit 7 is alarm enable BPL.S WheresMouse ; exit if not enabled MOVE.L SPAlarm,D0 BEQ.S WheresMouse ; exit if no alarm CMP.L Time,D0 ; is it past time? BHI.S WheresMouse ; exit if not ; flash apple menu item if there's a window world BCLR #1,AlarmState ; flag GNEFilter to flash ; this is the end of the new patch stuff. ; If journalling, force RawMouse to be Mouse. In the HWI mouse code, RawMouse is used as the ; 'true' position for mouse movement, then MouseX & MouseY are calculated from it (using ; MouseMask & MouseOffset). This 'adjusted' mouse position is then stuffed into Mouse. WheresMouse MOVE.B CRSRCoupled,CrsrTracking ; make sure Rick knows about cursor de-coupling BNE.S @3 ; cursor NOT decoupled, so don't update MouseX (kwk) MOVE.L Mouse,RawMouse ; uncoupled, so force Rick's constants for journalling @3 ; ; now we service the VBL queue . . . ; BSET #INVBL,QFLAGS+VBLQUEUE ; already in the VBL mgr? BNE VBLEXIT ; if so, skip it this time DoQueue MOVE.L QHEAD+VBLQUEUE,D0 ; fetch VBL header link BEQ.S VBLEXIT1 ; if queue is empty, exit ; Here we update the counter in the vertical retrace control block and ; call the appropriate routine when it times out. A0 points to the current ; block. RVBLOOP MOVE.L D0,A0 ; get pointer to next VBL control block SUBQ #1,VBLCOUNT(A0) ; decrement the counter BNE.S NEXTVBL ; if non-zero, don't bother MOVE.L A0,-(SP) ; preserve A0 (pointer to VBL ctl block) MOVE.L VBLADDR(A0),A1 ; get address of service routine JSR (A1) ; call the routine with A0 pointing to the block MOVE.L (SP)+,A0 ; restore A0 TST VBLCOUNT(A0) ; did the routine reset count? BNE.S NEXTVBL ; if so, we're done ; unlink the element since it timed out LEA VBLQUEUE,A1 ; A1 points to the queue, A0 to the element JSR DEQUEUE ; unlink it NEXTVBL MOVE.L VBLINK(A0),D0 ; advance to next block BNE.S RVBLOOP ; a zero link means we're done VBLEXIT1 BCLR #INVBL,QFLAGS+VBLQUEUE ; we're done! VBLEXIT MOVE.L (SP)+,A1 ; replace saved register (kwk) MOVEM.L (SP)+,D0-D3/A0-A3 ; restore registers (kwk) RTS ; return to level 1 interrupt dispatcher ;-------------------------------------------------------------------- ; ; Level 2 Interrupt Dispatcher: ; ; All SCC interrupts: this level 2 interrupt dispatcher determines the actual ; interrupting source and dispatches through a table of secondary vectors ; maintained in the SYSCOM area. The table looks like this: ; ; LVL2DT -> (00) channel B: transmit buffer empty ; (04) channel B: external status (mouse vertical interrupt) ; (08) channel B: receive character available ; (0C) channel B: special receive condition ; (10) channel A: transmit buffer empty ; (14) channel A: external status (mouse horizontal interrupt) ; (18) channel A: receive character available ; (1C) channel A: special receive condition ; ; LVL2DT contains a long entry-point address for each of the eight primary ; interrupt routines corresponding to the eight primary interrupting sources. ; ; The two external status interrupts may be broken down into the following ; sources by the primary receiver: ; ; zero count (when the SCC baud rate generator is used as a timer) ; DCD (mouse vertical/horizontal) ; sync/hunt (for synchronous serial modes only) ; CTS (external handshake in signal) ; Txunderrun/EOM (for synchronous serial modes only) ; Break/abort (interrupts when break(async)/abort(sync) begins and ends) ; ; A secondary dispatch is made for external/status interrupts through ; the external/status dispatch table: ; ; ExtStsDT -> (00) ext/sts B - non-mouse ; (04) (mouse vertical interrupt) ; (08) ext/sts A - non-mouse ; (0C) (mouse horizontal interrupt) ; ; A check is made to determine whether the mouse input (DCD) has changed ; from the last time: if so, the dispatch is made through the mouse ; vector, if not, thru the non-mouse vector. D0 contains the current ; status (read reg 0) and D1 the changed bits from the previous time ; an extenal/status interrupt was received; a reset ext/sts command is also given. ; ; The two special receive condition interrupts may also be further subdivided, but ; all subdivisions are directly related to serial data transfer and not the mice: ; ; end of frame (synchronous modes) ; CRC/framing error ; receiver overrun ; parity error ; all sent (asynchronous mode only) ; ; Each primary routine is entered with the processor priority = 2, and with ; registers D0-D3 and A0-A3 available for use; A0 will point to SCC channel A/B ; control read address and A1 to SCC channel A/B control write address, ; depending upon which channel is interrupting: ; ; (READ ADDRESS) (WRITE ADDRESS) ; ; CHANNEL A/B DATA 4(A0) 4(A1) ; ; CHANNEL A/B CONTROL (A0) (A1) ; ; Each routine (except for external/status secondary routines) is responsible ; for clearing the source of the interrupt in the SCC, and for saving and ; restoring any additional A or D registers used. ; ; Routines must exit with an RTS rather than an RTE. ; ; ; The interrupt routine is selected by reading the SCC modified interrupt vector. ; The SCC selects the particular vector to supply according to a fixed priority: ; ; Receiver channel A (highest) ; Transmit channel A ; Ext/Status channel A ; Receiver channel B ; Transmit channel B ; Ext/Status channel B (lowest) ; ; The highest priority interrupt which is also enabled is selected by the SCC. ; Only processing one request at a time allows LVL2INT to be re-entrant and service ; routines may lower the processor priority as soon as possible in order to ; process other pending interrupts. ; ; Written by: Bud Tribble 25-Mar-81 ; ; MODIFICATION HISTORY: ; ; 5-oct-81 bmw made it check deferred tasks on exit ; 02-May-82 LAK adapted to the SCC hardware; uses SCC interrupt vector to ; decide which service routine to go to ; 15-Aug-82 LAK A0-A1 point to appropriate port; save more registers; ; put in secondary ext/sts dispatch routine ; 10-Jun-83 LAK Added delay between SCC write and read (was only 6 pclks + 50ns) ; by loading 'and' mask before reading SCC interrupt vector. ; Changed ext int handler to ignore mouse movements when any ; other external condition has happened. ; ; - could skip the sync-read to speed things up a bit; also rely on a good ; vector (don't 'and' it), save fewer registers, don't adjust for chan A, ; MOVEM to get SCC addresses . . . ;_______________________________________________________________________ Lvl2Int MOVEM.L D0-D3/A0-A3,-(SP) ; preserve registers MOVE.L SCCRd,A0 ; get SCC read address MOVE.B (A0),D0 ; read to sync up SCC (just to be safe) MOVE.L SCCWr,A1 ; get SCC channel B write control address MOVE.B #2,(A1) ; point to SCC register 2, B channel LEA Lvl2DT,A2 ; point to dispatch table and delay MOVEQ #$0E,D0 ; 'and mask' and extra delay AND.B (A0),D0 ; read the modified interrupt vector CMPI.B #8,D0 ; channel A interrupt? BLT.S GoLvl2 ; branch if for B ADDQ #2,A0 ; adjust SCC addresses for port A ADDQ #2,A1 GoLvl2 ADD D0,D0 ; double vector for dispatch MOVE.L 0(A2,D0.W),A2 ; get dispatch vector JSR (A2) ; call service routine MOVEM.L (SP)+,D0-D3/A0-A3 ; restore registers Spurious ; point spurious at an RTE RTE ; and return from interrupt Lvl2RTS RTS ;_______________________________________________________________ ; ; Secondary Dispatch routines for SCC external status interrupts ;_______________________________________________________________ ExtBInt LEA SCCBSts,A3 ; old register value LEA ExtStsDT,A2 ; point to channel B dispatch vectors BRA.S ExtABInt ; share some code ExtAInt LEA SCCASts,A3 ; old register value LEA ExtStsDT+8,A2 ; point to channel A dispatch vectors ExtABInt MOVE.B (A0),D0 ; read SCC status channel A MOVE.B (A3),D1 ; get old status EOR.B D0,D1 ; get changed bits MOVE.B D0,(A3) ; update the old MOVE.B #$10,(A1) ; clear ext/status interrupts in chan CMP.B #$08,D1 ; only a mouse movement? BNE.S @1 ; branch if not ADDQ #4,A2 ; skip to mouse vert/horiz addr @1 MOVE.L (A2),A2 ; get the vector JMP (A2) ; go to it .END ; FILE IntHnd.text ; MACWORKS Copy ; .NOLIST .INCLUDE Tlasm-SYSEQU.TEXT .INCLUDE Tlasm-SYSMACS.TEXT .INCLUDE Tlasm-SysErr.Text .INCLUDE Tlasm-Grafequ.Text .LIST .PROC INTHND .DEF VBLHandler .DEF LVL2INT .DEF ExtAInt,ExtBInt .DEF LVL2RTS,Spurious .REF Enqueue,InitQueue .REF DEQUEUE ;_______________________________________________________________________ ; ; Interrupt Dispatchers ; ; This module contains the receivers for all interrupts. Lisa/Macintosh ; uses Rick's drivers to call these routines whenever an interrupt occurs. ; ; MODIFICATION HISTORY: ; ; 13-May-81 AJH added Level 1 Interrupt Handler ; 29-Apr-82 LAK adapted to the SCC hardware, included mouse button handler ; from KBD. ; 04-Jun-82 LAK changed VBL handler to call keyboard task only every 32 interrupts. ; 10-Aug-82 LAK brought deferred task handler here from Dispatch ; 26-Aug-82 LAK updated for 512-dot machine ; 05-Nov-82 LAK only high bit of MBState set now (just a little bit nicer) ; 15-Nov-82 LAK no more deferred task manager ; 16 Feb 83 LAK VBL interrupt dispatcher calls keyboard manager thru KybdTask ; vector. ; 10 Jun 83 LAK Changed level 2 interrupt handler (see comments there). ; 06 Aug 83 LAK Increments random number seed when mouse button changes. ; Saved some code by pointing Lvl1RTS and Lvl2RTS at an ; RTS and Spurious at an RTE. ; 17 Aug 83 LAK Now calls cursor task via a lowmem vector. ; 19 Aug 83 LAK StkLowPt now managed by VBL handler; calls SysError in case ; of stack crashing into heap. ; 30 Jul 84 SC/KWK Doesn't update Mouse if decoupled (journaling fix) ; 11 Oct 84 SC/KWK Forces cursor de-coupling for Rick's drivers ;_______________________________________________________________________ ; ; Level 1 Interrupt Dispatch Table: ; ; LVL1DT -> (00) Old Vertical Blanking Handler ; (04) Old KeyBoard Handler ; (08) Old NMI Handler ; (0C) unused ; (10) unused ; (14) unused ; (18) unused ; (1C) unused ; ; Vertical retrace interrupts are generated once per video frame at the start ; of vertical blanking. These cause the level 1 interrupt receiver to pass ; control to the vertical blanking task manager (VBL manager) which does ; three things: ; ; 1) increment TICKS (tick count of time passage). ; 2) checks mouse button state every other interrupt. ; 3) calls all VBL tasks in the VBL queue which are due ; to execute. ; ; Address registers A0-A3 and data registers D0-D3 are preserved by the interrupt ; handler. Vertical retrace tasks and other Level 1 interrupt handlers may trash ; these registers; if they use any other registers they must preserve them. ; The base address of the VIA is passed in A1 to level 1 interrupt receivers. ; ; If the combined service times of all tasks on a level 1 interrupt ; exceed a video frame time (16 ms), LVL1INT itself may be interrupted by a ; retrace interrupt; in this case the second interrupt will be cleared but ; VBL tasks ignored (i.e., the service routines won't be called). ; ; The interrupting sources are scanned in the order of the table entries, and ; only one dispatch is made per interrupt (even if a lower priority ; source was also interrupting). ; ; The reason for not processing more than one request at a time is ; to allow LVL1INT to be re-entrant. This allows the service routines ; to lower the processor priority as soon as possible in order to ; process other pending interrupts. Of course, this also means that ; a "higher priority" interrupting bit (lower order bit in this case) ; could conceivably lock out processing of the higher order bits if it ; interrupted at a sufficiently high rate, so be careful. ; ; ; Written by Andy Hertzfeld 05-May-81 ; ; MODIFICATION HISTORY: ; ; BLT 22-May-81 - put queue pointer in A0 ; bmw 05-oct-81 - put in deferred stuff ; AJH 31-Oct-81 - hardwired in cursor and keyboard tasks ; LAK 30-Apr-82 - converted to SCC hardware (changed to dispatch all ; VIA interrupts); moved mouse button handler from ; kybd task to here; modified VBL dispatcher to use less code. ; LAK 04-Jun-82 - changed VBL handler to call keyboard task only every 1/2 sec. ; LAK 12-Jan-82 - added 1 second interrupt support (increments Time) ; JWP XX-XXX-83 - extensively re-written for MacWorks ; KWK 18-Jun-84 - fixed VBLHandler reg saving and re-entry exiting probs ; ; to do: there is a potential problem ; if a VBL control block was VREMOVEd while the VBL manager was ; executing - this is only a problem for interrupt routines which ; do VREMOVEs so it can be avoided by making these routines remove ; a VBL block only by letting it lapse. ; ;_______________________________________________________________________ VBLHandler MOVEM.L D0-D3/A0-A3,-(SP) ; preserve registers (kwk) MOVE.L A1,-(SP) ; D0,A0 already saved by Rick. MOVE.L LVL1DT,A0 ; address of Rick's VBL handler JSR (A0) ; go to it ADDQ.L #1,Ticks ; update the 1/60 sec counter ; update the 1-sec counter if necessary CMPI.B #60,Tocks ; has it been a sec? BPL @1 ; ADDQ.B #1,Tocks ; bump tick count BRA WheresMouse @1 CLR.B Tocks ; yes - reset the counter ADDQ.L #1,Time ; another second in a lifetime . . . ; the following stuff is new to accomodate patches to the ROM made by the Mac ; group for the "Alarm Clock" (jwp). BCLR #5,AlarmState ; flag GNEFilter to flash if enabled ; we support alarm clock flash here in the system code: other functions may ; start the flash simply by clearing bit 1 of AlarmState ; ; AlarmState bit 7 = flash parity ; bit 6 = 1 to enable initial beep ; bit 5 = set to 0 every second by this code (flag to GNEFilter) ; bit 1 = 0 to start flashing ; bit 0 = 0 for global flash enable TST.B SPVolCtl ; bit 7 is alarm enable BPL.S WheresMouse ; exit if not enabled MOVE.L SPAlarm,D0 BEQ.S WheresMouse ; exit if no alarm CMP.L Time,D0 ; is it past time? BHI.S WheresMouse ; exit if not ; flash apple menu item if there's a window world BCLR #1,AlarmState ; flag GNEFilter to flash ; this is the end of the new patch stuff. ; If journalling, force RawMouse to be Mouse. In the HWI mouse code, RawMouse is used as the ; 'true' position for mouse movement, then MouseX & MouseY are calculated from it (using ; MouseMask & MouseOffset). This 'adjusted' mouse position is then stuffed into Mouse. WheresMouse MOVE.B CRSRCoupled,CrsrTracking ; make sure Rick knows about cursor de-coupling BNE.S @3 ; cursor NOT decoupled, so don't update MouseX (kwk) MOVE.L Mouse,RawMouse ; uncoupled, so force Rick's constants for journalling @3 ; ; now we service the VBL queue . . . ; BSET #INVBL,QFLAGS+VBLQUEUE ; already in the VBL mgr? BNE VBLEXIT ; if so, skip it this time DoQueue MOVE.L QHEAD+VBLQUEUE,D0 ; fetch VBL header link BEQ.S VBLEXIT1 ; if queue is empty, exit ; Here we update the counter in the vertical retrace control block and ; call the appropriate routine when it times out. A0 points to the current ; block. RVBLOOP MOVE.L D0,A0 ; get pointer to next VBL control block SUBQ #1,VBLCOUNT(A0) ; decrement the counter BNE.S NEXTVBL ; if non-zero, don't bother MOVE.L A0,-(SP) ; preserve A0 (pointer to VBL ctl block) MOVE.L VBLADDR(A0),A1 ; get address of service routine JSR (A1) ; call the routine with A0 pointing to the block MOVE.L (SP)+,A0 ; restore A0 TST VBLCOUNT(A0) ; did the routine reset count? BNE.S NEXTVBL ; if so, we're done ; unlink the element since it timed out LEA VBLQUEUE,A1 ; A1 points to the queue, A0 to the element JSR DEQUEUE ; unlink it NEXTVBL MOVE.L VBLINK(A0),D0 ; advance to next block BNE.S RVBLOOP ; a zero link means we're done VBLEXIT1 BCLR #INVBL,QFLAGS+VBLQUEUE ; we're done! VBLEXIT MOVE.L (SP)+,A1 ; replace saved register (kwk) MOVEM.L (SP)+,D0-D3/A0-A3 ; restore registers (kwk) RTS ; return to level 1 interrupt dispatcher ;-------------------------------------------------------------------- ; ; Level 2 Interrupt Dispatcher: ; ; All SCC interrupts: this level 2 interrupt dispatcher determines the actual ; interrupting source and dispatches through a table of secondary vectors ; maintained in the SYSCOM area. The table looks like this: ; ; LVL2DT -> (00) channel B: transmit buffer empty ; (04) channel B: external status (mouse vertical interrupt) ; (08) channel B: receive character available ; (0C) channel B: special receive condition ; (10) channel A: transmit buffer empty ; (14) channel A: external status (mouse horizontal interrupt) ; (18) channel A: receive character available ; (1C) channel A: special receive condition ; ; LVL2DT contains a long entry-point address for each of the eight primary ; interrupt routines corresponding to the eight primary interrupting sources. ; ; The two external status interrupts may be broken down into the following ; sources by the primary receiver: ; ; zero count (when the SCC baud rate generator is used as a timer) ; DCD (mouse vertical/horizontal) ; sync/hunt (for synchronous serial modes only) ; CTS (external handshake in signal) ; Txunderrun/EOM (for synchronous serial modes only) ; Break/abort (interrupts when break(async)/abort(sync) begins and ends) ; ; A secondary dispatch is made for external/status interrupts through ; the external/status dispatch table: ; ; ExtStsDT -> (00) ext/sts B - non-mouse ; (04) (mouse vertical interrupt) ; (08) ext/sts A - non-mouse ; (0C) (mouse horizontal interrupt) ; ; A check is made to determine whether the mouse input (DCD) has changed ; from the last time: if so, the dispatch is made through the mouse ; vector, if not, thru the non-mouse vector. D0 contains the current ; status (read reg 0) and D1 the changed bits from the previous time ; an extenal/status interrupt was received; a reset ext/sts command is also given. ; ; The two special receive condition interrupts may also be further subdivided, but ; all subdivisions are directly related to serial data transfer and not the mice: ; ; end of frame (synchronous modes) ; CRC/framing error ; receiver overrun ; parity error ; all sent (asynchronous mode only) ; ; Each primary routine is entered with the processor priority = 2, and with ; registers D0-D3 and A0-A3 available for use; A0 will point to SCC channel A/B ; control read address and A1 to SCC channel A/B control write address, ; depending upon which channel is interrupting: ; ; (READ ADDRESS) (WRITE ADDRESS) ; ; CHANNEL A/B DATA 4(A0) 4(A1) ; ; CHANNEL A/B CONTROL (A0) (A1) ; ; Each routine (except for external/status secondary routines) is responsible ; for clearing the source of the interrupt in the SCC, and for saving and ; restoring any additional A or D registers used. ; ; Routines must exit with an RTS rather than an RTE. ; ; ; The interrupt routine is selected by reading the SCC modified interrupt vector. ; The SCC selects the particular vector to supply according to a fixed priority: ; ; Receiver channel A (highest) ; Transmit channel A ; Ext/Status channel A ; Receiver channel B ; Transmit channel B ; Ext/Status channel B (lowest) ; ; The highest priority interrupt which is also enabled is selected by the SCC. ; Only processing one request at a time allows LVL2INT to be re-entrant and service ; routines may lower the processor priority as soon as possible in order to ; process other pending interrupts. ; ; Written by: Bud Tribble 25-Mar-81 ; ; MODIFICATION HISTORY: ; ; 5-oct-81 bmw made it check deferred tasks on exit ; 02-May-82 LAK adapted to the SCC hardware; uses SCC interrupt vector to ; decide which service routine to go to ; 15-Aug-82 LAK A0-A1 point to appropriate port; save more registers; ; put in secondary ext/sts dispatch routine ; 10-Jun-83 LAK Added delay between SCC write and read (was only 6 pclks + 50ns) ; by loading 'and' mask before reading SCC interrupt vector. ; Changed ext int handler to ignore mouse movements when any ; other external condition has happened. ; ; - could skip the sync-read to speed things up a bit; also rely on a good ; vector (don't 'and' it), save fewer registers, don't adjust for chan A, ; MOVEM to get SCC addresses . . . ;_______________________________________________________________________ Lvl2Int MOVEM.L D0-D3/A0-A3,-(SP) ; preserve registers MOVE.L SCCRd,A0 ; get SCC read address MOVE.B (A0),D0 ; read to sync up SCC (just to be safe) MOVE.L SCCWr,A1 ; get SCC channel B write control address MOVE.B #2,(A1) ; point to SCC register 2, B channel LEA Lvl2DT,A2 ; point to dispatch table and delay MOVEQ #$0E,D0 ; 'and mask' and extra delay AND.B (A0),D0 ; read the modified interrupt vector CMPI.B #8,D0 ; channel A interrupt? BLT.S GoLvl2 ; branch if for B ADDQ #2,A0 ; adjust SCC addresses for port A ADDQ #2,A1 GoLvl2 ADD D0,D0 ; double vector for dispatch MOVE.L 0(A2,D0.W),A2 ; get dispatch vector JSR (A2) ; call service routine MOVEM.L (SP)+,D0-D3/A0-A3 ; restore registers Spurious ; point spurious at an RTE RTE ; and return from interrupt Lvl2RTS RTS ;_______________________________________________________________ ; ; Secondary Dispatch routines for SCC external status interrupts ;_______________________________________________________________ ExtBInt LEA SCCBSts,A3 ; old register value LEA ExtStsDT,A2 ; point to channel B dispatch vectors BRA.S ExtABInt ; share some code ExtAInt LEA SCCASts,A3 ; old register value LEA ExtStsDT+8,A2 ; point to channel A dispatch vectors ExtABInt MOVE.B (A0),D0 ; read SCC status channel A MOVE.B (A3),D1 ; get old status EOR.B D0,D1 ; get changed bits MOVE.B D0,(A3) ; update the old MOVE.B #$10,(A1) ; clear ext/status interrupts in chan CMP.B #$08,D1 ; only a mouse movement? BNE.S @1 ; branch if not ADDQ #4,A2 ; skip to mouse vert/horiz addr @1 MOVE.L (A2),A2 ; get the vector JMP (A2) ; go to it .END ; 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 ןss  Untitledwwwuussssssssswuuwssssuuuuuuu u u u u u u u u u  u  u  u  u  u u u u u!u!u!u"u"u"u"u"u"u"u" u" u" u" u" u"u"u"u"u"u2u2u2u2u2u2u2u3u3 u3 u3 u3 u3 u3u3u3u3u3u3u3u3u3u3u3u3u3u3u3u3u3u3u3 u3!u3"u3#u3$u3%u3&u3'u4(u4)u4*u4+u4,u4-u4.u4/u40u41u42u43u44u45u46u47u48u49u4:u4;u4u4?u4@u4Au4Bu4Cu4Du4Eu4Fu4Gu4Hu4Iu4Ju5Ku5Lu5Mu5Nu5Ou5Pu5Qu5Ru5Su5Tu5Uu5Vu5Wu5Xu5Yu5Zu5[u5\u5]u5^u5_u5`u5au5bu5cu5du5eu5fu5gu5hu5iu5ju5ku6u9u9u9u9u9u:u:u:u: u: u: u: u: u:u:u:u:u:u:u:u:u:u:u:u:u:u:u:u:u:u:u: u:!u:"u:#u:$u;%u;&u;'u;(u;)u;*u;+u;,u;-u;.u;/u;0u;1u;2u;3u;4u; u= u= u= u= u= u= u> u> u> u> uG uH uH uH uH uI uI uI uI uI uI  uI  uI  uI  uI  uI uI uI uI uI uI uI uI uI uI uJ uJ uJ uJ uJ uJ uJ uJ uJ  uJ  uJ  uJ  uJ  uK uK uK uK uK uK uK uK uK uK uK uK uK uK uK uK uK uK uK  uK !uK "uK #uK u[ u\ u\ u\ u\ u\ u\ u\ u\  u\  u\  u\  u\  u\ u\ u\ u\ u\ u\ u\ u\ u\ u\ u\ u\ u\ u] u] u] u]u^u^u^u^u^u^u^u^u^ u^ u^ u^ u^ u^u^u^u^u^u^u^u^u^u^u_u_u_u_u_u_u_u_u_ u_!u_"u_#u_$u_%u_&u_'u_uauauauauauauauaua ua ua ua ua uauauaubububububububububububububububub ub!ub"ub#ub$ub%ub&ub'ub(ub)ub*ub+ub,ub-ub.ub/ub0ub1uc2uc3uc4uc5uc6uc7uc8uc9uc:uc;ucuc?ucuuuvuvuvuvuvuvuvuv uv uv uv uv uvuvuvuvuvuvuvuvuvuvuvuvuwuwuwuwuwuwuw uw!uw"uw#uw$uw%uw&uw'uw(uw)uw*uw+uw,uwuyuyuyuyuyuyuyuyuy uy uy uy uy uyuyuyuyuyuyuyuyuyuyuyuyuzuzuzuzuzuzuz uz!uz"uz#uz$uz%uzu{u|u|u|u|u|u|u|u| u| u| u| u| u|u|u|u|u|u|u|u|u|u|u|u|u|u|u}u}u}u}u} u}!u}"u}#u}$u}%u}&u}'u}(u})u}*u}+u},u}-u}.u}/u}0u}1u}2u}3u}4u}5u}6u}7u}8u}9u}:u};u}u~?u~@u~Au~Bu~Cu~Du~Eu~Fu~Gu~Hu~Iu~Ju~Ku~Lu~Mu~Nu~Ou~Pu~Qu~Ru~Su~Tu~Uu~Vu~Wu~Xu~Yu~Zu[u\u]u^u_u`uaubucudueufuguuuuuuuuuu u u u u uuuuuuuuuuuuuuuuuuu uuuuuuuuuu u u u u uuuuuuuuuuuuuuuuuuu uuuuuuuuuu u u u u uuuuuuuuuuuuuuuuuuu u!u"u#u$u%u&u'u(u)u*u+u,u-u.u/u0u1u2u3u4u5u6u7u8u9u:u;uu?u@uAuBuCuDuEuFuGuHuIuJuKuLuMuNuOuPuQuRus