summaryrefslogtreecommitdiff
path: root/v4.0/src/DOS/MISC.ASM
blob: e169f324d1f20fb772e83ba4d28d908c88ce7f0a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
;	SCCSID = @(#)misc.asm	1.1 85/04/10
TITLE MISC - Miscellanious routines for MS-DOS
NAME  MISC
;
; Miscellaneous system calls most of which are CAVEAT
;
;   $SLEAZEFUNC
;   $SLEAZEFUNCDL
;   $GET_INDOS_FLAG
;   $GET_IN_VARS
;   $GET_DEFAULT_DPB
;   $GET_DPB
;   $DISK_RESET
;   $SETDPB
;   $Dup_PDB
;   $CREATE_PROCESS_DATA_BLOCK
;   SETMEM
;   FETCHI_CHECK
;   $GSetMediaID
;
;   Revision  history:
;
;	Created: ARR 30 March 1983
;
;	A000   version 4.00   Jan. 1988
;	A001   D490 -- Change IOCTL subfunctions from 63h, 43h to 66h , 46h

.xlist
;
; get the appropriate segment definitions
;
include dosseg.asm

CODE	SEGMENT BYTE PUBLIC  'CODE'
	ASSUME	SS:DOSGROUP,CS:DOSGROUP

.xcref
INCLUDE DOSSYM.INC
INCLUDE DEVSYM.INC
.cref
.list

ENTRYPOINTSEG	EQU	0CH
MAXDIF		EQU	0FFFH
SAVEXIT 	EQU	10

	i_need	LASTBUFFER,DWORD
	i_need	BuffHead,DWORD
	i_need	INDOS,BYTE
	i_need	SYSINITVAR,BYTE
	i_need	CurrentPDB,WORD
	i_need	CreatePDB,BYTE
	i_need	FATBYTE,BYTE
	i_need	THISCDS,DWORD
	i_need	THISSFT,DWORD
	i_need	FETCHI_TAG,WORD 		 ; for TAG CHECK
	i_need	BUF_HASH_COUNT,WORD		 ;AN000; number of Hash Entries
	i_need	HIGH_SECTOR,WORD		 ;AN000; high word of sector #
	i_need	DOS34_FLAG,WORD 		 ;AN000;
if debug
	I_need	BugLev,WORD
	I_need	BugTyp,WORD
include bugtyp.asm
endif

BREAK <SleazeFunc -- get a pointer to media byte>

;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Return Stuff sort of like old get fat call
; Outputs:
;	DS:BX = Points to FAT ID byte (IBM only)
;		GOD help anyone who tries to do ANYTHING except
;		READ this ONE byte.
;	DX = Total Number of allocation units on disk
;	CX = Sector size
;	AL = Sectors per allocation unit
;	   = -1 if bad drive specified

	procedure   $SLEAZEFUNC,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	MOV	DL,0

entry	$SLEAZEFUNCDL
;Same as above except drive passed in DL (0=default, 1=A, 2=B, ...)

	context DS
	MOV	AL,DL
	invoke	GETTHISDRV		; Get CDS structure
SET_AL_RET:
;	MOV	AL,error_invalid_drive	; Assume error				;AC000;
	JC	BADSLDRIVE
	invoke	DISK_INFO
	JC	SET_AL_RET		; User FAILed to I 24
	MOV	[FATBYTE],AH
; NOTE THAT A FIXED MEMORY CELL IS USED --> THIS CALL IS NOT
; RE-ENTRANT. USERS BETTER GET THE ID BYTE BEFORE THEY MAKE THE
; CALL AGAIN
	MOV	DI,OFFSET DOSGROUP:FATBYTE
	XOR	AH,AH			; AL has sectors/cluster
	invoke	get_user_stack
ASSUME	DS:NOTHING
	MOV	[SI.user_CX],CX
	MOV	[SI.user_DX],BX
	MOV	[SI.user_BX],DI
	MOV	[SI.user_DS],CS 	; stash correct pointer
	return
BADSLDRIVE:
	transfer    FCB_Ret_ERR
EndProc $SLEAZEFUNC
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Get_INDOS_Flag -- Return location of DOS critical-section flag>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Returns location of DOS status for interrupt routines
; Returns:
;	Flag location in ES:BX

	procedure   $GET_INDOS_FLAG,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	invoke	get_user_stack
	MOV	[SI.user_BX],OFFSET DOSGROUP:INDOS
	MOV	[SI.user_ES],SS
	return
EndProc $GET_INDOS_FLAG
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Get_IN_VARS -- Return a pointer to DOS variables>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Return a pointer to interesting DOS variables This call is version
; dependent and is subject to change without notice in future versions.
; Use at risk.
	procedure   $GET_IN_VARS,NEAR
	invoke	get_user_stack
	MOV	[SI.user_BX],OFFSET DOSGROUP:SYSINITVAR
	MOV	[SI.user_ES],SS
	return
EndProc $GET_IN_VARS
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;


BREAK <$Get_Default_DPB,$Get_DPB -- Return pointer to DPB>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Return pointer to drive parameter table for default drive
; Returns:
;	DS:BX points to the DPB
;	AL = 0 If OK, = -1 if bad drive (call 50 only)

	procedure   $GET_DEFAULT_DPB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	MOV	DL,0

	entry	$GET_DPB
; Same as above only drive passed in DL (0=default, 1=A, 2=B, ...)

	context DS
	MOV	AL,DL
	invoke	GETTHISDRV		; Get CDS structure
	JC	ISNODRV 		; no valid drive
	LES	DI,[THISCDS]		; check for net CDS
	TEST	ES:[DI.curdir_flags],curdir_isnet
	JNZ	ISNODRV 		; No DPB to point at on NET stuff
	EnterCrit CritDisk
	invoke	FATRead_CDS		; Force Media Check and return DPB
	LeaveCrit CritDisk
	JC	ISNODRV 		; User FAILed to I 24, only error we
					;   have.
	invoke	get_user_stack
ASSUME	DS:NOTHING
	MOV	[SI.user_BX],BP
	MOV	[SI.user_DS],ES
	XOR	AL,AL
	return

ISNODRV:
	MOV	AL,-1
	return
EndProc $GET_Default_dpb
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;


BREAK <$Disk_Reset -- Flush out all dirty buffers>

	procedure   $DISK_RESET,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

; Inputs:
;	None
; Function:
;	Flush and invalidate all buffers
; Returns:
;	Nothing

	MOV	AL,-1
 entry $DISK_RESET2
	context DS
	EnterCrit   critDisk
	OR	[DOS34_FLAG],FROM_DISK_RESET					;AN000;
	invoke	FLUSHBUF
	AND	[DOS34_FLAG],NO_FROM_DISK_RESET 				;AN000;
;
; We will "ignore" any errors on the flush, and go ahead and invalidate.  This
; call doesn't return any errors and it is supposed to FORCE a known state, so
; let's do it.
;
; Invalidate 'last-buffer' used
;
	MOV	BX,-1
	MOV	WORD PTR [LASTBUFFER+2],BX
	MOV	WORD PTR [LASTBUFFER],BX
;
;	TEST	[DOS34_FLAG],IFS_DRIVE_RESET	 ;AN000;;IFS. from ifs call back ?
;	JZ	FreeDone			 ;AN000;;IFS. no
;	AND	[DOS34_FLAG],NO_IFS_DRIVE_RESET  ;AN000;;IFS. clear the flag
;	LeaveCrit   critDisk			 ;AN000;;IFS.
;	return					 ;AN000;;IFS. return
FreeDone:
	LeaveCrit   critDisk
	MOV	AX,-1
	CallInstall NetFlushBuf,multNET,32
	return
EndProc $DISK_RESET

BREAK <$SetDPB - Create a valid DPB from a user-specified BPB>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
	procedure   $SETDPB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

; Inputs:
;	ES:BP Points to DPB
;	DS:SI Points to BPB
; Function:
;	Build a correct DPB from the BPB
; Outputs:
; ES:BP and DS preserved all others destroyed

	MOV	DI,BP
	ADD	DI,2			; Skip over dpb_drive and dpb_UNIT
	LODSW
	STOSW				; dpb_sector_size
	CMP	BYTE PTR [SI.BPFTCNT-2],0     ; FAT file system drive		;AN000;
	JNZ	yesfat			      ; yes				;AN000;
	MOV	BYTE PTR ES:[DI.dpb_FAT_count-4],0
	JMP	setend			      ; NO				;AN000;
yesfat:
	MOV	DX,AX
	LODSB
	DEC	AL
	STOSB				; dpb_cluster_mask
	INC	AL
	XOR	AH,AH
LOG2LOOP:
	TEST	AL,1
	JNZ	SAVLOG
	INC	AH
	SHR	AL,1
	JMP	SHORT LOG2LOOP
SAVLOG:
	MOV	AL,AH
	STOSB				; dpb_cluster_shift
	MOV	BL,AL
	MOVSW				; dpb_first_FAT Start of FAT (# of reserved sectors)
	LODSB
	STOSB				; dpb_FAT_count Number of FATs
;	OR	AL,AL			; NONFAT ?				;AN000;
;	JZ	setend			; yes, don't do anything                ;AN000;
	MOV	BH,AL
	LODSW
	STOSW				; dpb_root_entries Number of directory entries
	MOV	CL,5
	SHR	DX,CL			; Directory entries per sector
	DEC	AX
	ADD	AX,DX			; Cause Round Up
	MOV	CX,DX
	XOR	DX,DX
	DIV	CX
	MOV	CX,AX			; Number of directory sectors
	INC	DI
	INC	DI			; Skip dpb_first_sector
	MOVSW				; Total number of sectors in DSKSIZ (temp as dpb_max_cluster)
	LODSB
	MOV	ES:[BP.dpb_media],AL	; Media byte
	LODSW				; Number of sectors in a FAT
	STOSW				;AC000;;>32mb dpb_FAT_size
	MOV	DL,BH			;AN000;;>32mb
	XOR	DH,DH			;AN000;;>32mb
	MUL	DX			;AC000;;>32mb Space occupied by all FATs
	ADD	AX,ES:[BP.dpb_first_FAT]
	STOSW				; dpb_dir_sector
	ADD	AX,CX			; Add number of directory sectors
	MOV	ES:[BP.dpb_first_sector],AX

	MOV	CL,BL		       ;F.C. >32mb				;AN000;
	CMP	WORD PTR ES:[BP.DSKSIZ],0	;F.C. >32mb			;AN000;
	JNZ	normal_dpb	       ;F.C. >32mb				;AN000;
	XOR	CH,CH		       ;F.C. >32mb				;AN000;
	MOV	BX,WORD PTR [SI+BPB_BigTotalSectors-BPB_SectorsPerTrack]	;AN000;
	MOV	DX,WORD PTR [SI+BPB_BigTotalSectors-BPB_SectorsPerTrack+2]	;AN000;
	SUB	BX,AX		       ;AN000;;F.C. >32mb
	SBB	DX,0		       ;AN000;;F.C. >32mb
	OR	CX,CX		       ;AN000;;F.C. >32mb
	JZ	norot		       ;AN000;;F.C. >32mb
rott:				       ;AN000;;F.C. >32mb
	CLC			       ;AN000;;F.C. >32mb
	RCR	DX,1		       ;AN000;;F.C. >32mb
	RCR	BX,1		       ;AN000;;F.C. >32mb
	LOOP	rott		       ;AN000;;F.C. >32mb
norot:				       ;AN000;
	MOV	AX,BX		       ;AN000;;F.C. >32mb
	JMP	setend		       ;AN000;;F.C. >32mb
normal_dpb:
	SUB	AX,ES:[BP.DSKSIZ]
	NEG	AX			; Sectors in data area
;;	MOV	CL,BL			; dpb_cluster_shift
	SHR	AX,CL			; Div by sectors/cluster
setend:
	INC	AX
	MOV	ES:[BP.dpb_max_cluster],AX
	MOV	ES:[BP.dpb_next_free],0 ; Init so first ALLOC starts at
					; begining of FAT
	MOV	ES:[BP.dpb_free_cnt],-1 ; current count is invalid.
	return
EndProc $SETDPB
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Create_Process_Data_Block,SetMem -- Set up process data block>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
;
; Inputs:   DX is new segment address of process
;	    SI is end of new allocation block
;
	procedure   $Dup_PDB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING,SS:NOTHING
	MOV	CreatePDB,0FFH		; indicate a new process
	MOV	DS,CurrentPDB
	PUSH	SI
	JMP	SHORT	CreateCopy
EndProc $Dup_PDB

	procedure   $CREATE_PROCESS_DATA_BLOCK,NEAR
ASSUME	DS:NOTHING,ES:NOTHING,SS:NOTHING

; Inputs:
;	DX = Segment number of new base
; Function:
;	Set up program base and copy term and ^C from int area
; Returns:
;	None
; Called at DOS init

	CALL	get_user_stack
	MOV	DS,[SI.user_CS]
	PUSH	DS:[PDB_Block_len]
CreateCopy:
	MOV	ES,DX
	XOR	SI,SI			; copy all 80h bytes
	MOV	DI,SI
	MOV	CX,80H
	REP	MOVSW
; DOS 3.3 7/9/86

	MOV	CX,FilPerProc		; copy handles in case of
	MOV	DI,PDB_JFN_Table	; Set Handle Count has been issued
	PUSH	DS
	LDS	SI,DS:[PDB_JFN_Pointer]
	REP	MOVSB
	POP	DS

; DOS 3.3 7/9/86
	TEST	CreatePDB,0FFh		; Shall we create a process?
	JZ	Create_PDB_cont 	; nope, old style call
;
; Here we set up for a new process...
;

	PUSH	CS			; Called at DOSINIT time, NO SS
	POP	DS
	DOSAssume   CS,<DS>,"MISC/Create_Copy"
	XOR	BX,BX			; dup all jfns
	MOV	CX,FilPerProc		; only 20 of them

Create_dup_jfn:
	PUSH	ES			; save new PDB
	invoke	SFFromHandle		; get sf pointer
	MOV	AL,-1			; unassigned JFN
	JC	CreateStash		; file was not really open
	TEST	ES:[DI].sf_flags,sf_no_inherit
	JNZ	CreateStash		; if no-inherit bit is set, skip dup.
;
; We do not inherit network file handles.
;
	MOV	AH,BYTE PTR ES:[DI].sf_mode
	AND	AH,sharing_mask
	CMP	AH,sharing_net_fcb
	jz	CreateStash
;
; The handle we have found is duplicatable (and inheritable).  Perform
; duplication operation.
;
	MOV	WORD PTR [THISSFT],DI
	MOV	WORD PTR [THISSFT+2],ES
	invoke	DOS_DUP 		; signal duplication
;
; get the old sfn for copy
;
	invoke	pJFNFromHandle		; ES:DI is jfn
	MOV	AL,ES:[DI]		; get sfn
;
; Take AL (old sfn or -1) and stash it into the new position
;
CreateStash:
	POP	ES
	MOV	ES:[BX].PDB_JFN_Table,AL; copy into new place!
	INC	BX			; next jfn...
	LOOP	create_dup_jfn

	MOV	BX,CurrentPDB		; get current process
	MOV	ES:[PDB_Parent_PID],BX	; stash in child
	MOV	[CurrentPDB],ES
	ASSUME	DS:NOTHING
	MOV	DS,BX
;
; end of new process create
;
Create_PDB_cont:
	MOV	BYTE PTR [CreatePDB],0h ; reset flag
	POP	AX

	entry	SETMEM
ASSUME	DS:NOTHING,ES:NOTHING,SS:NOTHING

; Inputs:
;	AX = Size of memory in paragraphs
;	DX = Segment
; Function:
;	Completely prepares a program base at the
;	specified segment.
; Called at DOS init
; Outputs:
;	DS = DX
;	ES = DX
;	[0] has INT int_abort
;	[2] = First unavailable segment
;	[5] to [9] form a long call to the entry point
;	[10] to [13] have exit address (from int_terminate)
;	[14] to [17] have ctrl-C exit address (from int_ctrl_c)
;	[18] to [21] have fatal error address (from int_fatal_abort)
; DX,BP unchanged. All other registers destroyed.

	XOR	CX,CX
	MOV	DS,CX
	MOV	ES,DX
	MOV	SI,addr_int_terminate
	MOV	DI,SAVEXIT
	MOV	CX,6
	REP	MOVSW
	MOV	ES:[2],AX
	SUB	AX,DX
	CMP	AX,MAXDIF
	JBE	HAVDIF
	MOV	AX,MAXDIF
HAVDIF:
	SUB	AX,10H			; Allow for 100h byte "stack"
	MOV	BX,ENTRYPOINTSEG	;	in .COM files
	SUB	BX,AX
	MOV	CL,4
	SHL	AX,CL
	MOV	DS,DX
	MOV	WORD PTR DS:[PDB_CPM_Call+1],AX
	MOV	WORD PTR DS:[PDB_CPM_Call+3],BX
	MOV	DS:[PDB_Exit_Call],(int_abort SHL 8) + mi_INT
	MOV	BYTE PTR DS:[PDB_CPM_Call],mi_Long_CALL
	MOV	WORD PTR DS:[PDB_Call_System],(int_command SHL 8) + mi_INT
	MOV	BYTE PTR DS:[PDB_Call_System+2],mi_Long_RET
	MOV	WORD PTR DS:[PDB_JFN_Pointer],PDB_JFN_Table
	MOV	WORD PTR DS:[PDB_JFN_Pointer+2],DS
	MOV	WORD PTR DS:[PDB_JFN_Length],FilPerProc
;
; The server runs several PDB's without creating them VIA EXEC.  We need to
; enumerate all PDB's at CPS time in order to find all references to a
; particular SFT.  We perform this by requiring that the server link together
; for us all sub-PDB's that he creates.  The requirement for us, now, is to
; initialize this pointer.
;
	MOV	word ptr DS:[PDB_Next_PDB],-1
	MOV	word ptr DS:[PDB_Next_PDB+2],-1
	return

EndProc $CREATE_PROCESS_DATA_BLOCK

;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
	procedure   FETCHI_CHECK,NEAR
ASSUME	DS:NOTHING,ES:NOTHING,SS:NOTHING
	PUSHF
	CMP	CS:[FETCHI_TAG],22642
	JZ	TAG_OK
	invoke	DOSINIT 	      ; go to hell
TAG_OK:
	POPF
	return
EndProc FETCHI_CHECK

BREAK <$GSetMediaID -- get set media ID>
; Inputs:
;	BL= drive number as defined in IOCTL
;	AL= 0 get media ID
;	    1 set media ID
;	DS:DX= buffer containing information
;		DW  0  info level (set on input)
;		DD  ?  serial #
;		DB  11 dup(?)  volume id
;		DB   8 dup(?)  file system type
; Function:
;	Get or set media ID
; Returns:
;	carry clear, DS:DX is filled
;	carry set, error

	procedure   $GSetMediaID,NEAR ;AN000;
ASSUME	DS:NOTHING,ES:NOTHING	      ;AN000;

	MOV	CX,0866H	      ;AN000;MS.; assume get  for IOCTL
	CMP	AL,0		      ;AN001;MS.; get ?
	JZ	doioctl 	      ;AN000;MS.; yes
	CMP	AL,1		      ;AN000;MS.; set ?
	JNZ	errorfunc	      ;AN000;MS.; no
	MOV	CX,0846H	      ;AN001;MS.;
doioctl:			      ;AN000;
	MOV	AL,0DH		      ;AN000;MS.; generic IOCTL
	invoke	$IOCTL		      ;AN000;MS.; let IOCTL take care of it
	return			      ;AN000;MS.;
errorfunc:			      ;AN000;
	error	error_invalid_function;AN000;MS.	; invalid function
EndProc $GSetMediaID		      ;AN000;

CODE	ENDS
END