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