5 ; Label: "The DOS SHARE Utility"
6 ; "Version 4.00 (C) Copyright 1988 Microsoft"
7 ; "Licenced Material - Program Property of Microsoft"
9 ;******************* END OF SPECIFICATIONS *************************************
27 AsmVars
<IBM
, Installed
>
29 Installed
= TRUE
; for installed version
35 mov si,OFFSET DOSGROUP
:val
44 ;---------------------------------------
45 ; if we are installed, then define the
46 ; base code segment of the sharer first
47 ;---------------------------------------
52 Share
SEGMENT BYTE PUBLIC 'SHARE'
57 ;---------------------------------------
58 ; include the rest of the segment
59 ; definitions for normal msdos
61 ; segment ordering for MSDOS
63 ;---------------------------------------
68 extrn DataVersion
:BYTE ; version number of DOS data.
69 extrn JShare
:BYTE ; location of DOS jump table.
70 extrn sftFCB
:DWORD ; [SYSTEM] pointer to FCB cache table
71 extrn KeepCount
:WORD ; [SYSTEM] LRU count for FCB cache
78 extrn ThisSFT
:DWORD ; pointer to SFT entry
79 extrn WFP_start
:WORD ; pointer to name string
99 ;---------------------------------------
100 ; if we are not installed, then the
101 ; code here is just part of the normal
102 ; MSDOS code segment otherwise,
103 ; define our own code segment
104 ;---------------------------------------
109 CODE SEGMENT BYTE PUBLIC 'CODE'
111 ASSUME
SS:DOSGROUP
,CS:DOSGROUP
115 Share
SEGMENT BYTE PUBLIC 'SHARE'
117 ASSUME
SS:DOSGROUP
,CS:SHARE
121 Extrn FreLock
:WORD,Serial
:WORD
122 Extrn MFT_Enter
:NEAR,MFTClose
:NEAR,MFTClu
:NEAR,MFTCloseP
:NEAR
124 Extrn Set_Mult_Block
:NEAR,Clr_Mult_Block
:NEAR,Chk_Block
:NEAR
131 BREAK <FNM
- Find name
in MFT
>
133 ;******************* START OF SPECIFICATIONS ***********************************
135 ; FNM - Find name in MFT
137 ; FNM searches the MFT for a name record.
139 ; ENTRY (DS:SI) = pointer to name string (.asciz)
140 ; (al) = 1 to create record if non exists
142 ; EXIT 'C' clear if found or created
143 ; (DS:BX) = address of MFT name record
145 ; If not to create, item not found
147 ; If to create, am out of space
151 ;******************* END OF SPECIFICATIONS *************************************
155 push ds ; save string address
157 xchg bh,al ; (bh) = create flag
158 or bh,bh ; if not creating
159 jz fnm01
; skip sft test
161 ;---------------------------------------
162 ; run down through string counting
164 ;---------------------------------------
166 fnm01: sub dx,dx ; (dx) = byte count
167 sub bl,bl ; (bl) = sum
169 fnm1: lodsb ; (al) = next char
174 jnz fnm1
; terminate after null char
176 ;---------------------------------------
178 ; Start searching name list
183 ; (TOS+2:TOS) = name string address
184 ;---------------------------------------
190 fnm2: cmp [si].mft_flag
,MFLG_FRE
191 jl fnm10
; at end - name not found
192 jz fnm4
; is free, just skip it
193 cmp bl,[si].mft_sum
; do sums compare?
194 jz fnm5
; its a match - look further
195 fnm4: add si,[si].mft_len
; not a match... skip it
197 ;---------------------------------------
198 ; name checksums match
199 ; - compare the actual strings
202 ; (ds:si = MFT address
206 ; (TOS+2:TOS) = name string address
207 ;---------------------------------------
209 fnm5: mov cx,dx ; (cx) = length to match
211 pop es ; (ES:DI) = fba given name
214 push si ; save MFT offset
215 add si,mft_name
; (ds:si) = fwa string in record
217 pop si ; (ds:si) = fwa name record
218 jnz fnm4
; not a match
220 ;---------------------------------------
221 ; Yes, we've found it. Return the info
223 ; (TOS+2:TOS) = name string address
224 ;---------------------------------------
226 fmt TypShare
,LevMFTSrch
,<"FNM found name record at $x\n">,<si>
227 pop ax ; discard unneeded stack stuff
229 mov bx,si ; (ds:bx) = fwa name record
233 ;---------------------------------------
235 ;** Its not in the list
236 ;** - lets find a free spot and put
241 ; (dx) = string length
242 ; (TOS+2:TOS) = ASCIZ string address
244 ;---------------------------------------
247 jnz fnm10$5
; yes, insert it
249 pop ds ; no insert, its a "not found"
252 fmt TypShare
,LevMFTSrch
,<"FNM failing\n">
254 mov ax,error_path_not_found
259 add dx,mft_name
; (dx) = minimum space needed
263 fnm11: cmp [si].mft_flag
,MFLG_FRE
266 jl fnm20
; at END, am out of space
271 jz fnm12
; is a free record
272 add si,[si].mft_len
; skip name record
279 fnm12: mov ax,[si].mft_len
; Have free record, (ax) = total length
281 jnc fnm13
; big enough
283 JMP SHORT fnm11
; not large enough - move on
285 ;---------------------------------------
286 ; OK, we have a record which is big
287 ; enough. If its large enough to hold
288 ; another name record of 6 characters
289 ; than we'll split the block, else
290 ; we'll just use the whole thing
292 ; (ax) = size of free record
294 ; (ds:si) = address of free record
296 ; (TOS+2:TOS) = name string address
297 ;---------------------------------------
299 fnm13: sub ax,dx ; (ax) = total size of proposed fragment
301 jc fnm14
; not big enough to split
302 push bx ; save sum byte
303 mov bx,dx ; (bx) = offset to start of new name record
304 mov [bx][si].mft_flag
,MFLG_FRE
305 mov [bx][si].mft_len
,ax ; setup tail as free record
306 sub ax,ax ; don't extend this record
307 pop bx ; restore sum byte
308 fnm14: add dx,ax ; (dx) = total length of this record
311 mov [si].mft_flag
,MFLG_NAM
313 fmt TypShare
,LevMFTSrch
,<"FNM creating record at $x\n">,<si>
316 pop es ; (es) = MFT segment for "stow"
320 stosw ; zero LCK pointer
321 ERRNZ mft_sptr
-mft_lptr
-2
322 ; add di,mft_sptr-mft_lptr-2
323 stosw ; zero SFT pointer
324 stosw ; zero SFT pointer
325 inc serial
; bump serial number
327 ERRNZ mft_serl
-mft_sptr
-4
328 ; ADD di,mft_serl-mft_sptr-4
330 ;---------------------------------------
331 ; We're all setup except for the name.
332 ; Note that we'll block copy the whole
333 ; name field, even though the name may
334 ; be shorter than that (we may have
335 ; declined to fragment this memory block)
337 ; (dx) = total length of this record
338 ; (ds:si) = address of working record
340 ; (TOS+2:TOS) = name string address
341 ;---------------------------------------
343 sub cx,mft_name
; compute total size of name area
344 ERRNZ mft_name
-mft_serl
-2
345 ; add di,mft_name-mft_serl-2 ; (ES:DI) = target address
346 mov ax,si ; save name record offset
350 mov bx,ax ; (bx) = name record offset
352 pop ds ; (DS:BX) = name record offset
358 ;** OUT OF FREE SPACE
360 ;** This is tough, folks. Lets trigger a garbage collection and see if
361 ;** there is enough room. If there is, we'll hop back and relook for a
362 ;** free hunk; if there isnt enough space, its error-city!
364 ; WARNING: it is important that the garbage collector be told how big a
365 ; name record hole we're looking for... if the size given GCM
366 ; is too small we'll loop doing "no space; collect; no space;
369 ; (dx) = total length of desired name record
372 ; (TOS+2:TOS) = name string address
375 mov ax,dx ; (ax) = size wanted
376 sub dx,mft_name
; (dx) = string length for reentry at fnm10
379 call GCM
; garbage collect MFT
386 jnc fnm10
; go back and find that space
389 ;---------------------------------------
390 ; no space, return w/error
391 ;---------------------------------------
395 mov ax,error_sharing_buffer_exceeded
406 BREAK <GCM
- Garbage Collect MFT
>
408 ;******************* START OF SPECIFICATIONS ***********************************
410 ; GCM - Garbage Collect MFT
412 ; GCM runs down the MFT structure squeezing out the free space and
413 ; putting it into one free block at the end. This is a traditional heap
414 ; collection process. We must be sure to update the pointer in the
415 ; SFTs. This presumes no adjacent free blocks.
417 ; ENTRY (ax) = space desired in last free block
419 ; EXIT 'C' clear if enough space in block
420 ; 'C' set if not enough space
422 ;******************* END OF SPECIFICATIONS *************************************
426 push ax ; save target
427 off
si,mft
; (si) = from pointer
428 mov di,si ; (di) = to pointer
430 ;---------------------------------------
431 ; (DI) points to the beginning of
433 ; (SI) points to the next block.
434 ;---------------------------------------
436 gcm1: mov cx,[si].mft_len
; (cx) = size of whatever it is
437 cmp [si].mft_flag
,MFLG_FRE
438 jl gcm10
; END marker
439 jnz gcm2
; have a name record
441 ;---------------------------------------
442 ; (SI) points to a free block.
443 ; We coalesce it by changing the size.
444 ;---------------------------------------
446 jz gcm15
; do NOT coalesce a block with itself
447 add [di].mft_len
,cx ; coalesce
449 add si,cx ; skip the empty one
451 ;---------------------------------------
452 ; (SI) points to a non-free,
454 ; (DI) points to the beginning of a
457 ; We move the non-free block down over
459 ;---------------------------------------
461 jnz gcm3
; have to copy
463 ;---------------------------------------
464 ; SI = DI => we are at a boundary
465 ; between allocated blocks.
467 ;---------------------------------------
469 mov di,si ; no emptys yet... no need to copy
471 ;---------------------------------------
472 ; CX is length of allocated block.
474 ;---------------------------------------
476 gcm3: mov bx,di ; (DS:BX) = new home for this record
480 ;---------------------------------------
481 ; We've moved the record, now fix up
482 ; the pointers in the SFT chain
484 ; (si) = address of next record
485 ; (di) = address of next free byte
486 ; (bx) = address of record in its new home
487 ; (TOS) = needed space
488 ;---------------------------------------
491 lds di,[bx].mft_sptr
; (ds:di) = chain of SFT
493 jz gcm5
; no more SFT
494 mov [di].sf_mft
,bx ; install new MFT position
495 lds di,[di].sf_chain
; link to next
496 JMP gcm4
; fix next SFT
500 ;---------------------------------------
501 ; (DI) points to beginning of
502 ; new free record (moved)
503 ; (SI) points to next record
505 ; Make sure that the (DI) record
507 ;---------------------------------------
509 mov [di].mft_flag
,MFLG_FRE
; indicate free record
510 mov [di].mft_len
,si ; calculate correct length
512 ;---------------------------------------
513 ; MFT now has correct record structure.
514 ; Go find more free blocks
515 ;---------------------------------------
517 ;---------------------------------------
518 ; We have scanned the entire table,
519 ; compacting all empty records together.
521 ; (di) = first free byte in table
522 ; (si) = address of END record
523 ; (TOS) = size needed
525 ; Be extra careful!!!
526 ;---------------------------------------
528 sub ax,di ; (ax) = free space
529 pop bx ; (bx) = space wanted
536 BREAK <RMN
- Remove MFT Name record
>
538 ;******************* START OF SPECIFICATIONS ***********************************
540 ; RMN - Remove MFT Name record
542 ; RMN removes a name record from the MFT list. The record is marked
543 ; free and all free space is coalesced.
545 ; ENTRY (DS:BX) = FBA MFT name record
546 ; EXIT to INTERR if lock and SFT chains are not empty
549 ;******************* END OF SPECIFICATIONS *************************************
554 mov ax,word ptr [si].mft_sptr
555 or ax,word ptr [si].mft_lptr
556 jnz RMNIER1
; not clean - internal error
557 mov si,bx ; (ds:si) = fwa name record
559 mov [si].mft_flag
,MFLG_FRE
; mark free
561 call mrg
; coalesce all free space
568 RMNIER: call INTERR
; internal error
570 rmnerr1 db "RMN: SFT LCK fields not 0", 13, 10, 0
574 Break <MRG
- merge all free space
>
576 ;******************* START OF SPECIFICATIONS ***********************************
578 ; MRG - merge all free space
580 ; MRG - walk through mft merging adjacent free space.
583 ; Outputs: none (all free space coalesced)
584 ; Registers Revised: none
586 ;******************* END OF SPECIFICATIONS *************************************
590 assume
ds:nothing
,es:nothing
595 off
si,mft
; start at beginning
596 mrg1: mov bx,[si].mft_len
; get length
597 cmp [si].mft_flag
,MFLG_FRE
; is record free?
599 jz mrg2
; yes, try to merge with next
600 mrg15: add si,bx ; advance to next
602 ;---------------------------------------
603 ; (si) points to free record.
604 ; - See if next is free
605 ;---------------------------------------
606 mrg2: cmp [bx][si].mft_flag
,MFLG_FRE
607 jnz mrg15
; not free, go scan again
608 mov bx,[bx][si].mft_len
; get length of next guy
609 add [si].mft_len
,bx ; increase our length
610 jmp mrg1
; and check again
618 BREAK <RSC
- Remove SFT
from SFT chain
>
620 ;******************* START OF SPECIFICATIONS ***********************************
622 ; RSC - Remove SFT from SFT chain
624 ; RSC removes a given SFT from its chain. The caller must insure that
625 ; any locks have been cleared and that the SFT is indeed free. The
626 ; sf_mft field is zeroed to indicate that this SFT is no longer chained.
628 ; NOTE - RSC does NOT remove the name record if this was the last SFT on
629 ; it. The caller must check for this and remove it, if
632 ; ENTRY (ES:DI) = SFT address
633 ; EXIT (DS:BX) = FBA name record for this SFT
634 ; 'Z' set if this is the last SFT
637 ;******************* END OF SPECIFICATIONS *************************************
644 mov ax,es ; easy spot for compare
645 mov bx,es:[di].sf_mft
646 lea si,[bx].mft_sptr
-sf_chain
; ds:[si].sf_chain point to prev link
649 cmp word ptr [si].sf_chain
,di
651 cmp word ptr [si].sf_chain
+2,ax
653 rsc15: lds si,[si].sf_chain
655 ;---------------------------------------
657 ; (ds:si) is prev sft link
658 ;---------------------------------------
659 rsc2: mov ax,word ptr es:[di].sf_chain
660 mov word ptr ds:[si].sf_chain
,ax
661 mov ax,word ptr es:[di].sf_chain
+2
662 mov word ptr ds:[si].sf_chain
+2,ax
667 xchg bx,es:[di].sf_MFT
; (DS:bx) = MFT address
668 ; and 0 MFT pointer (show free)
669 cmp word ptr [bx].mft_sptr
,0 ; set z flag if no more sft
678 rscerr db "RSC: SFT not in SFT list", 13, 10, 0
682 BREAK <SLE
- Scan for
Lock Entry>
684 ;******************* START OF SPECIFICATIONS ***********************************
686 ; SLE - Scan for Lock Entry
688 ; SLE scans a lock list looking for a lock range that overlaps the
689 ; caller-supplied range. SLE indicates:
695 ; ENTRY (AX:BX) = FBA of area
696 ; (CX:DX) = LBA of area
697 ; (DS:SI) = address of name record
698 ; (DI) = 0 to ignore locks by User_ID Proc_ID ThisSFT
699 ; = 1 to consider all locks
700 ; EXIT 'C' clear if no overlap
701 ; AX,BX,CX,DX preserved
703 ; (di) = address of pointer to found record
704 ; (i.e., DS:((di)) = address of lock record)
705 ; 'Z' set if 1-to-1 match
706 ; USES ALL but (ds), (es) (also see EXIT)
708 ;******************* END OF SPECIFICATIONS *************************************
714 pushf ; Z set to ignore own locks
715 lea di,[si].mft_lptr
; (ds:di) = addr of ptr to lock record
716 mov si,[di] ; (ds:si) = address of 1st lock record
718 ;---------------------------------------
719 ; check out next lock
721 ; (ds:si) = address of next lock record
722 ; (ds:di) = address of pointer to next
724 ; (TOS) = flags (Z set to ignore
727 ;---------------------------------------
729 jz sle9
; list exhaused, ergo no overlap
732 jnz sle2
; am to check all locks
734 ;---------------------------------------
735 ; am to ignore own locks...
736 ; check the user and proc IDs on this one
737 ;---------------------------------------
739 ;dcl - this code used to compare the process id in the sft pointed to by the
740 ; lock. now we compare the lock process id to the current process id. this
741 ; allows a child process to lock an area and then do i/o with it. before,
742 ; the child could lock it, but then could not access it
745 mov bp,[si].rlr_pid
;dcl
748 les si,[si].rlr_sptr
; (si) = sft address ;dcl
749 mov bp,es:[si].sf_UID
;dcl
751 jnz sce1$5
; doesn't belong to user ;dcl
753 cmp bp,WORD PTR ThisSFT
+2
755 cmp si,WORD PTR ThisSFT
756 sce1$5
: mov si,[di] ; (ds:si) = address of next lock record
757 jz sle3
; owned by user - ignore
760 sub bp,[si].rlr_fba
; compare proposed last to first of record
762 sbb bp,[si].rlr_fba
+2
763 jc sle3
; proposed is above current
765 sub bp,bx ; compare proposed first to last of record
766 mov bp,[si].rlr_lba
+2
768 jnc sle5
; we have a hit
770 ;---------------------------------------
771 ; This entry is harmless...
772 ; chain to the next one
773 ;---------------------------------------
776 sle3: mov di,si ; save addr of pointer to next
779 ;---------------------------------------
780 ; We have an overlap.
781 ; - See if its an exact match
783 ; (ds:di) = address of pointer
784 ; (offset only) to the lock record
785 ; (ds:si) = address of lock record
786 ; (TOS) = flags ('Z' set if to ignore
788 ; (TOS+1) = saved (es)
789 ;---------------------------------------
791 sle5: xor ax,[si].rlr_fba
+2 ; require a 4-word match
793 xor cx,[si].rlr_lba
+2
797 or ax,dx ; 'Z' set if exact match
798 stc ; flag an overlap
799 mov ax,error_lock_violation
800 sle9: pop bp ; discard flags (pushf)
801 pop es ; restore (es)
803 ;---------------------------------------
804 ; (ds:si) = address of lock record
806 ;---------------------------------------
811 BREAK <OFL
- obtain free
lock-record
>
813 ;******************* START OF SPECIFICATIONS ***********************************
815 ; OFL - obtain free lock-record
817 ; OFL returns a free lock-record, if one can be had.
819 ; ENTRY (DS) = MFT Segment
820 ; EXIT 'C' clear if OK
821 ; (DI) = FBA lock record
822 ; 'C' set if no space
826 ;******************* END OF SPECIFICATIONS *************************************
833 ; $if nz ; if something there
837 pop Frelock
; chain off of the list
838 ; exit with 'C' clear
840 ; $else ; none on free list
844 mov ax,error_sharing_buffer_exceeded
; None on free list, give up until
845 stc ; garbage collector is ready
854 Break <CPS
- close process SFT
>
856 ;******************* START OF SPECIFICATIONS ***********************************
858 ; CPS - close process SFT.
860 ; During maintenance, it is necessary to close a
861 ; file given ONLY the SFT. This necessitates walking all PDB's JFN
862 ; tables looking for the SFN. The difficult part is in generating the
863 ; SFN from the SFT. This is done by enumerating SFT's and comparing for
864 ; the correct SFT. Finding all PDBs is easy: walk arena and check
867 ; Inputs: ThisSFT points to SFT of interest
868 ; Outputs: Handle is closed on user
869 ; Registers Revised: none
871 ;******************* END OF SPECIFICATIONS *************************************
875 ASSUME
DS:NOTHING
,ES:NOTHING
877 SaveReg
<DS,SI,ES,DI,AX,BX,CX>
883 CallInstall SFFromSFN
,multDOS
,22,bx,bx
885 jc cps31
; no more SFN's. Must be FCB.
887 CallInstall PointComp
,multDOS
,20
889 jz cps02
; found matching SFN, go scan.
890 inc bx ; did not match, go back for more
892 ;---------------------------------------
893 ; BL is the sfn we want to find. Walk
894 ; the memory arena enumerating all PDB's
895 ; and zap the handle tables for the
897 ;---------------------------------------
900 mov ds,Arena_Head
; get first arena pointer
902 ;---------------------------------------
903 ; DS:[0] is the arena header.
904 ; AL is sfn to be closed
905 ;---------------------------------------
907 mov cx,ds:[arena_owner
]
909 inc bx ; is the owner the same as the current
911 jnz cps2
; no, go skip some more...
913 ;---------------------------------------
914 ; CX:0 is the correct pointer to a PDB.
915 ;---------------------------------------
918 ;---------------------------------------
919 ; Given a PDB at DS:0, scan his handle
920 ; table and then loop through the next
922 ;---------------------------------------
924 call CPJ
; free for this PDB
925 lds cx,DS:[PDB_Next_PDB
] ; advance to next
927 jnz cps15
; there is another link to process
929 ;---------------------------------------
930 ; We have processed the current
931 ; allocation block pointed to by DS.
932 ; DS:[0] is the allocation block
933 ;---------------------------------------
935 cmp ds:[arena_signature
],arena_signature_end
936 jz cps3
; no more blocks to do
937 mov bx,ds ; get current address
938 add bx,DS:[Arena_size
] ; add on size of block
939 inc bx ; remember size of header
940 mov ds,bx ; link to next
942 ;---------------------------------------
943 ; Just for good measure, use CurrentPDB
945 ;---------------------------------------
955 RestoreReg
<CX,BX,AX,DI,ES,SI,DS>
961 ;******************* START OF SPECIFICATIONS ***********************************
965 ; Scan JFN table for SFT # and put in -1 if found
968 ; AL is SFT index # of interest
972 ; Uses: Flags,CX,ES,DI
974 ;******************* END OF SPECIFICATIONS *************************************
978 assume
ds:nothing
,es:nothing
980 mov cx,ds:[PDB_JFN_length
]
981 les di,ds:[PDB_JFN_pointer
]
987 mov byte ptr es:[di-1],-1 ; free this
988 jcxz CPJret
; Found one in last JFN entry
989 jmp cpj1
; keep looking
996 Break <SFM
- convert an mft pointer
into a serial number
>
998 ;******************* START OF SPECIFICATIONS ***********************************
1000 ; SFM - convert a pointer to a mft entry into the serial number for that
1001 ; entry. We keep these around to see if a FCB really points to the correct
1004 ; Inputs: BX is the mft pointer
1005 ; Outputs: BX is the serial number
1006 ; Registers Revised: none
1008 ;******************* END OF SPECIFICATIONS *************************************
1012 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:DOSGROUP
1014 mov bx,cs:[bx].mft_serl
1020 Break <ShChk
- check a fcb for share related information
>
1022 ;******************* START OF SPECIFICATIONS ***********************************
1024 ; ShChk - check a fcb for share related information
1026 ; ShChk - checks the reserved field contents of an FCB with a SFT to see
1027 ; if they represent the same file. The open ref count must be > 0.
1029 ; Inputs: DS:SI point to FCB
1030 ; ES:DI point to SFT
1031 ; Outputs: Carry Set if contents do not match
1032 ; Carry clear if contents match
1033 ; BX has first cluster
1034 ; Registers Revised: none
1036 ;******************* END OF SPECIFICATIONS *************************************
1038 Procedure ShChk
,NEAR
1040 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:DOSGROUP
1042 CMP ES:[DI].sf_ref_count
,0
1044 MOV BX,ES:[DI].sf_mft
; Local file or dev with sharing
1048 CMP BX,[SI].fcb_l_mfs
1050 MOV BX,[SI].fcb_l_firclus
1060 Break <ShSave
- save information
from SFT
into an FCB
>
1062 ;******************* START OF SPECIFICATIONS ***********************************
1064 ; ShSave - save information from SFT into an FCB
1066 ; ShSave - copy information into the reserved area of an FCB from a SFT.
1067 ; This is so that we can later match the SFT with the FCB.
1069 ; Inputs: ES:DI point to SFT
1070 ; DS:SI point to FCB
1071 ; Outputs: FCB reserved field is filled in
1073 ; Registers Revised: AX,BX
1075 ;******************* END OF SPECIFICATIONS *************************************
1077 Procedure ShSave
,NEAR
1079 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:DOSGROUP
1081 MOV AL,ES:[DI].sf_attr
; move attribute (for reopen)
1082 MOV [SI].FCB_l_attr
,AL
1083 MOV AX,ES:[DI].sf_firclus
; get first cluster
1084 MOV [SI].FCB_l_firclus
,AX
1085 MOV BX,ES:[DI].sf_mft
; get sharing pointer
1089 MOV [SI].FCB_l_mfs
,BX
1096 Break <ShCol
- collapse identical handle SFTs
in mode
70 only
>
1098 ;******************* START OF SPECIFICATIONS ***********************************
1100 ; ShCol - collapse identical handle SFTs in mode 70 only
1102 ; ShCol - collapse same 70-mode handles together. This represents network
1103 ; originated FCBs. Since FCB's are incredibly mis-behaved, we collapse the
1104 ; SFT's for identical files, thus using a single sft for each file instead
1105 ; of a separate sft for each instance of the file.
1107 ; Note that the redirectors will collapse multiple instances of these
1108 ; files together. FCB's are pretty misbehaved, so the redirector will
1109 ; inform us of EACH close done on an FCB. Therefore, we must increment
1110 ; the ref count each time we see a collapse here.
1112 ; Inputs: DS:SI ThisSFT has new sft to find.
1113 ; Outputs: Carry set - no matching SFT was found
1114 ; Carry clear - matching SFT was found and all collapsing done.
1115 ; AX has proper handle
1116 ; Registers Revised: all.
1118 ;******************* END OF SPECIFICATIONS *************************************
1120 Procedure ShCol
,NEAR
1122 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:DOSGROUP
1124 ;---------------------------------------
1125 ; Collapse the files ONLY if
1126 ; the mode is for net FCB's
1127 ;---------------------------------------
1129 MOV AL,BYTE PTR [SI].sf_mode
1131 CMP AL,sharing_net_FCB
1134 ;---------------------------------------
1136 ;---------------------------------------
1138 XOR BX,BX ; for (i=0; sffromsfn(i); i++) {
1141 CallInstall SFFromSFN
,multDOS
,22,bx,bx
1145 CallInstall PointComp
,multDOS
,20 ; if (!pointcomp (s,d))
1148 CMP ES:[DI].sf_ref_count
,0
1150 MOV AX,ES:[DI].sf_mode
1153 MOV AX,ES:[DI].sf_mft
1156 MOV AX,WORD PTR ES:[DI].sf_UID
1157 CMP AX,WORD PTR [SI].sf_uid
1159 MOV AX,WORD PTR ES:[DI].sf_pid
1160 CMP AX,WORD PTR [SI].sf_pid
1165 ;--------------------------------------
1166 ; DS:SI points to an sft which is a
1167 ; duplicate of that found in
1168 ; ES:DI is the older one.
1170 ; We call mftclose to release the
1172 ;--------------------------------------
1174 MOV [SI].sf_ref_count
,0 ; free 'new' sft
1176 SaveReg
<DS,SI,ES,DI,BX>
1184 RestoreReg
<AX,DI,ES,SI,DS>
1188 INC ES:[DI].sf_ref_count
; d->refcount++;
1189 XOR BX,BX ; find jfn with sfn as contents
1192 CallInstall pJFNFromHandle
,multDOS
,32,AX,AX
1194 JC UseJFN
; ran out of handles?
1195 CMP AL,BYTE PTR ES:[DI] ; does JFN have SFN?
1196 jz JFNfound
; YES, go return JFN
1197 INC BX ; no, look at next
1201 MOV BYTE PTR [SI],0FFh ; free JFN
1202 MOV AX,BX ; return JFN
1213 Break <ShCloseFile
- close a particular
file for a particular UID
/PID
>
1215 ;******************* START OF SPECIFICATIONS ***********************************
1217 ; ShCloseFile - close a particular file for a particular UID/PID
1219 ; ShCloseFile - Compatability mode programs will often delete files that
1220 ; they had open. This was perfectly valid in the 2.0 days, but this
1221 ; presents a reliability problem in the network based operating environment.
1222 ; As a result, both RENAME and DELETE will call us to see if the file is
1223 ; open by is only. If it is not open or is open by us only, we close it.
1224 ; Note that we will ONLY close compatability SFTs.
1225 ; Otherwise, we signal and error.
1227 ; Inputs: WFT_Start has a DOSGROUP offset to the file name
1229 ; Outputs: nothing relevant.
1230 ; Registers Revised: None.
1232 ;******************* END OF SPECIFICATIONS *************************************
1234 Procedure ShCloseFile
,NEAR
1236 ASSUME
DS:DOSGroup
,ES:NOTHING
,SS:DOSGroup
1238 SaveReg
<AX,BX,CX,DX,SI,DI,BP,DS,ES>
1246 call FNM
; attempt to find name in list
1250 JC ShCloseDone
; can't find, signal success
1252 ;--------------------------------------
1253 ; We have found a file in the MFT.
1254 ; Walk the open sft list to find
1255 ; the SFTs for the current UID/PID.
1256 ;--------------------------------------
1258 LDS SI,[BX].mft_sptr
1268 CMP AX,sharing_net_fcb
1270 CMP AX,sharing_compat
1273 LDS SI,[SI].sf_chain
1277 LDS SI,[BX].mft_sptr
1278 ;--------------------------------------
1279 ; Everything matches. Set up ThisSFT
1280 ; and walk the chain from the beginning.
1281 ;--------------------------------------
1282 MOV WORD PTR ThisSFT
,SI
1283 MOV WORD PTR ThisSFT
+2,DS
1284 ;--------------------------------------
1285 ; Close all handles for this SFT
1286 ;--------------------------------------
1288 ;--------------------------------------
1289 ; Close the sft itself.
1290 ;--------------------------------------
1293 CallInstall DOS_Close
,multDos
,1
1294 ;--------------------------------------
1295 ; The SFT may be free and we have no
1296 ; idea where the next is. Go and loop
1298 ;--------------------------------------
1300 ;--------------------------------------
1301 ; There are no more SFTs to close. Leave
1302 ;---------------------------------------
1309 RestoreReg
<ES,DS,BP,DI,SI,DX,CX,BX,AX>
1316 Break <ShSU
- update all SFTs for a specified change
>
1317 ;******************* START OF SPECIFICATIONS ***********************************
1319 ; NAME: ShSU - update all SFTs for a specified change>
1321 ; FUNCTION: In a shared environment, we want to propogate the SFT
1322 ; changes for a particular file to all other SFTs for that
1323 ; file. The types of things we propogate are:
1325 ; - Time of last write - we only do this on CLOSE and on
1328 ; - Size and allocation information - we do this ONLY when
1329 ; we change sf_size.
1331 ; We achieve this by walking the linked list of SFTs for the
1332 ; file. See PSEUDOCODE below
1334 ; INPUT: ES.DI has SFT that was just Revised.
1335 ; AX = 0 for updating of time from ES:DI into old sfts
1336 ; AX = 1 for updating of size/allocation for growth from ES:DI
1337 ; AX = 2 for updating of size/allocation for shrink from ES:DI
1338 ; AX = 3 for new instance copy into ES:DI
1339 ; AX = 4 for update of codepage and high attribute
1341 ; OUTPUT: All relevant SFTs are updated.
1343 ; REGISTERS USED: All except ES:DI and DS:SI
1346 ; LINKAGE: DOS Jump Table
1348 ; EXTERNAL Invoke: New_Sft, Call_IFS
1349 ; REFERENCES: Callinstall
1357 ; CHANGE 04/15/87 - Major overhaul and IFS support
1360 ;******************* END OF SPECIFICATIONS *************************************
1361 ;******************+ START OF PSEUDOCODE +**************************************
1365 ; if not a device and
1369 ; advance to next SFT
1371 ; leave if no more SFT's
1378 ; if non - FAT file system
1383 ; if non - FAT file system
1386 ; update first cluster
1388 ; if lstclus un-set from create
1389 ; update cluster position
1390 ; update last cluster
1394 ; advance to next SFT
1402 ;******************+ END OF PSEUDOCODE +**************************************
1406 ASSUME
DS:NOTHING
,ES:NOTHING
1412 ifs_flag equ 8000h
; ;AN000;
1413 ;---------------------------------------
1414 ; Do nothing for device or network
1415 ;---------------------------------------
1416 mov bx,es:[di].sf_mode
1417 and bx,sf_isnet
+ devid_device
1419 ; $if z,and,long ; if not device and ;AC000;
1424 mov bx,es:[di].sf_MFT
1427 ; $if nz,,long ; if not network ;AC000;
1433 ;---------------------------------------
1434 ; Walk the sft chain for this file and
1435 ; skip the current SFT (ES:DI)
1436 ;---------------------------------------
1439 lds si,cs:[bx].MFT_SPTR
1445 CallInstall PointComp
,multDOS
,20 ; pointers different?
1447 ; $if z ; if ourselves ;AC000;
1450 lds si,[si].sf_chain
; move to next ;AC000;
1452 ; $endif ; endif - ourselves ;AC000;
1457 ; $leave z ; ;AC000;
1460 ;---------------------------------------
1461 ; CX = 0 for updating of time
1462 ; CX = 1 for updating of size/allocation
1464 ; CX = 2 for updating of size/allocation
1466 ; CX = 3 for new instance copy.
1467 ;---------------------------------------
1470 ; $exitif a ; ;AC000;
1472 ;---------------------------------------
1473 ; CX = 3 for new instance copy.
1474 ; CX = 4 for codepage and high attrib update
1475 ;---------------------------------------
1476 cmp cx,3 ; cx = 3 ? ;an000;
1477 ; $if e ; yes ;an000;
1479 call New_Sft
; ;AN000;
1480 ;; $else ; cx = 4 ;an000;
1481 ;; call New_CP_Attrib ; update codepage and high attrib ;an000;
1491 ; $if z ; if cx = 0 then ;AC000;
1493 ;---------------------------------------
1494 ; CX = 0 for updating of time
1496 ; Copy time from ES:DI into DS:SI
1497 ;---------------------------------------
1498 mov bx,es:[di].sf_time
1500 mov bx,es:[di].sf_date
1502 test [si].sf_flags
,ifs_flag
; ;AN000;
1504 ; $if nz ; if non-FAT ;AC003;
1507 call Call_IFS
; tell IFS of SFT change ;AN000;
1509 ; $endif ; endif non- FAT ;AN000;
1512 ; $else ; else - must be >0 and <2 ;AC000;
1515 ;---------------------------------------
1516 ; CX = 1 for updating of size/allocation
1518 ; CX = 2 for updating of size/allocation
1521 ; We always copy size and firclus
1522 ;---------------------------------------
1523 mov bx,word ptr es:[di].sf_size
1524 mov word ptr [si].sf_size
,bx
1525 mov bx,word ptr es:[di].sf_size
+2
1526 mov word ptr [si].sf_size
+2,bx
1527 test [si].sf_flags
,ifs_flag
; ;AN000;
1529 ; $if nz ; if non-FAT ;AC003;
1532 invoke Call_IFS
; tell IFS of SFT change ;AN000;
1534 ; $else ; else - its FAT ;AN000;
1538 mov bx,es:[di].sf_firclus
1539 mov [si].sf_firclus
,bx
1542 ; $if z,or ; if SFT is shrinking or ;AC000;
1545 cmp [si].sf_lstclus
,0 ; lstclus UN-set from a create? ;AC000;
1547 ; $if z ; If it is, set lstclus and cluspos too;AC000;
1550 ;---------------------------------------
1551 ; Shrink the file, move in new cluspos
1553 ;---------------------------------------
1554 mov [si].sf_cluspos
,0 ; retrace from start
1555 mov [si].sf_lstclus
,bx ; ditto
1557 ; $endif ; endif - set lstclus and cluspos ;AC000;
1560 ; $endif ; endif FAT ;AN000;
1563 ; $endif ; enndif - > 0 ;AC000;
1565 ;---------------------------------------
1567 ;---------------------------------------
1568 lds si,[si].sf_chain
1570 ; $endloop ; ;AC000;
1574 ; $endsrch ; ;AC000;
1576 ;---------------------------------------
1578 ;---------------------------------------
1583 ; $endif ; endif - device and network ;AC000;
1590 Break <New_Sft
- update a new SFT
>
1592 ;******************* START OF SPECIFICATIONS ***********************************
1594 ; NAME: New_Sft - update a new SFT
1596 ; FUNCTION: Copy all SFT information into a NEW sft of a SHARED file.
1599 ; INPUT: ES.DI has SFT that was just Revised.
1600 ; DS:SI has SFT that is to be updated
1602 ; OUTPUT: SFT is updated.
1604 ; REGISTERS USED: AX, BX
1607 ; LINKAGE: Invoked by: ShSU
1609 ; EXTERNAL Invoke: Call_IFS
1612 ; CHANGE 04/15/87 - First release
1615 ;******************* END OF SPECIFICATIONS *************************************
1616 ;******************+ START OF PSEUDOCODE +**************************************
1623 ; if non - FAT file system
1626 ; update first cluster
1627 ; update cluster position
1628 ; update last cluster
1634 ;******************+ END OF PSEUDOCODE +**************************************
1636 Procedure New_Sft
,near ; ;AN000;
1638 mov bx,[si].sf_time
; update time
1639 mov es:[di].sf_time
,bx
1640 mov bx,[si].sf_date
; update date
1641 mov es:[di].sf_date
,bx
1642 mov bx,word ptr [si].sf_size
; update size
1643 mov word ptr es:[di].sf_size
,bx
1644 mov bx,word ptr [si].sf_size
+2
1645 mov word ptr es:[di].sf_size
+2,bx
1646 test es:[di].sf_flags
,ifs_flag
; ;AN000;
1648 ; $if nz ; if non-FAT ;AC003;
1651 call Call_IFS
; tell IFS of SFT change ;AN000;
1653 ; $else ; else - its FAT ;AN000;
1657 mov bx,[si].sf_firclus
; update first cluster
1658 mov es:[di].sf_firclus
,bx
1659 mov es:[di].sf_cluspos
,0 ; retrace from start
1660 mov es:[di].sf_lstclus
,bx ; ditto
1662 ; $endif ; endif FAT ;AN000;
1665 ret ; we'er done ;AN000;
1667 EndProc New_Sft
; ;AN000;
1669 Break <New_CP_Attrib
- update the codepage
and attrib
in SFT
>
1671 ;******************* START OF SPECIFICATIONS ***********************************
1673 ; NAME: New_CP_Attrib - Update codepage and attrib in SFT
1675 ; FUNCTION: Copy all codepage and attrib into SFT of a SHARED file.
1678 ; INPUT: ES.DI has SFT that was just Revised.
1679 ; DS:SI has SFT that is to be updated
1681 ; OUTPUT: SFT is updated.
1683 ; REGISTERS USED: AX, BX
1686 ; LINKAGE: Invoked by: ShSU
1688 ; EXTERNAL Invoke: Call_IFS
1691 ; CHANGE 10/06/87 - First release - D. M. Sewell
1694 ;******************* END OF SPECIFICATIONS *************************************
1695 ;******************+ START OF PSEUDOCODE +**************************************
1697 ; START New_CP_Attrib
1700 ; Update high attribute
1708 ;******************+ END OF PSEUDOCODE +**************************************
1710 ;; Procedure New_CP_Attrib,near ; ;AN000;
1712 ;; mov bx,es:[di].SF_Codepage ; update codepage ;an000;
1713 ;; mov [si].SF_Codepage,bx ;an000; dms;
1714 ;; mov bl,es:[di].SF_Attr_Hi ; update high attribute ;an000;
1715 ;; mov [si].SF_Attr,bl ;an000; dms;
1716 ;; test es:[di].sf_flags,ifs_flag ; ;AN000;
1718 ;; $if nz ; if non-FAT ;AC003;
1720 ;; call Call_IFS ; tell IFS of SFT change ;AN000;
1722 ;; $endif ; endif FAT ;AN000;
1724 ;; ret ; we'er done ;AN000;
1726 ;; EndProc New_CP_Attrib ; ;AN000;
1729 Break <Call_IFS
- warn IFS that SFT has changed
>
1731 ;******************* START OF SPECIFICATIONS ***********************************
1733 ; NAME: Call_IFS - warn IFS that SFT has changed
1735 ; FUNCTION: Call IFS thru 2F interupt.
1737 ; INPUT: DS.SI points to SFT that was just Revised.
1741 ; REGISTERS USED: AX
1744 ; LINKAGE: Invoked by: ShSU, New_SFT
1746 ; EXTERNAL Callinstall
1749 ; CHANGE 04/15/87 - First release
1752 ;******************* END OF SPECIFICATIONS *************************************
1753 ;******************+ START OF PSEUDOCODE +**************************************
1763 ;******************+ END OF PSEUDOCODE +**************************************
1765 Procedure Call_IFS
,near ; ;AN000;
1767 CallInstall BlockUpdate
,MultIFS
,44,CX,CX ; ;AC005;
1771 EndProc Call_IFS
; ;AN000;
1773 Break <Internal error routines
>
1775 ;******************* START OF SPECIFICATIONS ***********************************
1777 ; INTERR - INTernal ERRor routines
1779 ;******************* END OF SPECIFICATIONS *************************************
1781 Procedure INTERR
,NEAR
1783 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
1785 SaveReg
<BX,SI,DS> ; save registers that get clobbered
1787 push cs ; gain addressability
1789 mov si,ax ; get message to print
1797 RestoreReg
<ds,si,bx>
1799 INTERRL:jmp INTERRL
; hang here - we're sick
1808 IntErrMsg
DB "Share: Internal error", 13, 10, 0
1812 Break <INT 2F handler
>
1818 skip_check db 0 ; start with do checking
1820 state_change db 0 ; SHARE change in state flag
1821 ; 0 - no change in state
1822 ; 1 - SHARE load state has changed
1828 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:NOTHING
1832 ; Its for SHARE! Check to see who is calling:
1835 ; 81h its us, with /NC - set skip_check
1836 ; - return 0F0h - end init
1838 ; if skip_check is reset
1839 ; - return 0FFh - loaded
1840 ; if skip_check is set
1841 ; - reset skip_check
1842 ; - return 0F0h - end init
1844 ; 40h its IFSFUNC - return 0FFh - loaded
1846 ; 00h its anyone else - clear skip_check
1847 ; - return 0FFh - loaded
1849 test al,80h
; is it share? ;AN010;
1850 ; $if nz ; if it is ;AN010;
1852 and al,1 ; is /NC set ;AN010;
1853 mov al,0F0H ; assume a quiet return ;AN010;
1854 ; $if nz ; if it is ;AN010;
1856 cmp skip_check
,1 ; is skip_check set ? ;AN011;
1857 ; $if ne ; if it is ;AN011;
1859 mov state_change
,1 ; set the change state flag ;AN011;
1862 mov skip_check
,1 ; set skip_check ;AN010;
1863 ; $else ; /NC not requested ;AN010;
1866 cmp skip_check
,1 ; is skip_check set ? ;AN010;
1867 ; $if e ; if it is ;AN010;
1869 mov state_change
,1 ; set the change state flag ;AN011;
1870 mov skip_check
,0 ; reset skip_check ;AN010;
1871 ; $else ; else , its already clear ;AN010;
1874 mov al,0FFH ; and we are loaded ;AN010;
1883 cmp al,40h
; is it IFSFUNC? ;AN010;
1884 ; $if ne ; if it is not ;AN010;
1887 or al,al ; loop it any other value caus' ;AC010;
1889 jnz freeze
; no one should EVER issue this ;AC010;
1890 cmp skip_check
,1 ; is skip_check set ? ;AN010;
1891 ; $if e ; if it is ;AN011;
1893 mov state_change
,1 ; set the change state flag ;AN011;
1896 mov skip_check
,0 ; and believe it ! ;AN011;
1900 mov al,0FFH ; else - say we are here ;AN010;
1903 cmp state_change
,1 ; SHARE installed state may have change;AN011;d
1904 ; $if e ; - update DOS ;AN011;
1907 push es ; this is interesting - ;AN011;
1908 MOV AH,Get_In_Vars
; if SHARE =1 and DOS =1 - no change;AN011;
1909 INT 21h
; if SHARE = ;AN011;
1913 mov al,skip_check
; get the SHARE operating mode ;AN011;
1914 cmp al,1 ; is it a /nc - tell DOS " 1 " ;AN011;
1915 ; $if ne ; if not ;AN011;
1917 dec al ; "full" SHARE - tell DOS " -1 " ;AN011;
1920 MOV fShare
,al ; tell DOS we are here ;AN011;
1923 mov state_change
,0 ; REset the change state flag ;AN011;
1934 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:DOSGroup
1936 IRP rtn
,<MFT_enter
, MFTClose
, MFTclU
, MFTCloseP
, MFTCloN
, set_mult_block
, clr_mult_block
>
1943 IRP rtn
,<chk_block
, MFT_get
, ShSave
, ShChk
, ShCol
, ShCloseFile
, ShSU
>
1950 IRP sect
,<critShare
>
1951 Procedure E
§
,NEAR
1959 Procedure L
§
,NEAR
1970 BREAK <MFT
and Lock Record
Data Area
>
1972 ;******************* START OF SPECIFICATIONS ***********************************
1976 ; Note that the name field can have garbage after the trailing
1977 ; 00 byte. This is because the field might be too long, but
1978 ; not long enough (at least 16 extra bytes) to fragment.
1979 ; in this case we copy the length of the string area, not
1980 ; the length of the string and thus may copy tailing garbage.
1982 ;******************* END OF SPECIFICATIONS *************************************
1989 DW PoolSize
; PoolSize bytes long
1993 DB (PoolSize
-3) DUP(0) ; leave rest of record
1994 MEND
DB -1 ; END record
1997 DB SIZE RLR_entry
-2 DUP(0)
1998 lck2
DW OFFSET DOSGROUP
:lck1
; link
1999 DB SIZE RLR_entry
-2 DUP(0)
2000 lck3
DW OFFSET DOSGROUP
:lck2
; link
2001 DB SIZE RLR_entry
-2 DUP(0)
2002 lck4
DW OFFSET DOSGROUP
:lck3
; link
2003 DB SIZE RLR_entry
-2 DUP(0)
2004 lck5
DW OFFSET DOSGROUP
:lck4
; link
2005 DB SIZE RLR_entry
-2 DUP(0)
2006 lck6
DW OFFSET DOSGROUP
:lck5
; link
2007 DB SIZE RLR_entry
-2 DUP(0)
2008 lck7
DW OFFSET DOSGROUP
:lck6
; link
2009 DB SIZE RLR_entry
-2 DUP(0)
2010 lck8
DW OFFSET DOSGROUP
:lck7
; link
2011 DB SIZE RLR_entry
-2 DUP(0)
2015 %
out Ignore this
END error
(blasted assembler
)
2021 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:NOTHING
2024 InitSpace
DW PoolSize
2026 IF shareinit
-MFT LT PoolSize
2027 InitSpace
DW PoolSize
2029 InitSpace
DW shareinit
-MFT
2037 DD JMFT_Enter
; 1 MFT_enter
2038 DD JMFTClose
; 2 MFTClose
2039 DD JMFTclU
; 3 MFTclU
2040 DD JMFTcloseP
; 4 MFTCloseP
2041 DD JMFTcloN
; 5 MFTCloN
2042 DD JSet_Mult_Block
; 6 Set_Mult_Block
2043 DD JClr_Mult_Block
; 7 Clr_Mult_Block
2044 DD JChk_Block
; 8 Chk_Block
2045 DD JMFT_Get
; 9 MFT_get
2046 DD JShSave
; 10 ShSave
2047 DD JShChk
; 11 ShChk
2048 DD JShCol
; 12 ShCol
2049 DD JShCloseFile
; 13 ShCloseFile
2051 JTableLen
= $ - JTable
2053 ; $SALUT (4,9,17,36)
2054 ;---------------------------------------
2055 ; STRUCTURE TO DEFINE ADDITIONAL
2056 ; COMMAND LINE PARAMETERS
2057 ;---------------------------------------
2059 DW OFFSET PARMSX
; POINTER TO PARMS STRUCTURE
2060 DB 0 ; NO DELIMITER LIST FOLLOWS
2061 DB 0 ; NUMBER OF ADDITIONAL DELIMITERS
2063 ;---------------------------------------
2064 ; STRUCTURE TO DEFINE SORT
2065 ; SYNTAX REQUIREMENTS
2066 ;---------------------------------------
2068 DB 0,0 ; THERE ARE NO POSITIONAL PARAMETERS
2069 DB 1 ; THERE ARE ONLY ONE TYPE OF SWITCH
2070 DW OFFSET SW
; POINTER TO THE SWITCH DEFINITION AREA
2071 DW 0 ; THERE ARE NO KEYWORDS IN SHARE SYNTAX
2073 ;---------------------------------------
2074 ; STRUCTURE TO DEFINE THE SWITCHES
2075 ;---------------------------------------
2078 DW 08001H ; MUST BE NUMERIC
2079 DW 0 ; NO FUNCTION FLAGS
2080 DW OFFSET SWITCH_BUFF
; PLACE RESULT IN SWITCH BUFFER
2081 DW OFFSET VALUES
; NEED VALUE LIST
2082 DB 3 ; TWO SWITCHES IN FOLLOWING LIST
2083 F_SW
DB "/F",0 ; /F: INDICATES n FILESPACE REQUESTED
2084 L_SW
DB "/L",0 ; /L: INDICATES m LOCKS REQUESTED
2085 N_SW
DB "/NC",0 ; /NC: INDICATES no checking required
2088 ;---------------------------------------
2089 ; VALUE LIST DEFINITION FOR n
2090 ;---------------------------------------
2093 DB 1 ; ONE VALUE ALLOWED
2094 DB 1 ; ONLY ONE RANGE
2095 DB FILE_SWITCH
; IDENTIFY IT AS n
2096 DD 1,65535 ; USER CAN SPECIFY /+1 THROUGH /+65535
2098 ;---------------------------------------
2099 ; RETURN BUFFER FOR SWITCH INFORMATION
2100 ;---------------------------------------
2101 ; $SALUT (4,17,27,36)
2103 SWITCH_BUFF
LABEL BYTE
2104 SW_TYPE
DB ?
; TYPE RETURNED
2105 SW_ITEM_TAG
DB ?
; SPACE FOR ITEM TAG
2106 SW_SYN
DW ?
; POINTER TO SWITCH LIST ENTRY
2107 SW_VALUE
DD ?
; SPACE FOR VALUE
2111 Break <INIT
- INITalization routines
>
2113 ;******************* START OF SPECIFICATIONS ***********************************
2115 ; INIT - INITalization routines
2117 ;******************* END OF SPECIFICATIONS *************************************
2130 MOV WORD PTR [SI+1],BX ; length of first item
2131 ADD SI,BX ; link to end of structure
2132 MOV BYTE PTR [SI],-1 ; signal end
2133 INC SI ; point to next free byte
2135 MOV CX,initlocks
; count for loop
2141 MOV [SI].RLR_next
,AX ; link in previous
2142 MOV AX,SI ; this is now previous
2143 ADD SI,SIZE RLR_Entry
; move to next object
2145 ; $enddo loop ; ;AC000;
2148 MOV FreLock
,AX ; point to beginning of free list
2160 PUSH SI ; # of paras for share on stack
2162 MOV AX,(Get_Interrupt_Vector
SHL 8) + 2Fh
2164 MOV WORD PTR CONT
,BX
2165 MOV WORD PTR CONT
+2,ES
2166 MOV AX,(Set_Interrupt_Vector
SHL 8) + 2Fh
2169 ;---------------------------------------
2170 ; Notify the DOS that we are around so that
2171 ; the DOS can make expensive calls to us.
2172 ;---------------------------------------
2178 mov al,skip_check
; get the SHARE operating mode ;AN011;
2179 cmp al,1 ; is it a /nc - tell DOS " 1 " ;AN011;
2181 ; $if ne ; if not ;AN011;
2183 dec al ; "full" SHARE - tell DOS " -1 " ;AN011;
2187 MOV fShare
,al ; tell DOS we are here ;AC011;
2188 ;---------------------------------------
2189 ; Cram in the new jump table
2190 ;---------------------------------------
2192 MOV SI,OFFSET JTable
2193 MOV DI,OFFSET JShare
2196 ;---------------------------------------
2197 ; Examine the size of the FCB cache.
2198 ; If it is NOT the system default of 4,0
2199 ; change it (via reallocation) to 16,8.
2200 ; The old table is lost.
2201 ;---------------------------------------
2206 ; $if z,and ; if the ",0" part and ;AC000;
2209 LDS SI,ES:[BX].SYSI_FCB
; point to the existing cache
2212 ; $if z ; if the "4," part then ;AC000;
2215 ;---------------------------------------
2216 ; Whammo, we need to allocate 16 * size
2217 ; of SF_entry + size of sfTable.
2218 ; Compute this size in paragraphs
2219 ;---------------------------------------
2221 MOV CX,size sf_entry
2223 ADD AX,(size sf
) - 2
2224 ;---------------------------------------
2225 ; This size is in bytes...
2226 ; Round up to paragraph size
2227 ;---------------------------------------
2233 ;---------------------------------------
2234 ; AX is the number of paragraphs to add.
2235 ; Word on stack is current TNR size.
2236 ; Make dos point to new table
2237 ;---------------------------------------
2238 MOV WORD PTR ES:[BX].SYSI_FCB
,0
2239 MOV WORD PTR ES:[BX].SYSI_FCB
+2,SS
2241 ADD WORD PTR ES:[BX].SYSI_FCB
+2,SI
2242 ;---------------------------------------
2243 ; Initialize table parts, next link
2245 ;---------------------------------------
2246 MOV DS,WORD PTR ES:[BX].SYSI_FCB
+2
2247 MOV WORD PTR DS:[sfLink
],-1
2248 MOV WORD PTR DS:[sfLink
+2],-1
2250 ;---------------------------------------
2251 ; Set up succeeding LRU size
2252 ;---------------------------------------
2258 ; $endif ; endif - "4,0" ;AC000;
2261 ;---------------------------------------
2262 ; Clean out the FCB Cache
2263 ;---------------------------------------
2264 LES DI,ES:[BX].SYSI_FCB
2268 MOV CX,ES:[DI].SFCount
2274 MOV ES:[DI].sf_ref_count
,0
2275 MOV WORD PTR ES:[DI].sf_position
,0
2276 MOV WORD PTR ES:[DI].sf_position
+2,0
2277 ADD DI,SIZE sf_entry
2279 ; $enddo loop ; ;AC000;
2287 MOV CX,5 ; StdIN,StdOUT,StdERR,StdAUX,StdPRN
2289 ; $do ; Close STD handles before ;AC000;
2296 ; $enddo loop ; ;AC000;
2299 POP DX ; T+R size in DX
2300 MOV AX,(Keep_Process
SHL 8) + 0
2302 MOV AX,(EXIT
SHL 8) + 1
2303 INT 21h
; We'er now resident, return to DOS
2307 Break <SHAREINIT
- Share initialization
entry point
>
2309 ;******************* START OF SPECIFICATIONS ***********************************
2311 ; SHAREINIT - Share initialization entry point
2313 ;******************* END OF SPECIFICATIONS *************************************
2315 Procedure SHAREINIT
,NEAR
2317 ASSUME
CS:SHARE
,DS:NOTHING
,ES:NOTHING
,SS:STACK
2324 PUSH DS ; save PSP segment for later stack ;AC001;
2327 ;---------------------------------------
2329 ;---------------------------------------
2330 call ShLoadMsg
; ;AN000;
2331 ;---------------------------------------
2332 ; At this point, the DOS version is OK.
2333 ; (checked by SYSLOADMSG)
2334 ; Now - Check the DOS data version
2335 ;---------------------------------------
2336 ; $if c,or ; if not same as us ;AC009;
2344 CMP DataVersion
,ShareDataVersion
2348 ; $if ne ; if not same as us ;AC000;
2351 mov ax,(Utility_Msg_CLASS
shl 8) + Bad_DOS_Ver
; ;AN000;
2352 call ShDispMsg
; ;AN000;
2353 ; $endif ; endif - not same as us ;AC000;
2356 ;---------------------------------------
2357 ; Deallocate memory if possible
2358 ;---------------------------------------
2359 mov ax,ds:[pdb_environ
]
2362 ; $if nz ; if > 0 deallocate memory ;AC000;
2367 ; $endif ; endif - > 0 deallocate memory ;AC000;
2370 ;---------------------------------------
2371 ; Parse the command line
2372 ;---------------------------------------
2373 call ShComndParse
; ;AN000;
2374 ;---------------------------------------
2375 ; Check to see if share already installed.
2376 ;---------------------------------------
2377 mov al,skip_check
; ;AN010;
2378 or al,80h
; signal its SHARE calling ;AN010;
2379 mov ah,multShare
; ;AC010;
2381 CMP AL,0FFh ; ;AC010;
2383 ; $if z ; if we'er already loaded ;AC010;
2385 mov ax,(UTILITY_MSG_CLASS
shl 8) + Sh_Already_Loaded
; ;AC010;
2386 call ShDispMsg
; ;AC010;
2387 ; $endif ; endif - we'er already loaded ;AC010;
2390 ;---------------------------------------
2391 ; Check to see if share installed and
2392 ; a toggle was just performed
2393 ;---------------------------------------
2394 CMP AL,0F0h ; ;AN010;
2396 ; $if z ; if we'er already loaded ;AN010;
2399 MOV AX,(EXIT
SHL 8) ; ;AN010;
2400 INT 21h
; Return to DOS with RC = 0 ;AN010;
2402 ; $endif ; endif - we'er already loaded ;AN010;
2405 ;---------------------------------------
2406 ; All set to initialize the world.
2407 ; Make sure that we have enough memory
2408 ; for everything in our little 64K here.
2409 ; First get avail count of paras.
2410 ;---------------------------------------
2411 pop es ; recover PSP segment ;AC002;
2414 MOV AX,ES:[PDB_Block_Len
]
2416 ;---------------------------------------
2417 ; AX has the number of paragraphs
2418 ; available to us after the beginning
2419 ; of CS. Max this out at 64K.
2420 ;---------------------------------------
2423 ; $if a ; if more than we can handle ;AC000;
2425 MOV AX,1000h
; force it
2426 ; $endif ; endif - more than we can handle ;AC000;
2429 ;---------------------------------------
2430 ; Take AX paragraphs and convert them
2432 ;---------------------------------------
2440 ;---------------------------------------
2441 ; compute in DX:AX, the size
2442 ; requested by the user
2443 ;---------------------------------------
2445 MOV SI,size RLR_Entry
2451 ;---------------------------------------
2452 ; Compare the 32 bit sizes DX:AX and BX:CX.
2453 ; If BX:CX is smaller, then we
2454 ; are out of memory.
2455 ;---------------------------------------
2457 CMP DX,BX ; try upper half first
2459 ; $if a,or ; if most significant is bigger or ;AC000;
2462 ; $if e,and ; if equal and ;AC000;
2467 ; $if a ; if least significant is bigger ;AC000;
2471 mov ax,(EXT_ERR_CLASS
shl 8) + No_Mem_Error
; issue error message ;AN000;
2473 call ShDispMsg
; ;AN000;
2475 ; $endif ; endif - bigger ;AC000;
2478 ;--------------------------------------
2479 ; Move stack to PSP area. Otherwise we
2480 ; will run into problems with growing
2481 ; the stack into the lock records.
2482 ;---------------------------------------
2483 POP AX ; this is the entry value for DS (PSP) ;AC001;
2485 MOV SP,100h
; ;AC001;
2488 ;---------------------------------------
2489 ; Continue with rest of initialization
2490 ;---------------------------------------
2495 Break <ShLoadMsg
- Share
Load Message
>
2497 ;******************* START OF SPECIFICATIONS ***********************************
2499 ; NAME: ShLoadMsg - Share Load Message
2501 ; FUNCTION: Load the Share messages into the message buffer.
2505 ; OUTPUT: Messages loaded into the message buffer and Message
2506 ; Sevices code initalized
2508 ; REGISTERS USED: DI AX CX DX
2511 ; LINKAGE: Call near
2519 ; CHANGE 04/15/87 - First release
2522 ;******************* END OF SPECIFICATIONS *************************************
2524 ;---------------------------------------
2526 ;---------------------------------------
2528 ; $SALUT (4,27,34,41)
2530 Bad_DOS_Ver equ
1 ; Incorrect DOS version ;AN000;
2531 Sh_Already_Loaded equ
2 ; SHARE already loaded message number ;AN000;
2532 No_Mem_Error equ
8 ; insufficient memory message number ;AN000;
2536 Procedure ShLoadMsg
,near ; ;AN000;
2537 ;---------------------------------------
2539 ;---------------------------------------
2540 EXTRN SYSLOADMSG
:NEAR ; ;AN000;
2542 call SYSLOADMSG
; ;AN000;
2544 ; $IF C ; if we have a MAJOR problem ;AN000;
2546 mov ah,dh ; save the class
2547 call ShDispMsg
; ;AN000;
2548 ; For pre DOS 2.0, we may come back
2549 xor ax,ax ; here - so do it the old way
2550 push ss ; just in case
2553 xxx proc
far ; ;AN000;
2557 ; $ENDIF ; endif - we have a MAJOR problem ;AN000;
2565 Break <ShDispMsg
- Share
Display Message
>
2567 ;******************* START OF SPECIFICATIONS ***********************************
2569 ; NAME: ShDispMsg - Share Display Message
2571 ; FUNCTION: Display the messages for share
2573 ; INPUT: AX = message number - AH - Class
2576 ; OUTPUT: - Messages output to Output Device
2579 ; REGISTERS USED: CX DX
2582 ; LINKAGE: Call near
2588 ; EXIT: CX = 0 - INCORRECT DOS VERSION
2590 ; CHANGE 04/15/87 - First release
2593 ;******************* END OF SPECIFICATIONS *************************************
2595 ; $SALUT (4,27,34,41)
2597 ; The following structure is a
2598 ; SYSMSG SUBLIST control block.
2599 ; It is initalized for the "already
2600 ; installed " message. The parse
2601 ; routine will set it up to work
2605 db sub_size
; size of sublist
2607 msg_offset dw offset SHARE_Name
; insert 'SHARE'
2609 msg_segment
LABEL WORD
2621 num_ins db 1 ; only one insert
2622 db Char_Field_ASCIIZ
; data type flag - ascii z string
2623 max_ins db SHARE_Name_Size
; maximum field size
2624 min_ins db SHARE_Name_Size
; minimum field size
2625 db " " ; pad character
2627 sub_size equ
$ - SUBLIST
2629 SHARE_Name
LABEL WORD
2633 SHARE_Name_Size equ
$ - Share_Name
2635 db 0 ; make it a Z string
2638 Procedure ShDispMsg
,near ; ;AN000;
2639 ;---------------------------------------
2640 ; Set up required parameters
2641 ;--------------------------------------
2642 MOV BX,STDERR
;display message on STD ERROR ;AN000;
2643 XOR CX,CX ;no substitution required ;AN000;
2644 XOR DX,DX ;set flags to 0 ;AN000;
2645 DEC DH ;and class to utility ;AN000;
2646 cmp ah,PARSE_ERR_CLASS
;
2647 ; $if be,and ; ;AC009;
2650 ; $if e ; set up implied substitution ;AC009;
2653 ASSUME
DS:nothing
,ES:DOSGROUP
2655 mov num_ins
,cl ; set number of inserts to 0 ;AN009;
2656 mov BYTE PTR max_ins
,030h ; set maximum size of insert ;AN009;
2657 mov BYTE PTR min_ins
,1 ; set minimum size of insert ;AN009;
2658 push ds ; set up segment ;AN009;
2659 pop [msg_segment
] ; ;AN009;
2660 mov BYTE PTR ds:[si],0 ; turn it into a ASCIIZ string ;AN009;
2661 cmp si,msg_offset
; is there something there? ;AN009;
2662 ; $if a ; if it is... ;AN009;
2669 cmp al,Sh_Already_Loaded
; SHARE already loaded message ? ;AN000;
2670 ; $if e ; if it is... ;AN000;
2673 mov msg_offset
,OFFSET SHARE_name
; ensure the pointer is right ;AN010;
2676 push cs ; ensure that SYSMSG has proper ;AC009;
2677 pop ds ; addressability ;AC009;
2678 lea si,SUBLIST
; point to sublist ;AC009;
2681 ;--------------------------------------
2682 ; Output the Message
2683 ;---------------------------------------
2684 EXTRN SYSDISPMSG
:NEAR ; ;AN000;
2686 CALL SYSDISPMSG
; ;AN000;
2688 ; $IF C ; if error occured ;AN000;
2691 CALL Get_DOS_Error
; a DOS extended error occured ;AN000;
2692 CALL SYSDISPMSG
; try to issue it ;AN000;
2694 ; $ENDIF ; endif - error occured ;AN000;
2697 MOV AX,(EXIT
SHL 8) + 0FFH ; exit to DOS ;AN000;
2700 ret ; may return if pre DOS 2.0 ;AN000;
2702 EndProc ShDispMsg
; ;AN000;
2704 BREAK < Get_DOS_Error
>
2706 ;******************* START OF SPECIFICATIONS ***********************************
2707 ;Routine name: Get_DOS_Error
2708 ;*******************************************************************************
2710 ;Description: Call DOS to obtain DOS extended error #
2712 ;Called Procedures: None
2716 ;Output: AX = error number
2717 ; DH = DOS extended error class
2719 ;Change History: Created 5/01/87 FG
2721 ;******************* END OF SPECIFICATIONS *************************************
2722 ;******************+ START OF PSEUDOCODE +**************************************
2724 ; START Get_DOS_Error
2726 ; call DOS for extended error (INT21 GetExtendedError + 00 <5900>)
2727 ; set up registers for return
2732 ;******************- END OF PSEUDOCODE -**************************************
2734 public Get_DOS_Error
2736 Get_DOS_Error PROC
NEAR
2738 mov ax,(GetExtendedError
shl 8) ; DOS ext. error ;AN000;
2741 INT 21h
; GetExtendedError + not_used <5900>;AN000;
2743 mov bx,STDERR
; fix up bx ;AN000;
2744 xor cx,cx ; fix up cx ;AN000;
2745 mov dh,EXT_ERR_CLASS
; set class to dos error
2749 ENDPROC Get_DOS_Error
2751 Break <ShComndParse
- Share Command line Parser
>
2753 ;******************* START OF SPECIFICATIONS ***********************************
2755 ; NAME: ShComndParse - Share Command line Parser
2757 ; FUNCTION: Call the DOS PARSE Service Routines to process the command
2758 ; line. Search for valid switches (/F:n and /L:m) and
2759 ; update the values for file size and number of locks accordingly
2761 ; INPUT: Parameter string from command line in the PSP
2763 ; OUTPUT: INITspace and INITlocks are updated.
2765 ; REGISTERS USED: ES DI AX BX CX DX
2770 ; NORMAL - If /F:n specified, then INITspace is updated.
2771 ; EXIT: - If /L:m specified, then INITlocks is updated.
2773 ; ERROR If user enters:
2774 ; EXIT: - any parameter or switch other than /F:n or /L:m
2775 ; - an invalid value for "n" or "m"
2776 ; then this routine will display the "Invalid Parameter"
2777 ; error message and terminate.
2779 ; EXTERNAL - System parse service routines
2780 ; REFERENCES: - INT21 - GET PSP Function Call 062h
2782 ; CHANGE 04/15/87 - First release
2785 ;******************* END OF SPECIFICATIONS *************************************
2786 ;******************+ START OF PSEUDOCODE +**************************************
2794 ;******************- END OF PSEUDOCODE -*************************************
2796 ; $SALUT (4,27,34,41)
2798 ;--------------------------------------
2800 ;--------------------------------------
2802 EOL equ
-1 ; Indicator for End-Of-Line ;AN000;
2803 NOERROR equ
0 ; Return Indicator for No Errors ;AN000;
2804 FILE_SWITCH equ
1 ; this is a file switch ;AN000;
2805 LOCK_SWITCH equ
2 ; this is a lock switch ;AN000;
2806 Syntax_Error equ
9 ; maximum PARSE error # ;AN000;
2810 Procedure ShComndParse
,near ; ;AN000;
2811 ;--------------------------------------
2812 ; Get address of command line
2813 ;--------------------------------------
2814 EXTRN SYSPARSE
:NEAR ; ;AN000;
2816 MOV SI,0081H ; OFFSET OF COMMAND LINE IN PSP ;AN000;
2817 MOV AH,62H
; AH=GET PSP ADDRESS FUNCTION CALL ;AN000;
2818 INT 21H
; PSP SEGMENT RETURNED IN BX ;AN000;
2819 MOV DS,BX ; PUT PSP SEG IN DS ;AN000;
2820 MOV CX,0 ; NUMBER OF PARMS PROCESSED SO FAR ;AN000;
2824 ASSUME
ES:SHARE
; ;AN000;
2826 ;--------------------------------------
2827 ; Loop for each operand at DS:SI
2828 ;--------------------------------------
2832 LEA DI,PARMS
; ADDRESS OF PARSE CONTROLS ;AN000;
2833 MOV DX,0 ; RESERVED ;AN000;
2834 mov msg_offset
,si ; save the start scan point ;AC009;
2835 CALL SYSPARSE
; PARSE IT! ;AN000;
2836 CMP AX,EOL
; ARE WE AT END OF COMMAND LINE ? ;AN000;
2838 ; $leave e ; ;AN000;
2841 CMP AX,NOERROR
; ANY ERRORS? ;AN000;
2843 ; $if ne,or ; if parse says error or ;AN000;
2846 MOV AX,Syntax_Error
; Parse syntax error - just in case ;AN000;
2847 MOV BX,DX ; PLACE RESULT ADDRESS IN BX ;AN000;
2848 CMP BX,OFFSET SWITCH_BUFF
; ;AN000;
2850 ; $if ne ; if no pointer ;AN000;
2854 call PARSE_ERROR
; call error routine ;AN000;
2856 ; $endif ; endif - error ;AN000;
2859 MOV AX,WORD PTR SW_VALUE
; load the value ;AN000;
2860 MOV BX,SW_SYN
; load pointer to synonym ;AN000;
2862 ;--------------------------------------
2863 ; If user said /F:n, then
2864 ;--------------------------------------
2866 CMP BX,OFFSET F_SW
; IF USER SPECIFIED /F ;AN000;
2871 CMP INITspace
,AX ; is default < requested ? ;AN000;
2873 ; $if b ; if default is < ;AN000;
2875 MOV INITspace
,AX ; save the new value ;AN000;
2876 ; $endif ; endif (else leave it alone) ;AN000;
2879 ; $else ; else - CHECK FOR LOCKS ;AN000;
2883 ;---------------------------------------
2884 ; If user said /L:m, then update INITlocks
2885 ;---------------------------------------
2886 CMP BX,OFFSET L_SW
; IF USER SPECIFIED /L ;AN000;
2888 ; $if e ; if it is ;AN000;
2891 CMP INITlocks
,AX ; is default < requested ? ;AN000;
2893 ; $if b ; if default is < ;AN000;
2895 MOV INITlocks
,AX ; save the value ;AN000;
2896 ; $endif ; endif (else leave it alone) ;AN000;
2899 ; $else ; else - CHECK FOR TOGGLE ;AN010;
2903 ;---------------------------------------
2904 ; If user said /NC, then update check_flag
2905 ;---------------------------------------
2906 CMP BX,OFFSET N_SW
; IF USER SPECIFIED /NC ;AN010;
2907 ; $if ne ; if error ;AC010;
2909 MOV AX,Syntax_Error
; Parse syntax error ;AN000;
2910 call PARSE_ERROR
; call error routine ;AN000;
2911 ; $endif ; endif - error ;AC010;
2914 mov skip_check
,1 ; set the skip check flag ;AN010;
2916 ; $endif ; endif - CHECK FOR TOGGLE ;AN010;
2919 ; $endif ; endif - CHECK FOR LOCKS ;AN000;
2922 ; $enddo ; CHECK FOR NEXT PARM ;AN000;
2926 ret ; NORMAL RETURN TO CALLER ;AN000;
2928 ;---------------------------------------
2929 ; If any other parameter specified,
2930 ; display message and quit
2931 ;---------------------------------------
2932 PARSE_ERROR: ; ;AN000;
2934 cmp al,Syntax_Error
; error 1 to 9 ? ;AN000;
2936 ; $if a ; if parse error ;AN000;
2939 mov al,Syntax_Error
; Parse syntax error
2941 ; $endif ; endif errors ;AN000;
2944 lea bx,Parse_Ret_Code
2946 mov ah,PARSE_ERR_CLASS
; set class to parse error ;AN000;
2948 CALL ShDispMsg
; display the parse error ;AN000;
2950 ret ; this should never be used
2952 Parse_Ret_Code
label byte
2955 db 9 ; Ret Code 1 - Too many parameters
2956 db 9 ; Ret Code 2 - Required parameter missing
2957 db 3 ; Ret Code 3 - Invalid switch
2958 db 9 ; Ret Code 4 - Invalid keyword
2959 db 9 ; Ret Code 5 - (reserved)
2960 db 6 ; Ret Code 6 - Parm val out of range
2961 db 9 ; Ret Code 7 - Parameter val not allowed
2962 db 9 ; Ret Code 8 - Parameter val not allowed
2963 db 9 ; Ret Code 9 - Parm format not correct
2965 EndProc ShComndParse
; ;AN000;
2972 DB 278 + 128 DUP (?
) ; 278 == IBM's ROM requirements