0
|
1 ********************************************************************
|
|
2 * PipeMan - OS-9 Level Two Named Pipe File Manager
|
|
3 *
|
|
4 * $Id$
|
|
5 *
|
|
6 * Ed. Comments Who YY/MM/DD
|
|
7 * ------------------------------------------------------------------
|
|
8 * Pipeman Modified to Include the SS.Ready KDM 86/02/23
|
|
9 * I$GETSTT Call.
|
|
10 * Major Bug Corrected KDM 86/03/26
|
|
11 * Upgraded to Level II version KDM 88/06/29
|
|
12 * Added new labels and special defs
|
|
13 * Added code to detect EOF in SS.Ready
|
|
14 * Reformatted to make the module easier CJB 88/11/11
|
|
15 * to understand during coding
|
|
16 * Added named pipes, etc for Level 2 upgrade CJB 88/12/03
|
|
17 * Includes SS.SSig, SS.Relea, SS.Attr, SS.FD
|
|
18 * 1 Release 1.0 for Tandy CoCo OS9 CJB 88/12/26
|
|
19
|
|
20 nam PipeMan
|
|
21 ttl OS9 Level Two Named Pipe File Manager
|
|
22
|
|
23 *
|
|
24 * Copyright 1981, 1985, 1986, 1988 by Microware Systems Corporation
|
|
25 * All Rights Reserved
|
|
26 *
|
|
27 * Named pipe code by Burke & Burke.
|
|
28 * All rights assigned to Microware Systems Corporation.
|
|
29 *
|
|
30 * This file contains proprietary information of Microware Systems
|
|
31 * Corporation. Persons accessing this file will be held strictly
|
|
32 * accountable for their use of the information herein.
|
|
33 *
|
|
34
|
|
35 *
|
|
36 * PIPEMAN
|
|
37 *
|
|
38 * Pipe File Manager
|
|
39 *
|
|
40 * WARNING
|
|
41 * -------
|
|
42 *
|
|
43 * Opening an existing named pipe emulates IOMan's I$Close and
|
|
44 * I$Dup calls. This file manager contains subroutines that
|
|
45 * mimic the current operation of IOMan. Any changes to IOMan's
|
|
46 * FMEXEC, I$Close or I$Dup calls must also be made to this code.
|
|
47 *
|
|
48 * Device Driver Static Storage Layout
|
|
49 * -----------------------------------
|
|
50 *
|
|
51 * $00-$01 V.List Pointer in system map to pipe buffer for 1st
|
|
52 * pipe (16 bits).
|
|
53 *
|
|
54 * Pipe Buffer Data Structure
|
|
55 * --------------------------
|
|
56 *
|
|
57 * $00-$01 PP.PD Pointer to shared path descriptor
|
|
58 * $02-$03 PP.Next Pointer to next pipe buffer in system map
|
|
59 * $04-$05 PP.Prev Pointer to previous pipe buffer in system map
|
|
60 * $06-$07 PP.Rsv2 Reserved
|
|
61 *
|
|
62 * $08 PP.Data Data buffer begins at this offset
|
|
63 *
|
|
64 * Path Descriptor Data Structure
|
|
65 * ------------------------------
|
|
66 *
|
|
67 * $00 PD.PD Path number
|
|
68 * $01 PD.MOD Access permissions
|
|
69 * $02 PD.CNT Number of open images (e.g. I$DUP)
|
|
70 * $05 PD.CPR Current process ID
|
|
71 * $06-$07 PD.RGS Address of caller's register stack
|
|
72 * $08-$09 PD.BUF System space pipe buffer base pointer
|
|
73 *** PP.Read must have bit 4 clear; PP.Writ must be PP.Read XOR 4
|
|
74 * $0A PD.Read No bytes -- offset only
|
|
75 * $0A PD.RPID Process ID of reader waiting on signal
|
|
76 * $0B PD.RCT Number of blocked readers
|
|
77 * $0C PD.RSIG Signal to send reader
|
|
78 * $0D PD.REOR Read EOR character
|
|
79 * $0E PD.Writ No bytes -- offset only
|
|
80 * $0E PD.WPID Process ID of writer waiting on signal
|
|
81 * $0F PD.WCT Number of blocked writers
|
|
82 * $10 PD.WSIG Signal to send writer
|
|
83 * $11 PD.WEOR Write EOR character (dummy)
|
|
84 *** End of special section
|
|
85 * $12-$13 PD.End Pointer to end of pipe buffer
|
|
86 * $14-$15 PD.NxtI Next in pointer
|
|
87 * $16-$17 PD.NxtO Next out pointer
|
|
88 * $18 PD.RFlg "Ready" flag
|
|
89 * $19 PD.Wrtn "Written" flag
|
|
90 * $1A-$1B PD.BCnt # queue elements currently bufered
|
|
91 * $1C PD.Own Process ID of pipe original creator
|
|
92 * $1D PD.Keep Non-zero if this pipe has been kept open artificially
|
|
93 * $1E-$1F PD.QSiz Max. size of queue (in elements)
|
|
94 * .
|
|
95 * .
|
|
96 * $20 PD.DTP Device type $02 = PIPE
|
|
97 * $21 PD.ESiz Size of each queue element
|
|
98 * $22-$23 PD.ECnt Max. elements in queue
|
|
99 * $23-$3F PD.Name Pipe name (after moving PD.ECnt to PD.QSiz)
|
|
100 *
|
|
101
|
|
102 page
|
|
103 *
|
|
104 * Global equates
|
|
105 *
|
|
106 ifp1
|
|
107 use defsfile
|
|
108 use pipedefs.l2v3
|
|
109 use rbfdefs
|
|
110 endc
|
|
111
|
|
112 *
|
|
113 * Local Equates
|
|
114 *
|
|
115
|
|
116 XVER equ 3 ;Version
|
|
117
|
|
118 * ASCII CONTROL CHARACTERS
|
|
119
|
|
120 CR equ $0D
|
|
121
|
|
122 * CONDITION CODES
|
|
123
|
|
124 * PIPEMAN SPECIAL OFFSETS.
|
|
125
|
|
126 PM.CPR equ PD.RPID-PD.READ
|
|
127 PM.CNT equ PD.RCT-PD.READ
|
|
128 PM.SIG equ PD.RSIG-PD.READ
|
|
129 PM.EOR equ PD.REOR-PD.READ
|
|
130
|
|
131 * IOMAN special offsets.
|
|
132 *
|
|
133 * This constant is IOMAN release-dependent.
|
|
134 * It is the number of bytes between the entry stack
|
|
135 * pointer and the stacked PD pointer saved by *IOMAN*.
|
|
136 * Currently, the stack looks like this:
|
|
137 *
|
|
138 * A PL
|
|
139 * 9 PH <IOMAN post-SOPEN return address>
|
|
140 * 8 UL
|
|
141 * 7 UH
|
|
142 * 6 YL +
|
|
143 * 5 YH <PD pointer saved by IOMAN>
|
|
144 * 4 XL
|
|
145 * 3 XH
|
|
146 * 2 A
|
|
147 * 1 PL
|
|
148 * SP-> 0 PH <post OPEN/CREATE return address>
|
|
149 * <start of stack to be used by PIPEMAN>
|
|
150
|
|
151 IOMAGIC equ 5 ;5 bytes to PD pointer
|
|
152
|
|
153 * Local pipe buffer equates
|
|
154
|
|
155 CInit equ %00100000 ;Set this bit to override PD queue parameters
|
|
156
|
|
157 * Conditional assembly
|
|
158
|
|
159 ANON set 0 ;Anonymous pipes only
|
|
160 NAMED set 1 ;Anonymous and named pipes
|
|
161 MSGS set 2 ;Both types of pipes, and message queues
|
|
162 WIZBANG set NAMED ;What features are we providing?
|
|
163
|
|
164 NODIR set 0 ;Don't allow DIR on pipe devices
|
|
165 YESDIR set 1 ;Allow DIR on pipe devices
|
|
166 PIPEDIR set YESDIR ;Does DIR work on pipes?
|
|
167
|
|
168 SLOWPD set 0 ;Slow PD location algorithm
|
|
169 QUICKPD set 1 ;Fast PD location algorithm
|
|
170 PDALGO set QUICKPD ;How to convert PD to system path #
|
|
171
|
|
172 RECKLES set 0 ;Don't check for certain errors
|
|
173 CAREFUL set 1 ;Check for certain errors
|
|
174 CAUTION set CAREFUL
|
|
175
|
|
176 page
|
|
177 *
|
|
178 * Module Header
|
|
179 *
|
|
180
|
|
181 edition set 1
|
|
182
|
|
183 mod MODSIZE,MODNAM,FlMgr+Objct,ReEnt+XVER,JmpTbl,$0000
|
|
184
|
|
185 * Module Name
|
|
186
|
|
187 MODNAM fcs "PipeMan"
|
|
188 fcb edition
|
|
189
|
|
190 * Jump table
|
|
191
|
|
192 JmpTbl lbra Create
|
|
193 lbra Open
|
|
194 lbra MakDir
|
|
195 lbra ChgDir
|
|
196 lbra Delete
|
|
197 lbra Seek
|
|
198 lbra Read
|
|
199 lbra Write
|
|
200 lbra ReadLn
|
|
201 lbra WriteLn
|
|
202 lbra GetStt
|
|
203 lbra SetStt
|
|
204 lbra Close
|
|
205
|
|
206 page
|
|
207 *
|
|
208 * Create a named or anonymous pipe
|
|
209 *
|
|
210 * The size of the queue is determined by examining
|
|
211 * the path descriptor, since this information has
|
|
212 * been copied there from the device descriptor.
|
|
213 *
|
|
214 * Reg-U points to stacked registers of user.
|
|
215 * Reg-Y points to path descriptor
|
|
216 *
|
|
217 * If success, carry clear and X points to pipe buffer
|
|
218 *
|
|
219
|
|
220 * Create function allows user to override both element
|
|
221 * count and element size. Override is enabled if if bit
|
|
222 * 5 of the access mode is set. For override, if MS bit
|
|
223 * of Reg-Y is clear, just use Reg-Y as queue element
|
|
224 * count. If MS bit of Reg-Y is set, use LS byte of
|
|
225 * Reg-Y as element size ($00 = no change) and bottom 7
|
|
226 * bits of MS byte of Reg-Y as element count ($00 = no change)
|
|
227
|
|
228 Create equ *
|
|
229
|
|
230 lda R$A,U ;Get access mode
|
|
231 bita #CInit
|
|
232 beq Open
|
|
233
|
|
234 * Handle queue size override
|
|
235
|
|
236 ldd R$Y,U ;Get queue size initializer
|
|
237 bpl SetCnt ; (branch if just setting count)
|
|
238
|
|
239 * Set element size and count
|
|
240
|
|
241 tstb
|
|
242 beq Creat00 ; (branch if using default size)
|
|
243
|
|
244 stb PD.ESiz,Y ;Reg-B = size of each element
|
|
245
|
|
246 Creat00 anda #$7F
|
|
247 beq Open ; (branch if using default count)
|
|
248
|
|
249 tfr a,B
|
|
250 clra
|
|
251
|
|
252 * Set number of elements in queue from Reg-D
|
|
253
|
|
254 SetCnt std PD.ECnt,Y ;Reg-D = number of elements
|
|
255
|
|
256 * Enter here for normal OPEN
|
|
257
|
|
258 Open equ *
|
|
259
|
|
260 * Move number of elements in queue to make room for name
|
|
261
|
|
262 ldd PD.ECnt,Y
|
|
263 std PD.QSiz,Y
|
|
264
|
|
265 * Parse pathname
|
|
266
|
|
267 clrb ;Assume anonymous pipe
|
|
268 clra ;Assume not 1st pipe
|
|
269 ldx R$X,U ;Point at file name in user's space
|
|
270 pshs U,Y,X,D ;Save file name, PD, reg. base, 1st & anon flag
|
|
271
|
|
272 * Caller's Regs Ptr
|
|
273 * PD Ptr
|
|
274 * Path name uptr
|
|
275 * Named flag
|
|
276 * SP-> First flag
|
|
277
|
|
278 os9 F$PrsNam ;Error if driver name (e.g. /pipe) invalid
|
|
279 bcs BadName
|
|
280
|
|
281 * See if named or anonymous pipe requested.
|
|
282
|
|
283 lbsr GtNext ;Return CC=MI if at end of path name
|
|
284 cmpa #'/
|
|
285 beq HasName
|
|
286
|
|
287 * /pipe____
|
|
288 * X Y
|
|
289 * Pipe is anonymous -- set up dummy name in PD.
|
|
290 * Stack must match the named pipe setup
|
|
291
|
|
292 NotName tfr Y,X ;Skip any trailing blanks
|
|
293 os9 F$PrsNam ; (should return carry set)
|
|
294 ldd #1 ;Length of dummy name
|
|
295 pshs Y,X,D
|
|
296
|
|
297 ldy 10,S ;Get PD pointer
|
|
298 clr PD.Name,Y ; and set dummy name
|
|
299
|
|
300 bra GoCheck
|
|
301
|
|
302 * /pipe/foo____
|
|
303 * X Y
|
|
304 * Pipe is named -- check length and flag on stack
|
|
305
|
|
306 HasName tfr Y,X
|
|
307 os9 F$PrsNam ;Scan off the name
|
|
308 bcs BadName
|
|
309
|
|
310 cmpb #NameMax ;Check length of name
|
|
311 bhi BadName
|
|
312
|
|
313 * Length OK. X points to start of name, Y to end of name,
|
|
314 * B has name length.
|
|
315 * Save registers & length, and do final parse to skip white
|
|
316
|
|
317 com 1,S ;Set "named" flag
|
|
318 clra
|
|
319 pshs Y,X,D
|
|
320
|
|
321 tfr Y,X
|
|
322 os9 F$PrsNam ;Error if trying for pipe subdirectory
|
|
323 bcc BadNam2
|
|
324
|
|
325 * /pipe/foo____
|
|
326 * X Y
|
|
327 * Need to get the pipe name into our address space
|
|
328 * Use the PD for a temporary buffer.
|
|
329
|
|
330 NameOK ldx <D.Proc ;Pointer to caller's PD
|
|
331 lda P$Task,X ; get caller's DAT image #
|
|
332 ldx <D.SysPrc ;Pointer to our PD
|
|
333 ldb P$Task,X ; get system's DAT image #
|
|
334 ldy 0,S ;Byte count
|
|
335 ldx 2,S ;Source address
|
|
336 ldu 10,S ;Get PD pointer and convert to . . .
|
|
337 leau PD.Name,U ;Destination address
|
|
338 lbsr MovSet ;Move block, set MSB of last byte.
|
|
339
|
|
340 * Wow! Everybody's in the same address space now.
|
|
341
|
|
342 * Since this is a named pipe, force mode to UPDATE.
|
|
343 * Also, do not permit DIR. access
|
|
344
|
|
345 ldx 10,S
|
|
346 lda PD.MOD,X
|
|
347 bita #DIR.
|
|
348 bne BadNam2
|
|
349
|
|
350 ora #(READ.+WRITE.)
|
|
351 sta PD.MOD,X
|
|
352
|
|
353 * See if this is an existing pipe. To do this, we
|
|
354 * must get the linked list head pointer from the
|
|
355 * device driver's static storage.
|
|
356 *
|
|
357 * Stack looks like this:
|
|
358 *
|
|
359 * C 2 Sysmap Reg Pointer
|
|
360 * A 2 Sysmap PD Pointer
|
|
361 * 8 2 Usrmap Path name pointer
|
|
362 * 7 1 Named pipe flag
|
|
363 * 6 1 First pipe flag
|
|
364 * 4 2 Usrmap Pipe name end pointer
|
|
365 * 2 2 Usrmap Pipe name start pointer
|
|
366 * 0 2 Name length
|
|
367 * sp->
|
|
368
|
|
369 GoCheck ldx 10,S ;Get PD pointer
|
|
370 ldx PD.DEV,X ;Get device table pointer
|
|
371 ldu V$Stat,X ;Get static storage pointer
|
|
372 ldx V.List,U ;Get pointer to head of pipe bfr linked list
|
|
373 bne Not1st ; (reg-X = $0000 if no previous pipes)
|
|
374
|
|
375 * This is the 1st pipe for this descriptor.
|
|
376 * Reg-X = $0000
|
|
377 * Set flag and process as a new pipe.
|
|
378
|
|
379 com 6,S ;This is the first pipe
|
|
380 bra NewPipe ;This is a new pipe
|
|
381
|
|
382 * No time like the present for some error handlers
|
|
383
|
|
384 * Generic error, cleaning stack
|
|
385
|
|
386 BadXit2 leas 8,S
|
|
387 coma ;Set carry
|
|
388 rts
|
|
389
|
|
390 * Bad Pathname -- 2 versions, depending on
|
|
391 * how much junk is on the stack.
|
|
392
|
|
393 BadNam2 leas 6,S ;Clean stack
|
|
394 BadName ldb #E$BPNam
|
|
395 bra BadXit2
|
|
396
|
|
397 * Not Enough System RAM
|
|
398
|
|
399 TooBig ldb #E$NoRAM
|
|
400 BadExit leas 6,S ;Clean stack
|
|
401 bra BadXit2
|
|
402
|
|
403 * Look up the pipe name, unless the pipe is anonymous.
|
|
404 *
|
|
405 * Reg-U points to driver static storage.
|
|
406 * Reg-X points to next pipe buffer to check.
|
|
407
|
|
408 Not1st tst 7,S ;Unnamed pipes are always new
|
|
409 beq NewPipe
|
|
410
|
|
411 ldy 10,S ;point at PD
|
|
412 leay PD.Name,Y ; then point at name in PD
|
|
413
|
|
414 * Main loop. Always at least 1 pipe buffer to check first time through.
|
|
415 * Reg-X points to buffer to check, or $0000 if none.
|
|
416 * Reg-Y points to desired pipe name.
|
|
417
|
|
418 ChkLoop pshs X
|
|
419 ldx PP.PD,X ;Point at PD for this pipe buffer
|
|
420 leax PD.Name,X ; and then point at name stored in PD
|
|
421 lbsr Compare
|
|
422 puls X
|
|
423 lbeq OldPipe ; (got a match)
|
|
424
|
|
425 ldd PP.Next,X ;Don't fall off the edge
|
|
426 beq NewPipe ; (end of list)
|
|
427
|
|
428 tfr D,X ;Advance to next buffer
|
|
429 bra ChkLoop
|
|
430
|
|
431 * Pipe name not found. Create a new pipe.
|
|
432 *
|
|
433 * Reg-U points to driver static storage.
|
|
434 * Reg-X points to last pipe buffer checked ($0000 if 1st pipe)
|
|
435
|
|
436 NewPipe ldy 10,S ;Get PD pointer
|
|
437
|
|
438 ifeq (PIPEDIR-YESDIR)
|
|
439 lda PD.MOD,Y ;Check pipe attributes
|
|
440 bita #DIR.
|
|
441 beq NEWP1
|
|
442
|
|
443 * Initialize pipe characteristics for DIR. bit set
|
|
444
|
|
445 lbsr SizDirP
|
|
446 * beq XYZZY ;Special if no pipes created
|
|
447 endc
|
|
448
|
|
449 * Normal (non-dir) processing
|
|
450
|
|
451 NewP1 ldd PD.QSiz,Y ;Get max element count
|
|
452 bne DoNew ; (graceful operation if no count)
|
|
453
|
|
454 * Default pipe parameters if none in PD.
|
|
455
|
|
456 ldd #$0100 ;Assume 256 byte buffer, 1 byte element
|
|
457 sta PD.ESiz,Y ;Reg-A = 1
|
|
458 subd #PP.Data ;Compute elements for total size = 256
|
|
459 std PD.QSiz,Y Use parameters in PD
|
|
460
|
|
461 DoNew lbsr ECtoBC ;Convert element count to byte count in D
|
|
462 bcs TooBig ; (carry set if too big)
|
|
463
|
|
464 * Carry has exit status
|
|
465 * Reg-D = # bytes for queue, w/o overhead
|
|
466 * Reg-X = previous buffer
|
|
467 * Reg-U = driver static storage
|
|
468
|
|
469 tfr U,Y ;Save static storage pointer
|
|
470
|
|
471 addd #PP.Data ;Add in overhead
|
|
472 bcs TooBig
|
|
473
|
|
474 pshs D ;Save buffer size
|
|
475 os9 F$SrqMem ;Attempt to allocate buffer
|
|
476 puls D ;Recover size, clean stack, lose error msg
|
|
477 bcs TooBig
|
|
478
|
|
479 * Found enough memory for pipe buffer.
|
|
480 *
|
|
481 * Pointer in Reg-U
|
|
482 * Size in Reg-D
|
|
483 * Previous buffer in Reg-X.
|
|
484 * Driver static storage in Reg-Y.
|
|
485 *
|
|
486 * Initialize the buffer
|
|
487
|
|
488 pshs U,D ;Save buffer pointer & size
|
|
489
|
|
490 * Clear pipe buffer header
|
|
491
|
|
492 ldb #PP.Data ;Size of header
|
|
493 ClrBuf clr ,U+
|
|
494 decb
|
|
495 bne ClrBuf
|
|
496
|
|
497 puls D,U
|
|
498
|
|
499 * Initialize path descriptor and other fields of pipe buffer
|
|
500 * for new pipe.
|
|
501 *
|
|
502 * Pointer in Reg-U
|
|
503 * Size in Reg-D
|
|
504 * Previous buffer in Reg-X.
|
|
505 * Driver static storage in Reg-Y.
|
|
506 *
|
|
507 * IOMan has already prefilled the PD to $00 and
|
|
508 * has set PD.CNT for this path to 1.
|
|
509
|
|
510 pshs Y,X ;Save static storage pointer & prev.buff
|
|
511
|
|
512 ldy (4+10),S ;Get PD pointer to Reg-Y
|
|
513 sty PP.PD,U ;Save pointer to PD in pipe buffer
|
|
514
|
|
515 leax D,U ;Point to end of pipe.buff + 1
|
|
516 stx PD.End,Y
|
|
517
|
|
518 leax PP.Data,U ;Initial Next in & Next out pointers
|
|
519 stx PD.NxtI,Y
|
|
520 stx PD.NxtO,Y
|
|
521
|
|
522 ldx <D.Proc ;Save ID of original creator
|
|
523 lda P$ID,X
|
|
524 sta PD.Own,Y
|
|
525
|
|
526 puls Y,X ;Recover static storage pointer and prev.buff
|
|
527
|
|
528 stx PP.Prev,U ;Save address of previous buffer ($0 if none)
|
|
529 bne LinkIn ; (branch if this isn't the 1st pipe)
|
|
530
|
|
531 * Special -- this is the first pipe.
|
|
532 * Set PP.Next to $0000 and store buffer address in device memory.
|
|
533 *
|
|
534 * Reg-U = pointer to new buffer.
|
|
535 * Reg-X = $0000.
|
|
536 * Reg-Y = static storage
|
|
537
|
|
538 ** Zero prefill of PP header covers this
|
|
539 ** stx PP.Next,U ;No next buffer
|
|
540 ** stx PP.Prev,U ;No previous buffer
|
|
541 stu V.List,Y ;Point driver static at this buffer
|
|
542 bra IsAsOld
|
|
543
|
|
544 * There are other named pipes. Link this one in correctly
|
|
545 * after the last one checked.
|
|
546 *
|
|
547 * Reg-U = pointer to new buffer.
|
|
548 * Reg-X = Pointer to previous buffer.
|
|
549 * Reg-Y = static storage.
|
|
550
|
|
551 LinkIn ldd PP.Next,X ;Get old's next (could be $0000)
|
|
552 stu PP.Next,X ;Set old's next pointing at new
|
|
553 std PP.Next,U ;Set new's next where old's was
|
|
554 stx PP.Prev,U ;Set new's prev pointing at old
|
|
555 pshs X,D
|
|
556 ldx 0,S ;Point X at original old's next
|
|
557 beq Link9 ; (branch if no next -- $0000 set already)
|
|
558 stu PP.Prev,X ;Set prev of old's original next to new
|
|
559 Link9 puls D,X
|
|
560
|
|
561 * Now we look pretty much like a new access to an old pipe.
|
|
562 * Fix up pointers to match "old pipe" code
|
|
563
|
|
564 IsAsOld tfr U,X ;Point Reg-X at pipe buffer
|
|
565 tfr Y,U ;Point Reg-U at driver static storage
|
|
566 ldy 10,S ;Recover PD pointer
|
|
567 stx PD.BUF,Y ;Set up buffer pointer in PD
|
|
568 bra OpnXit ; (go to common trailer code)
|
|
569
|
|
570 * Pipe name found. Set up to access an old pipe.
|
|
571 *
|
|
572 * Reg-U points to driver static storage.
|
|
573 * Reg-X points to matching pipe buffer.
|
|
574 *
|
|
575 * We need to make this look like a DUP call, so
|
|
576 * there's some nasty code here to give back the
|
|
577 * PD allocated by IOMan and go get the "original"
|
|
578 * PD for this named pipe.
|
|
579
|
|
580 OldPipe equ *
|
|
581
|
|
582 *** ***
|
|
583 * WARNING -- This code emulates IOMan's I$Close and I$Dup *
|
|
584 *** ***
|
|
585
|
|
586 *
|
|
587 * Processing to give back the new path descriptor and use
|
|
588 * the original PD that the pipe was opened with.
|
|
589 *
|
|
590 * Fake close of PD passed by IOMan
|
|
591 * Fake dup of named pipe's "master" PD
|
|
592 * Fix PD pointer saved on IOMAN's stack
|
|
593 *
|
|
594 * All of the subroutines preserve all regs, except as noted
|
|
595 * This section MUST preserve Reg-X and Reg-U. There must
|
|
596 * be exactly 14 bytes on the stack at this point.
|
|
597
|
|
598 ldy 10,S ;Get IOMAN PD pointer (original Reg-Y)
|
|
599
|
|
600 * Detach the path.
|
|
601
|
|
602 pshs U
|
|
603 ldu PD.DEV,Y ; Get device pointer
|
|
604 os9 I$Detach ; Detach to compensate for IOMAN Attach
|
|
605 puls U
|
|
606
|
|
607 * Decrement use count
|
|
608
|
|
609 dec PD.CNT,Y ;Decrement use count
|
|
610
|
|
611 * Give back unwanted PD
|
|
612
|
|
613 *** This is the way I did it originally
|
|
614 pshs X
|
|
615 lda PD.PD,Y ;Get system path number
|
|
616 ldx <D.PthDBT ;Point at path table index
|
|
617 os9 F$Ret64 ; and give back descriptor
|
|
618 puls X
|
|
619 *** This is the way the OSK named pipe manager does it.
|
|
620 *** I had to translate, of course, but the translated
|
|
621 *** version doesn't work right.
|
|
622 * pshs U,X
|
|
623 * lda PD.PD,Y ;Get system path #
|
|
624 * ldx <D.PthDBT ;Point at path table index
|
|
625 * ldu <D.SysDis ;Point at system SVC dispatch table
|
|
626 * jsr [(F$Ret64*2),U] ;Do a RET64
|
|
627 * puls X,U
|
|
628
|
|
629 * Stack clean.
|
|
630 * Update IOMAN variables.
|
|
631 * Reg-Y = where IOMAN thinks the PD is.
|
|
632
|
|
633 ifeq (CAUTION-CAREFUL)
|
|
634 cmpy (14+IOMAGIC),S ;Make sure the stack looks right (PD matches)
|
|
635 beq OKMagic
|
|
636
|
|
637 * Stack is wrong; declare bad magic!
|
|
638
|
|
639 comb
|
|
640 ldb #E$Bug
|
|
641 leas 14,S
|
|
642 rts
|
|
643 endc
|
|
644
|
|
645 * Stack is right; go fix PD pointers
|
|
646
|
|
647 OKMagic ldy PP.PD,X ;Get PD pointer of existing named pipe PD.
|
|
648 sty 10,S ;Point PD pointer at existing PD
|
|
649 sty (14+IOMAGIC),S ;Save new IOMAN PD pointer in IOMAN stack
|
|
650 inc PD.CNT,Y ;Increment use count
|
|
651
|
|
652 * End of dangerous code
|
|
653 * This section MUST have preserved Reg-X and Reg-U
|
|
654
|
|
655 * Exit code.
|
|
656 *
|
|
657 * Reg-U points to driver static storage.
|
|
658 * Reg-Y points to PD.
|
|
659 * Reg-X points to matching pipe buffer.
|
|
660 *
|
|
661 * Advance caller's path name pointer
|
|
662
|
|
663 OpnXit equ *
|
|
664
|
|
665 * Fix use count based on PD.Keep
|
|
666
|
|
667 lda PD.CNT,Y
|
|
668 suba PD.Keep,Y
|
|
669 sta PD.CNT,Y ;Get rid of any artificial openings
|
|
670 clr PD.Keep,Y
|
|
671
|
|
672 ifeq (PIPEDIR-YESDIR)
|
|
673 * Handle prefill of pipe directory buffer
|
|
674
|
|
675 lda PD.Mod,Y ;Is this a DIR. open?
|
|
676 bita #DIR.
|
|
677 beq OpnXt2
|
|
678
|
|
679 lbsr FilDirP ;Send directory info to pipe
|
|
680 endc
|
|
681
|
|
682 OpnXt2 ldu 12,S ;Point at caller's registers
|
|
683 ldd 4,S ;Get revised path name pointer
|
|
684 std R$X,U
|
|
685
|
|
686 leas 14,S ;Clean the stack
|
|
687
|
|
688 * Successful exit. Reg-X points to pipe buffer.
|
|
689
|
|
690 clrb
|
|
691 rts
|
|
692
|
|
693 page
|
|
694 *
|
|
695 * Compare pipe names.
|
|
696 *
|
|
697 * Can't use F$CmpNam here because the strings
|
|
698 * are in system space.
|
|
699 *
|
|
700 * Path names are pointed to by Reg-X and Reg-Y.
|
|
701 * Case is ignored. Returns NE if not equal, else
|
|
702 * EQ.
|
|
703 *
|
|
704
|
|
705 Compare pshs Y,X,A ;Reg-A is temp. storage
|
|
706
|
|
707 * Main comparison loop
|
|
708
|
|
709 Cmp001 lda ,X+
|
|
710 anda #%11011111 ;Cheap and fast TOUPPER
|
|
711 sta 0,S
|
|
712
|
|
713 lda ,Y+
|
|
714 anda #%11011111 ;Cheap and fast TOUPPER
|
|
715 bmi Cmp.Y ; (exit if we find end of Y-string)
|
|
716
|
|
717 cmpa 0,S
|
|
718 beq Cmp001
|
|
719
|
|
720 * Names don't match. Return CC=NE
|
|
721
|
|
722 puls A,X,Y,PC
|
|
723
|
|
724 * End of "Y" string. "X" character either matches or
|
|
725 * it doesn't. Return CC accordingly.
|
|
726
|
|
727 Cmp.Y cmpa 0,S
|
|
728 puls A,X,Y,PC
|
|
729
|
|
730 *
|
|
731 * Convert element count in D to byte count in D.
|
|
732 * Return carry set if too big.
|
|
733 *
|
|
734 * Reg-Y = PD pointer
|
|
735 * Reg-D = Element count
|
|
736 *
|
|
737
|
|
738 ECtoBC pshs D
|
|
739 lda PD.ESiz,Y ;Get size of each element
|
|
740 ldb 0,S ;Get MSB of element count
|
|
741 mul
|
|
742 pshs D
|
|
743 lda PD.ESiz,Y ;Get size of each element
|
|
744 ldb (2+1),S ;Get LSB of element count
|
|
745 mul
|
|
746 adda 1,S ;C-bit set if too big
|
|
747 tst ,S++ ;Z-bit clear if too big, C-bit OK
|
|
748 leas 2,S
|
|
749 bcs EB.err
|
|
750 bne EB.err
|
|
751
|
|
752 * OK exit
|
|
753 andcc #^Carry
|
|
754 rts
|
|
755
|
|
756 * Error exit
|
|
757 EB.err orcc #Carry
|
|
758 rts
|
|
759
|
|
760 * Get next character of path name.
|
|
761 * Reg-Y set up as if just did a PRSNAM.
|
|
762
|
|
763 GtNext ldx <D.Proc
|
|
764 ldb P$Task,X
|
|
765 tfr Y,X
|
|
766 os9 F$LDABX
|
|
767 rts
|
|
768
|
|
769 page
|
|
770 *
|
|
771 * Error hook
|
|
772 *
|
|
773 MAKDIR equ *
|
|
774 CHGDIR equ *
|
|
775 UNKNOWN comb
|
|
776 ldb #E$UNKSVC
|
|
777 rts
|
|
778
|
|
779 page
|
|
780 *
|
|
781 * Close a pipe
|
|
782 *
|
|
783 * If there are any other pipe users, leave the pipe
|
|
784 * around. Also, if the pipe is named and contains
|
|
785 * any data, leave the pipe around even if there are
|
|
786 * no remaining pipe users.
|
|
787 *
|
|
788 * PD.Keep will be non-zero if the pipe has been kept
|
|
789 * open artificially.
|
|
790 *
|
|
791 * This routine is called each time a path to the pipe
|
|
792 * is closed.
|
|
793 *
|
|
794
|
|
795 CLOSE equ *
|
|
796
|
|
797 * Account for extra use count if pipe artificially kept open.
|
|
798 * Then see if this is the last user of the pipe
|
|
799
|
|
800 lda PD.Keep,Y ;Account for extra pipe images
|
|
801 nega
|
|
802 clr PD.Keep,Y
|
|
803 adda PD.CNT,Y
|
|
804 sta PD.CNT,Y ;Set correct PD.CNT value
|
|
805 bne READERS ; and branch if any users left
|
|
806
|
|
807 * No open paths to this pipe.
|
|
808 * If it's named and not empty, leave it around anyway.
|
|
809
|
|
810 tst PD.Name,Y ;Named pipe?
|
|
811 beq CLOSE2
|
|
812
|
|
813 ldd PD.BCnt,Y ;How many elements buffered on named pipe?
|
|
814 beq CLOSE2
|
|
815
|
|
816 * Leave this named pipe around for a while
|
|
817
|
|
818 inc PD.CNT,Y ;Create an extra image
|
|
819 inc PD.Keep,Y ; and remember that we did it
|
|
820 bra CLOXIT
|
|
821
|
|
822 * Delete the pipe.
|
|
823 * Y = PD pointer.
|
|
824
|
|
825 CLOSE2 bsr ZapPipe
|
|
826 bra CloXit ;No error
|
|
827
|
|
828 * Open paths left. What kind?
|
|
829
|
|
830 READERS cmpa PD.RCT,Y ;Are all open paths readers?
|
|
831 bne WRITERS
|
|
832
|
|
833 * All other open paths are readers.
|
|
834 * Send signal to next reader (let him read a bit)
|
|
835
|
|
836 leax PD.Read,Y
|
|
837 bra SENDSIG
|
|
838
|
|
839 * Not all readers. What kind?
|
|
840
|
|
841 WRITERS cmpa PD.WCT,Y ;Are all open paths writers?
|
|
842 bne CloXit
|
|
843
|
|
844 * All other open paths are writers.
|
|
845 * Send signal to next writer (let him write a bit)
|
|
846
|
|
847 leax PD.Writ,Y
|
|
848
|
|
849 * Send signal to next reader or writer
|
|
850
|
|
851 SENDSIG lda PM.CPR,X ;Process ID to signal
|
|
852 beq CLOXIT
|
|
853
|
|
854 ldb PM.SIG,X ;Signal code to send
|
|
855 beq CLOXIT
|
|
856
|
|
857 * Committed to send signal: clear the flag and send it
|
|
858
|
|
859 clr PM.SIG,X ;Force no pending signal
|
|
860 os9 F$SEND
|
|
861
|
|
862 * Done with close
|
|
863
|
|
864 CLOXIT clrb
|
|
865 rts
|
|
866
|
|
867 page
|
|
868 *
|
|
869 * Delete a named pipe.
|
|
870 *
|
|
871 * Reg-Y = PD
|
|
872 * Reg-U = caller's registers
|
|
873 * Reg-X = path name
|
|
874 *
|
|
875
|
|
876 Delete lda #Read.
|
|
877 sta PD.MOD,Y ;Need only READ permission
|
|
878 pshs U,Y,X,A ;***Match stack set up by IOMAN
|
|
879 lbsr Open ;Try to open the pipe
|
|
880 puls U,Y,X,A ;***Clean up special stack
|
|
881 bcs BadDel
|
|
882
|
|
883 * Disconnect from pipe, but keep pointer.
|
|
884 * Then check to see if we're the only user.
|
|
885 *
|
|
886 * Note -- The call to OPEN updated PD.CNT
|
|
887 * and cleared PD.Keep.
|
|
888
|
|
889 dec PD.CNT,Y ;Don't count ourselves
|
|
890 beq DoDel ;If count is zero, OK to delete
|
|
891
|
|
892 * Pipe is in use. Return E$FNA
|
|
893
|
|
894 FNAXIT comb
|
|
895 ldb #E$FNA
|
|
896
|
|
897 * Exit w/ carry set and error code in B
|
|
898
|
|
899 BadDel rts
|
|
900
|
|
901 * Perform the delete.
|
|
902
|
|
903 DoDel bsr ZapPipe
|
|
904 clrb
|
|
905 rts
|
|
906
|
|
907 *
|
|
908 * Return all memory for the pipe buffer specified
|
|
909 * in the path descriptor, and remove it from the linked list.
|
|
910 *
|
|
911 * Reg-Y = PD pointer
|
|
912 * Pipe buffer pointer is at PD.BUF,Y
|
|
913 *
|
|
914
|
|
915 ZapPipe ldu PD.DEV,Y ;Get device table pointer
|
|
916 ldu V$Stat,U ;Get static storage pointer
|
|
917
|
|
918 ldx PD.BUF,Y ;Point to pipe's buffer
|
|
919 ldd PP.Next,X ;Save pointer to current and next in list
|
|
920 pshs D
|
|
921 ldd PP.Prev,X ;Save pointer to previous in list
|
|
922 pshs D
|
|
923
|
|
924 * Reg-D has pointer to previous. If zero, we're zapping head of list.
|
|
925 * Z-bit is already set accordingly
|
|
926
|
|
927 bne OldHead
|
|
928
|
|
929 * New head of list.
|
|
930 * Reg-X still points to buffer to be deleted
|
|
931
|
|
932 ldd 2,S ;Get pointer to next (may be $0000)
|
|
933 std V.List,U ; and set as new head
|
|
934 pshs X,D
|
|
935 ldx 0,S ;Point Reg-X at next, set CC
|
|
936 beq Zap9
|
|
937 clr (PP.Prev+0),X ; and set no prev for next
|
|
938 clr (PP.Prev+1),X
|
|
939 Zap9 puls D,X ;Point back at pipe to delete
|
|
940 bra ZapIt
|
|
941
|
|
942 * No new head of list. Just delete from linked list.
|
|
943 * We know there is a previous buffer.
|
|
944 *
|
|
945 * Reg-X points to buffer to be deleted.
|
|
946 * Reg-D points to previous buffer.
|
|
947
|
|
948 OldHead ldu PP.Next,X ;Get U pointing at our next (may be $0000)
|
|
949 exg D,X ;Point X at our prev, D at us
|
|
950 stu PP.Next,X ;Save new next for out prev
|
|
951 beq Zap8
|
|
952 stx PP.Prev,U ;Point our next's prev at our original prev
|
|
953 Zap8 exg D,X
|
|
954
|
|
955 * All cleaned up. Give back the buffer
|
|
956 * Reg-X points to buffer, Reg-Y points to PD.
|
|
957
|
|
958 ZapIt ldd PD.End,Y
|
|
959 pshs X
|
|
960 subd 0,S ;Get total bytes to Reg-D
|
|
961 puls U ;Point at buffer, clean stack
|
|
962 os9 F$SRtMem
|
|
963
|
|
964 * Exit with whatever error F$SRtMem produces
|
|
965
|
|
966 leas 4,S ;Clean stack
|
|
967 rts
|
|
968
|
|
969 page
|
|
970 *
|
|
971 * Dummy hook
|
|
972 *
|
|
973 SEEK equ *
|
|
974 Dummy clrb
|
|
975 rts
|
|
976
|
|
977 page
|
|
978 *
|
|
979 * GETSTT processing
|
|
980 *
|
|
981 * Supports the following codes:
|
|
982 *
|
|
983 * SS.Opt Option section
|
|
984 * SS.Ready # bytes in queue
|
|
985 * SS.Siz Size of queue
|
|
986 * SS.EOF Queue empty
|
|
987 * SS.FD Bogus file descriptor (WIZBANG==MSGS)
|
|
988 * SS.ScSiz Screen Size
|
|
989 *
|
|
990 * SS.Opt handled in IOMAN, etc.
|
|
991 * SS.Ready code by Kent Meyers, modified by Chris Burke
|
|
992 * SS.Siz, SS.EOF, SS.FD, SS.ScSiz by Chris Burke
|
|
993 *
|
|
994
|
|
995 GETSTT lda R$B,U Get User B Register ++
|
|
996 cmpa #SS.READY Test for Ready Call ++
|
|
997 bne NotSSRDY
|
|
998
|
|
999 * Process SS.Rdy -- return # elements in queue
|
|
1000 * If more than 255, return 255.
|
|
1001
|
|
1002 G.Rdy ldb #255
|
|
1003 tst (PD.BCnt+0),Y
|
|
1004 bne G.Rdy0 ;Accomodate large queues (256 or more bytes)
|
|
1005 ldb (PD.BCnt+1),X ;Get element count LSB
|
|
1006
|
|
1007 * Reg-B has LSB of element count, CC set based on value
|
|
1008
|
|
1009 beq RDNRDY ;Not Ready if no characters
|
|
1010
|
|
1011 G.Rdy0 stb R$B,U ;Return count in B
|
|
1012
|
|
1013 SST.OK equ *
|
|
1014 SST.Ign equ *
|
|
1015
|
|
1016 G.OK clrb No Error ++
|
|
1017 tfr CC,A
|
|
1018 sta R$CC,U
|
|
1019 rts Return ++
|
|
1020
|
|
1021 * No characters for SS.Ready
|
|
1022
|
|
1023 RDNRDY tst PD.Wrtn,Y Anybody writing to pipe?
|
|
1024 bne NOTEOF (not OK if so)
|
|
1025
|
|
1026 * No writer
|
|
1027
|
|
1028 ldb PD.CNT,Y Exactly one path open to pipe?
|
|
1029 decb
|
|
1030 bne NOTEOF (OK if no, e.g. nobody or > 1)
|
|
1031
|
|
1032 * Internal error
|
|
1033
|
|
1034 IntErr comb
|
|
1035 ldb #255
|
|
1036 rts
|
|
1037
|
|
1038 NOTEOF comb Set Error Flag ++
|
|
1039 ldb #E$NOTRDY Get Error Code ++
|
|
1040 rts Return ++
|
|
1041
|
|
1042 * Not SS.Ready. Check for SS.Siz
|
|
1043
|
|
1044 NotSSRdy cmpa #SS.Size Test for Size call
|
|
1045 bne NotSSSiz
|
|
1046
|
|
1047 * Process SS.Siz -- return size of queue in ELEMENTS.
|
|
1048
|
|
1049 G.Siz ldd PD.QSiz,Y ;Get max. # of queue elements
|
|
1050 std R$U,U
|
|
1051 clr (R$X+0),U Set 16 MSB's to $0000
|
|
1052 clr (R$X+1),U
|
|
1053 GOK001 bra G.OK
|
|
1054
|
|
1055 * Not SS.Siz. Check for SS.EOF
|
|
1056
|
|
1057 NotSSSiz cmpa #SS.EOF
|
|
1058 bne NotSSEOF
|
|
1059
|
|
1060 * Process SS.EOF
|
|
1061 * Handle like SS.Rdy, but preserve Reg-B
|
|
1062
|
|
1063 G.EOF bsr G.Siz
|
|
1064 ldb #0 ;NOT clrb -- preserve carry
|
|
1065 stb R$B,U
|
|
1066 bcc G.OK ;No error if ready
|
|
1067
|
|
1068 ldb #E$EOF ;Carry is already set
|
|
1069 rts
|
|
1070
|
|
1071 * Not SS.EOF. Check for SS.FD
|
|
1072
|
|
1073 ifeq (PIPEDIR-YESDIR)
|
|
1074 NotSSEOF cmpa #SS.FD
|
|
1075 bne NotSSFD
|
|
1076
|
|
1077 * Process SS.FD
|
|
1078
|
|
1079 lbsr DoSSFD
|
|
1080 bra G.OK ;Successful always
|
|
1081 else
|
|
1082 NotSSEOF equ *
|
|
1083 endc
|
|
1084
|
|
1085 * Not SS.FD. Check for SS.ScSiz
|
|
1086
|
|
1087 NotSSFD cmpa #SS.ScSiz ;Force UNKNOWN here
|
|
1088 lbeq UnKnown
|
|
1089
|
|
1090 NotSCSZ equ *
|
|
1091
|
|
1092 NotSSAT equ *
|
|
1093
|
|
1094 * Process unknown GETSTT
|
|
1095
|
|
1096 * lbra UNKNOWN
|
|
1097 bra G.OK
|
|
1098 * bra NotEOF
|
|
1099
|
|
1100 page
|
|
1101 *
|
|
1102 * SETSTT processing
|
|
1103 *
|
|
1104 * Supports the following codes:
|
|
1105 *
|
|
1106 * SS.Opt Option section
|
|
1107 * SS.Siz No effect unless size=0; then clears pipe buffer
|
|
1108 * SS.FD No effect
|
|
1109 * SS.SSig Set signal on data available
|
|
1110 * SS.Relea Release signal
|
|
1111 *
|
|
1112 * SS.Opt handled in IOMAN, etc.
|
|
1113 * SS.Siz, SS.SSig, SS.Relea by Chris Burke, modified
|
|
1114 * from OSK.
|
|
1115 *
|
|
1116
|
|
1117 SetStt lda R$B,U Get User B Register ++
|
|
1118 cmpa #SS.Opt
|
|
1119 beq SST.Ign ; (ignore)
|
|
1120 cmpa #SS.FD
|
|
1121 beq SST.Ign
|
|
1122
|
|
1123 * Check for SS.SIZ
|
|
1124
|
|
1125 cmpa #SS.Size
|
|
1126 bne NoS.Siz
|
|
1127
|
|
1128 ldd R$U,U ;Get caller's size
|
|
1129 bne SST.Ign
|
|
1130
|
|
1131 * Clear the pipe
|
|
1132
|
|
1133 ldx PD.Buf,Y
|
|
1134 leau PP.Data,X
|
|
1135 stu PD.NxtI,Y
|
|
1136 stu PD.NxtO,Y
|
|
1137 clr (PD.BCnt+0),Y
|
|
1138 clr (PD.BCnt+1),Y
|
|
1139 clr PD.RFlg,Y
|
|
1140 clr PD.Wrtn,Y
|
|
1141
|
|
1142 QST.OK bra SST.OK
|
|
1143
|
|
1144 * Check for SS.SSig
|
|
1145
|
|
1146 NoS.Siz cmpa #SS.SSig
|
|
1147 bne NoS.Sig
|
|
1148
|
|
1149 leax PD.Read,Y ;Point at read packet
|
|
1150 tst PM.Cpr,X ;Error if already somebody waiting
|
|
1151 bne NOTEOF
|
|
1152
|
|
1153 * Set signal trap
|
|
1154
|
|
1155 lda PD.CPR,Y ;Set process ID
|
|
1156 sta PM.CPR,X
|
|
1157 lda (R$X+1),U ;Get signal code
|
|
1158 sta PM.Sig,X
|
|
1159 tst PD.BCnt,Y ;Immediate signal if
|
|
1160 lbne SendSig
|
|
1161
|
|
1162 bra QST.OK
|
|
1163
|
|
1164 * Check for release of signal
|
|
1165
|
|
1166 NoS.Sig cmpa #SS.Relea
|
|
1167 bne NoS.Rel
|
|
1168
|
|
1169 leax PD.Read,Y ;Point at read packet
|
|
1170 lda PM.CPR,X
|
|
1171 cmpa PD.CPR,Y ;Our process set it?
|
|
1172 bne QST.OK
|
|
1173
|
|
1174 * Release signal trap
|
|
1175
|
|
1176 clrb
|
|
1177 lbra Switch
|
|
1178
|
|
1179 * Not SS.Relea. Check for SS.Attr
|
|
1180
|
|
1181 NoS.Rel cmpa #SS.Attr
|
|
1182 bne NoS.Atr
|
|
1183
|
|
1184 * Change attributes if allowed
|
|
1185
|
|
1186 ldx <D.Proc
|
|
1187 lda P$ID,X ;Are we superuser?
|
|
1188 beq SAT.OK
|
|
1189 tst PD.Own,Y ;Is creator still attached?
|
|
1190 bne SAT.XX
|
|
1191
|
|
1192 sta PD.Own,Y ;Inherit pipe if owner abandoned it
|
|
1193
|
|
1194 SAT.XX cmpa PD.Own,Y
|
|
1195 lbne FNAXit ;If can't match PID, E$FNA error
|
|
1196
|
|
1197 * Change attributes.
|
|
1198 * Reg-U points at caller's registers
|
|
1199
|
|
1200 SAT.OK lda R$A,U
|
|
1201 ora #(READ.+WRITE.) ;We insist . . .
|
|
1202 sta PD.MOD,Y
|
|
1203 bra QST.OK
|
|
1204
|
|
1205 * Unknown SETSTT
|
|
1206
|
|
1207 NoS.Atr lbra Unknown
|
|
1208
|
|
1209 page
|
|
1210 *
|
|
1211 * Read CR-terminated line or element count from
|
|
1212 * pipe with no editing. Note that this call is
|
|
1213 * not well defined for element sizes other than
|
|
1214 * 1 byte.
|
|
1215 *
|
|
1216
|
|
1217 READLN ldb PD.ESiz,Y
|
|
1218 decb
|
|
1219 bne RddEOF ;EOF error if more than 1 byte per element
|
|
1220
|
|
1221 ldb #CR
|
|
1222 stb PD.REOR,Y
|
|
1223 bra READ001
|
|
1224
|
|
1225 *
|
|
1226 * Read element count from pipe with no editing.
|
|
1227 *
|
|
1228 * Note that if there are fewer elements in the pipe
|
|
1229 * than the user wants to read, and there are no writers
|
|
1230 * for the pipe, we return all elements followed by E$EOF.
|
|
1231 *
|
|
1232
|
|
1233 READ clr PD.REOR,Y
|
|
1234
|
|
1235 * Generic read. PD.REOR = terminator if non-null
|
|
1236
|
|
1237 READ001 leax PD.Read,Y ;Get PID of reader (us)
|
|
1238 lbsr GETFREE
|
|
1239 bcs RddRTS
|
|
1240
|
|
1241 ldd R$Y,U ;Desired element count
|
|
1242 beq RddXit
|
|
1243
|
|
1244 * Set up for outer loop -- push zero element count
|
|
1245 * and space for buffer pointers on stack.
|
|
1246
|
|
1247 clra
|
|
1248 clrb
|
|
1249 pshs D ;Initial count of elements read
|
|
1250 leas -4,S
|
|
1251 ldx R$X,U ;Initial buffer start address
|
|
1252 bra RddNext
|
|
1253
|
|
1254 * Enter here to block on read. If there are no writers,
|
|
1255 * return E$EOF.
|
|
1256
|
|
1257 CantRdd pshs X ;Save buffer pointer
|
|
1258
|
|
1259 leax PD.Read,Y
|
|
1260 lbsr SigSlp
|
|
1261 lbcs RddDone
|
|
1262
|
|
1263 * Inner loop to read bytes.
|
|
1264 * Here for initial attempt to read,
|
|
1265 * or to retry after blocking
|
|
1266
|
|
1267 READOK ldx <D.PROC ;Point to our task descriptor
|
|
1268 ldb P$TASK,X ++LII
|
|
1269 puls X ++LII Recover current buffer pointer
|
|
1270
|
|
1271 * Inner read loop. Read one element.
|
|
1272 * Note that we could use F$Move for elements larger
|
|
1273 * than 1 byte, because queue size is always an even
|
|
1274 * multiple of element size.
|
|
1275
|
|
1276 RddMore lbsr DOREAD ;Get byte to A, or CS
|
|
1277 bcs CantRdd
|
|
1278
|
|
1279 os9 F$STABX ;Put byte in caller's buffer
|
|
1280 leax 1,X
|
|
1281 tst PD.REOR,Y ;Is there an EOR character?
|
|
1282 beq NotRdLn
|
|
1283
|
|
1284 cmpa PD.REOR,Y ;Did we match it?
|
|
1285 beq RddEOL
|
|
1286
|
|
1287 NotRdLn cmpx 0,S ;Compare current addr. to end addr
|
|
1288 blo RddMore ; and loop until done
|
|
1289
|
|
1290 * Done with element. Check for next.
|
|
1291
|
|
1292 pshs X ;Save buffer pointer
|
|
1293
|
|
1294 bsr CntDn ;Update queue count, etc
|
|
1295 cmpd R$Y,U ;Got all elements?
|
|
1296 bhs RddTail
|
|
1297
|
|
1298 * Outer loop -- read one element at a time.
|
|
1299 *
|
|
1300 * X = next data pointer
|
|
1301 * Y = PD pointer
|
|
1302
|
|
1303 RddNext stx 0,S ;Set new start address
|
|
1304 ldb PD.ESiz,Y ;Size of one element
|
|
1305 clra
|
|
1306 addd 0,S ;Compute end address of current element bfr
|
|
1307 std 2,S
|
|
1308 bra READOK ;Go to element reading loop
|
|
1309
|
|
1310 * Read an EOL. Advance element count
|
|
1311
|
|
1312 RddEOL pshs X ;Save buffer pointer
|
|
1313 bsr CntDn
|
|
1314
|
|
1315 * Read everything, or aborting
|
|
1316
|
|
1317 RddDone ldd 4,S ;Get element count
|
|
1318
|
|
1319 * Tail end of read
|
|
1320
|
|
1321 RddTail std R$Y,U
|
|
1322 leas 6,S ;Clean stack
|
|
1323 bne RddSome ;Success if read more than 0 elements
|
|
1324
|
|
1325 * EOF error if no bytes read
|
|
1326
|
|
1327 RddEOF comb
|
|
1328 ldb #E$EOF
|
|
1329 bra RddXit
|
|
1330
|
|
1331 * Successful exit
|
|
1332
|
|
1333 RddSome clrb
|
|
1334
|
|
1335 RddXit leax PD.Read,Y
|
|
1336 lbra SWITCH
|
|
1337
|
|
1338 * Decrement queued count, inc read count
|
|
1339
|
|
1340 CntDn ldd #-1
|
|
1341 bra CUpDn
|
|
1342
|
|
1343 * Increment queued count, inc written count
|
|
1344
|
|
1345 CntUp ldd #1
|
|
1346
|
|
1347 CUpDn addd PD.BCnt,Y ;Modify count of elements queued
|
|
1348 std PD.BCnt,Y
|
|
1349
|
|
1350 * Bump I/O count
|
|
1351
|
|
1352 IOCnt ldd (2+4),S ;Bump count of elements read/written
|
|
1353 addd #1
|
|
1354 std (2+4),S
|
|
1355 RDDRTS rts
|
|
1356
|
|
1357 page
|
|
1358 *
|
|
1359 * Write CR-terminated line or element count to
|
|
1360 * pipe with no editing
|
|
1361 *
|
|
1362
|
|
1363 WRITELN ldb PD.ESiz,Y
|
|
1364 decb
|
|
1365 bne RddEOF ;EOF error if more than 1 byte per element
|
|
1366
|
|
1367 ldb #CR
|
|
1368 stb PD.WEOR,Y
|
|
1369 bra Wrt001
|
|
1370
|
|
1371 *
|
|
1372 * Write byte count to pipe with no editing.
|
|
1373 *
|
|
1374
|
|
1375 WRITE clr PD.WEOR,Y
|
|
1376
|
|
1377 * Generic entry point
|
|
1378
|
|
1379 Wrt001 leax PD.Writ,Y
|
|
1380 lbsr GETFREE ;Check I/O queue
|
|
1381 bcs WrtXit
|
|
1382
|
|
1383 ldd R$Y,U ;Element count
|
|
1384 beq WrtXit
|
|
1385
|
|
1386 * Set up for outer loop -- push zero element count
|
|
1387 * and space for buffer pointers on stack.
|
|
1388
|
|
1389 clra
|
|
1390 clrb
|
|
1391 pshs D ;Initial count of elements read
|
|
1392 leas -4,S
|
|
1393 ldx R$X,U ;Initial buffer start address
|
|
1394 bra WrtNext
|
|
1395
|
|
1396 * Enter here to block on write
|
|
1397
|
|
1398 CantWrt pshs X
|
|
1399
|
|
1400 leax PD.Writ,Y
|
|
1401 lbsr SigSlp
|
|
1402 bcs WrtErr
|
|
1403
|
|
1404 * Begin (or resume) write
|
|
1405
|
|
1406 WRITOK ldx <D.PROC ++LII
|
|
1407 ldb P$TASK,X ;Get our DAT image #
|
|
1408 puls X ++LII
|
|
1409
|
|
1410 * Main write loop
|
|
1411
|
|
1412 WrtMore os9 F$LDABX ;Get a byte from caller's buffer
|
|
1413 lbsr DOWRITE
|
|
1414 bcs CantWrt
|
|
1415
|
|
1416 leax 1,X
|
|
1417 tst PD.WEOR,Y ;EOL character defined?
|
|
1418 beq NotWrLn
|
|
1419
|
|
1420 cmpa PD.WEOR,Y
|
|
1421 beq WrtEOL
|
|
1422
|
|
1423 * See if at end of buffer
|
|
1424
|
|
1425 NotWrLn cmpx 0,S
|
|
1426 blo WrtMore
|
|
1427
|
|
1428 * Done with element. Check for next.
|
|
1429
|
|
1430 pshs X ;Save buffer pointer
|
|
1431
|
|
1432 bsr CntUp
|
|
1433 cmpd R$Y,U ;Put all elements?
|
|
1434 bhs WrtTail
|
|
1435
|
|
1436 * Outer loop -- write one element at a time.
|
|
1437
|
|
1438 WrtNext stx 0,S ;Set new start address
|
|
1439 ldb PD.ESiz,Y ;Size of one element
|
|
1440 clra
|
|
1441 addd 0,S ;Compute end address of current element bfr
|
|
1442 std 2,S
|
|
1443 bra WRITOK ;Go to element reading loop
|
|
1444
|
|
1445 * Wrote an EOL. Advance element count
|
|
1446
|
|
1447 WrtEOL pshs X ;Save buffer pointer
|
|
1448 bsr CntUp
|
|
1449
|
|
1450 * Wrote everything, or aborting
|
|
1451
|
|
1452 WrtDone ldd 4,S ;Get element count
|
|
1453
|
|
1454 * Tail end of write
|
|
1455
|
|
1456 WrtTail std R$Y,U
|
|
1457 leas 6,S ;Clean stack
|
|
1458
|
|
1459 * Successful exit
|
|
1460
|
|
1461 WrtSome clrb
|
|
1462
|
|
1463 WrtXit leax PD.Writ,Y
|
|
1464 bra SWITCH
|
|
1465
|
|
1466 * Error exit
|
|
1467
|
|
1468 WrtErr pshs B
|
|
1469 ldd (4+1),S
|
|
1470 std R$Y,U
|
|
1471 puls B
|
|
1472
|
|
1473 leas 6,S
|
|
1474 bra WrtXit
|
|
1475
|
|
1476 page
|
|
1477 *
|
|
1478 * I/O queue manipulation routines
|
|
1479 *
|
|
1480
|
|
1481 GETFREE lda PM.CPR,X ;Is any process using this resource?
|
|
1482 beq SETPMCPR ; (branch if not)
|
|
1483
|
|
1484 cmpa PD.CPR,Y ;Does caller control this resource?
|
|
1485 beq OURDEVIC ; (branch if so)
|
|
1486
|
|
1487 inc PM.CNT,X ;Bump # of active r/w images
|
|
1488 ldb PM.CNT,X
|
|
1489 cmpb PD.CNT,Y ;See if equal to # of open images
|
|
1490 bne SETQUEUE ; (if not, run everybody else to free it)
|
|
1491
|
|
1492 lbsr SENDSIG ;Yes -- wake up next process
|
|
1493
|
|
1494 * Process number in Reg-A
|
|
1495 * Put the process into the I/O queue and
|
|
1496 * sleep until a signal wakes us up
|
|
1497
|
|
1498 SETQUEUE os9 F$IOQU
|
|
1499 dec PM.CNT,X ;Caller is asleep, so 1 less active
|
|
1500 pshs X
|
|
1501 ldx <D.PROC
|
|
1502 ldb P$SIGNAL,X ;Get caller's signal
|
|
1503 puls X
|
|
1504 beq GETFREE ;Loop until there's a signal
|
|
1505
|
|
1506 coma ;Error if caller is waiting
|
|
1507 rts
|
|
1508
|
|
1509 * Nobody using the resource. Grab it.
|
|
1510
|
|
1511 SETPMCPR ldb PD.CPR,Y
|
|
1512 stb PM.CPR,X ;Make caller "owner"
|
|
1513
|
|
1514 * Exit -- caller owns the pipe
|
|
1515
|
|
1516 OURDEVIC clrb
|
|
1517 rts
|
|
1518
|
|
1519 *
|
|
1520 * Set a wakeup signal for the calling process
|
|
1521 *
|
|
1522
|
|
1523 SigSlp ldb PM.CNT,X ;Active image count
|
|
1524 incb
|
|
1525 cmpb PD.CNT,Y ;Everybody active?
|
|
1526 bne SgSlp01 ; (if not, try sending signals)
|
|
1527
|
|
1528 * Nobody on the other end to signal.
|
|
1529 * Error if anonymous, else hang out a bit.
|
|
1530
|
|
1531 tst PD.Name,Y ;If anonymous pipe & nobody left, error
|
|
1532 beq WRITEROR
|
|
1533
|
|
1534 * Named pipe and nobody to signal. Not an error if data in pipe.
|
|
1535
|
|
1536 tst PD.BCnt,Y ;Number of items in pipe
|
|
1537 beq WRITEROR
|
|
1538
|
|
1539 * Send signal to other end of pipe (may not be one, though)
|
|
1540
|
|
1541 SgSlp01 stb PM.CNT,X
|
|
1542 ldb #S$WAKE
|
|
1543 stb PM.SIG,X ;Force caller's signal to "wakeup"
|
|
1544 clr PD.CPR,Y
|
|
1545 pshs X
|
|
1546 tfr X,D ;Switch from reader to writer or vis-a-vis
|
|
1547 eorb #4
|
|
1548 tfr D,X
|
|
1549 lbsr SENDSIG ;Send signal to opposite end of pipe
|
|
1550 ldx #0
|
|
1551 os9 F$SLEEP ;Caller sleeps until signaled
|
|
1552 ldx <D.PROC
|
|
1553 ldb P$SIGNAL,X
|
|
1554 puls X
|
|
1555 dec PM.CNT,X ;Caller is asleep, so 1 less active
|
|
1556 tstb
|
|
1557 bne GOTSIGNL ;Error if opposite end set no signal
|
|
1558
|
|
1559 clrb
|
|
1560 rts
|
|
1561
|
|
1562 * WRITE ERROR hook
|
|
1563
|
|
1564 WRITEROR ldb #E$WRITE
|
|
1565
|
|
1566 * Generic error hook
|
|
1567
|
|
1568 GOTSIGNL coma
|
|
1569 rts
|
|
1570
|
|
1571 *
|
|
1572 * Release this end of the pipe, and
|
|
1573 * send a signal to the other end.
|
|
1574 *
|
|
1575 * Enter pointing to variables for
|
|
1576 * this end; exit pointing to variables
|
|
1577 * for opposite end.
|
|
1578 *
|
|
1579
|
|
1580 SWITCH pshs CC,B,U
|
|
1581 clr PM.CPR,X ;No process controlling current end
|
|
1582 tfr X,D
|
|
1583 eorb #4 ;Switch to other end (MAGIC)
|
|
1584 tfr D,X
|
|
1585 lbsr SENDSIG ;Awaken other end
|
|
1586 puls CC,B,U,PC
|
|
1587
|
|
1588 *
|
|
1589 * Write one byte to queue described in path
|
|
1590 * descriptor. Return CS if queue full.
|
|
1591 * Doesn't update count of ELEMENTS queued.
|
|
1592 *
|
|
1593
|
|
1594 DOWRITE pshs B,X
|
|
1595 ldx PD.NxtI,Y
|
|
1596 ldb PD.RFlg,Y
|
|
1597 beq SETREADY ;(say data available)
|
|
1598
|
|
1599 cmpx PD.NxtO,Y
|
|
1600 bne STORDATA ;(branch if queue not full)
|
|
1601
|
|
1602 * Error -- queue is full
|
|
1603
|
|
1604 comb
|
|
1605 puls B,X,PC
|
|
1606
|
|
1607 * Mark data available in queue
|
|
1608
|
|
1609 SETREADY ldb #1
|
|
1610 stb PD.RFlg,Y
|
|
1611
|
|
1612 * Put data in Reg-A into queue, and advance
|
|
1613 * pointer to next in w/ wrap
|
|
1614
|
|
1615 STORDATA sta ,X+
|
|
1616 cmpx PD.End,Y
|
|
1617 blo WTNOWRAP
|
|
1618
|
|
1619 ldx PD.BUF,Y
|
|
1620 leax PP.Data,X
|
|
1621
|
|
1622 WTNOWRAP stx PD.NxtI,Y
|
|
1623
|
|
1624 * Don't step Character Input Counter.
|
|
1625
|
|
1626 clr PD.Wrtn,Y
|
|
1627 puls B,X,PC
|
|
1628
|
|
1629 *
|
|
1630 * Read one byte from queue described in path
|
|
1631 * descriptor. Return CS if none available.
|
|
1632 * Doesn't update count of ELEMENTS queued.
|
|
1633 *
|
|
1634
|
|
1635 DOREAD lda PD.RFlg,Y ;Any data?
|
|
1636 bne DATAREDY
|
|
1637
|
|
1638 * No data -- return CS
|
|
1639
|
|
1640 comb
|
|
1641 rts
|
|
1642
|
|
1643 * Get data from queue
|
|
1644
|
|
1645 DATAREDY pshs X
|
|
1646 ldx PD.NxtO,Y ;Get next out pointer
|
|
1647 lda ,X+
|
|
1648 cmpx PD.End,Y
|
|
1649 blo RDNOWRAP
|
|
1650
|
|
1651 ldx PD.BUF,Y
|
|
1652 leax PP.Data,X
|
|
1653
|
|
1654 * Save updated next out pointer
|
|
1655
|
|
1656 RDNOWRAP stx PD.NxtO,Y
|
|
1657 cmpx PD.NxtI,Y
|
|
1658 bne NOTEMPTY
|
|
1659
|
|
1660 clr PD.RFlg,Y ;Mark queue empty
|
|
1661
|
|
1662 * Don't decrement Character Input Counter.
|
|
1663
|
|
1664 NOTEMPTY equ *
|
|
1665
|
|
1666 * Exit with character in Reg-A
|
|
1667
|
|
1668 andcc #^Carry ;Clear carry
|
|
1669 puls X,PC
|
|
1670
|
|
1671 page
|
|
1672 *
|
|
1673 * Utility placed here to not make assembly listing obsolete.
|
|
1674 *
|
|
1675
|
|
1676 MovSet os9 F$Move ;Do inter-process block move
|
|
1677
|
|
1678 * Force set MSB at end of name
|
|
1679
|
|
1680 tfr Y,D ;Byte count to D
|
|
1681 decb
|
|
1682 lda B,U ;Get last byte of name
|
|
1683 ora #%10000000
|
|
1684 sta B,U
|
|
1685
|
|
1686 rts
|
|
1687
|
|
1688 ifeq (PIPEDIR-YESDIR)
|
|
1689
|
|
1690 *
|
|
1691 * Find out how many pipes there are for the
|
|
1692 * current device, and set up device descriptor
|
|
1693 * so that pipe buffer will hold 32 bytes of
|
|
1694 * data for each.
|
|
1695 *
|
|
1696 * Reg-Y = PD pointer
|
|
1697 *
|
|
1698 * Exit with size set up in PD.
|
|
1699 * CC=EQ if no pipes.
|
|
1700 *
|
|
1701
|
|
1702 SizDirP pshs X,D
|
|
1703
|
|
1704 clrb ;Clear count of pipes
|
|
1705 ldx PD.Dev,Y
|
|
1706 ldx V$Stat,X ;Point at static storage
|
|
1707 ldx V.List,X ;Get head of linked list
|
|
1708 beq GotCnt
|
|
1709
|
|
1710 * There are some pipes. Count them.
|
|
1711
|
|
1712 PCount incb
|
|
1713 ldx PP.Next,X ;Track down linked list
|
|
1714 bne PCount
|
|
1715
|
|
1716 * Now Reg-B = pipe count. Need 32 bytes per pipe.
|
|
1717
|
|
1718 GotCnt incb ;Add one for us!
|
|
1719 lda #32
|
|
1720 mul
|
|
1721 std PD.QSiz,Y ;Set element count for this pipe
|
|
1722 lda #1
|
|
1723 sta PD.ESiz,Y ;Set element size to 1 byte
|
|
1724
|
|
1725 puls D,X,PC
|
|
1726
|
|
1727 *
|
|
1728 * Fill pipe buffer with directory data.
|
|
1729 *
|
|
1730 * The data is organized like an RBF directory:
|
|
1731 *
|
|
1732 * Offset Data
|
|
1733 * -------- --------------------------------
|
|
1734 * $00-$1C Pipe name
|
|
1735 * $1D DAT task number of pipe buffer
|
|
1736 * $1E-$1F Address of pipe buffer in task
|
|
1737 *
|
|
1738 *
|
|
1739 FilDirP pshs U,X,D
|
|
1740
|
|
1741 ldx PD.Dev,Y
|
|
1742 ldx V$Stat,X ;Point at static storage
|
|
1743 ldx V.List,X ;Get head of linked list
|
|
1744 beq GotFil
|
|
1745
|
|
1746 * Write data for pipe buffer @X to pipe with PD @Y
|
|
1747
|
|
1748 FD000 ldu PP.PD,X ;Point at PD for pipe to be dumped
|
|
1749 leau PD.Name,u
|
|
1750 ldb #NameMax
|
|
1751
|
|
1752 FD001 lda ,u+ ;Write pipe name
|
|
1753 bsr QWrite
|
|
1754 decb
|
|
1755 bne FD001
|
|
1756
|
|
1757 ldu <D.SysPrc ;Get system DAT image number
|
|
1758 lda P$Task,u
|
|
1759 bsr QWrite
|
|
1760
|
|
1761 pshs X ;Get pipe buffer pointer
|
|
1762 lda ,S+
|
|
1763 bsr QWrite
|
|
1764 lda ,S+
|
|
1765 bsr QWrite
|
|
1766
|
|
1767 * Advance to next pipe buffer
|
|
1768
|
|
1769 ldx PP.Next,X
|
|
1770 bne FD000
|
|
1771
|
|
1772 * All done. Restore regs & exit
|
|
1773
|
|
1774 GotFil puls D,X,U,PC
|
|
1775
|
|
1776 * Byte saver
|
|
1777
|
|
1778 QWrite lbra DoWrite
|
|
1779
|
|
1780 *
|
|
1781 * Immortal entry point
|
|
1782 *
|
|
1783 Immort coma
|
|
1784 sbcb #38
|
|
1785 lsrb
|
|
1786 fcb $42 ;SBCB op-code
|
|
1787
|
|
1788 * Fall through to SS.FD processing
|
|
1789
|
|
1790 *
|
|
1791 * Routine to process SS.FD call on an open pipe.
|
|
1792 * Creates a pseudo-FD in the user's buffer (@R$X).
|
|
1793 * Desired byte count in R$Y.
|
|
1794 *
|
|
1795 * The pseudo-file descriptor sector includes the following:
|
|
1796 *
|
|
1797 * Offset Description
|
|
1798 * -------- --------------------------------------
|
|
1799 * $00 Attributes
|
|
1800 * $01-$02 Owner's *PROCESS* ID
|
|
1801 * $03-$07 Zeros (date of last access)
|
|
1802 * $08 Use count
|
|
1803 * $09-$0C Number of items queued
|
|
1804 * $0D-$0F Zeros (creation date)
|
|
1805 * $10-$FF Zeros (segment list -- at least 5 zeros needed)
|
|
1806 *
|
|
1807
|
|
1808 DoSSFD pshs D,X,Y,U
|
|
1809
|
|
1810 ldb #(16+5) ;Clear data on stack
|
|
1811 SSFD01 clr ,-S
|
|
1812 decb
|
|
1813 bne SSFD01
|
|
1814
|
|
1815 * Set attributes
|
|
1816 lda PD.Mod,Y
|
|
1817 sta FD.ATT,S
|
|
1818
|
|
1819 * Set owner's process ID
|
|
1820 lda PD.Own,Y
|
|
1821 sta (FD.OWN+1),S
|
|
1822
|
|
1823 * Set use count
|
|
1824 lda PD.CNT,Y
|
|
1825 sta FD.LNK,S
|
|
1826
|
|
1827 * Set queue count
|
|
1828
|
|
1829 ldd PD.BCNT,Y
|
|
1830 std (FD.SIZ+2),S
|
|
1831
|
|
1832 * Now copy the data into the caller's buffer
|
|
1833
|
|
1834 ldx <D.SysPrc ;Pointer to our PD
|
|
1835 lda P$Task,X ; get system's DAT image # (source)
|
|
1836 ldx <D.Proc ;Pointer to caller's PD
|
|
1837 ldb P$Task,X ; get caller's DAT image # (dest)
|
|
1838 ldy R$Y,U ;Byte count
|
|
1839 leax 0,S ;Source address
|
|
1840 ldu R$X,U ;Destination address
|
|
1841 os9 F$Move ;Do the move
|
|
1842
|
|
1843 * All done.
|
|
1844
|
|
1845 leas (16+5),S
|
|
1846 puls U,X,Y,D,PC
|
|
1847
|
|
1848 endc
|
|
1849
|
|
1850 emod
|
|
1851
|
|
1852 MODSIZE equ *
|
|
1853
|
|
1854 end
|
|
1855
|