Repository navigation
Expand file tree
/
Copy pathpipedev.asm
More file actions
444 lines (411 loc) · 9.29 KB
/
Copy pathpipedev.asm
File metadata and controls
444 lines (411 loc) · 9.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
;
; BASIC-DOS Pipe Device Driver
;
; @author Jeff Parsons <Jeff@pcjs.org>
; @copyright (c) 2020-2026 Jeff Parsons
; @license MIT <https://basicdos.com/LICENSE.txt>
;
; This file is part of PCjs, a computer emulation software project at pcjs.org
;
DOSFUNC equ 1
include macros.inc
include bios.inc
include dev.inc
include devapi.inc
include dosapi.inc
DEV group CODE,DATA
CODE segment para public 'CODE'
public PIPE
PIPE DDH <offset DEV:ddpipe_end+16,,DDATTR_CHAR,offset ddpipe_init,-1,2020202445504950h>
DEFLBL CMDTBL,word
dw ddpipe_none, ddpipe_none, ddpipe_none, ddpipe_none ; 0-3
dw ddpipe_read, ddpipe_none, ddpipe_none, ddpipe_none ; 4-7
dw ddpipe_write, ddpipe_none, ddpipe_none, ddpipe_none ; 8-11
dw ddpipe_none, ddpipe_open, ddpipe_close ; 12-14
DEFABS CMDTBL_SIZE,<($ - CMDTBL) SHR 1>
CONTEXT struc
CT_STATUS db ? ; 00h: context status bits (CTSTAT_*)
CT_SIG db ? ; 01h: (holds SIG_CT in DEBUG builds)
CT_HEAD dw ? ; 02h: offset of next byte to read
CT_TAIL dw ? ; 04h: offset of next byte to write
CT_DATA db 128 dup(?); 06h: data buffer
CONTEXT ends
SIG_CT equ 'P'
CTSTAT_EWAIT equ 01h ; set on empty-wait condition
CTSTAT_FWAIT equ 02h ; set on full-wait condition
CTSTAT_TRUNC equ 04h ; set when pipe is being "truncated"
CTSTAT_DONE equ 08h ; set when the reader is done (see ddpipe_read)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Driver request
;
; Inputs:
; ES:BX -> DDP
;
; Outputs:
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_req,far
mov di,bx ; ES:DI -> DDP
mov bl,es:[di].DDP_CMD
cmp bl,CMDTBL_SIZE
jb ddq1
mov bl,0
ddq1: mov bh,0
add bx,bx
mov ds,es:[di].DDP_CONTEXT ; DS = device context (if any)
call CMDTBL[bx]
ret
ENDPROC ddpipe_req
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ddpipe_read
;
; A zero-length read means that the reader is done (eg, the session reading
; the pipe has ended), so from then on, writes fail instead of waiting for
; room that will never be made (see push_data).
;
; Inputs:
; ES:DI -> DDPRW
; DS = device context
;
; Outputs:
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_read
ASSERT STRUCT,ds:[0],CT
mov cx,es:[di].DDPRW_LENGTH
jcxz ddr8
call pull_data
jnc ddr9
mov es:[di].DDP_STATUS,DDSTAT_ERROR + DDERR_RDFAULT
ret
ddr8: or ds:[CT_STATUS],CTSTAT_DONE
test ds:[CT_STATUS],CTSTAT_FWAIT
jz ddr9 ; no writer is waiting for room
and ds:[CT_STATUS],NOT CTSTAT_FWAIT
call endwait_data
ddr9: mov es:[di].DDP_STATUS,DDSTAT_DONE
ret
ENDPROC ddpipe_read
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ddpipe_write
;
; Inputs:
; ES:DI -> DDPRW
; DS = device context
;
; Outputs:
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_write
ASSERT STRUCT,ds:[0],CT
mov cx,es:[di].DDPRW_LENGTH
jcxz ddw8
call push_data
jnc ddw9
mov es:[di].DDP_STATUS,DDSTAT_ERROR + DDERR_WRFAULT
ret
ddw8: or ds:[CT_STATUS],CTSTAT_TRUNC
test ds:[CT_STATUS],CTSTAT_EWAIT
jz ddw9
DBGBRK
and ds:[CT_STATUS],NOT CTSTAT_EWAIT
call endwait_data
ddw9: mov es:[di].DDP_STATUS,DDSTAT_DONE
ret
ENDPROC ddpipe_write
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ddpipe_open
;
; Inputs:
; ES:DI -> DDP
; [DDP].DDP_PTR -> context descriptor (eg, "PIPE$")
;
; Outputs:
; None
;
; Modifies:
; AX, BX, DS
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_open
mov bx,(size CONTEXT + 15) SHR 4
mov ax,DOS_MEM_ALLOC SHL 8
int INT_DOSFUNC
jc ddo8
mov ds,ax
ASSUME DS:NOTHING
DBGINIT STRUCT,ds:[0],CT
sub ax,ax
mov ds:[CT_STATUS],al
mov ds:[CT_HEAD],ax
mov ds:[CT_TAIL],ax
jmp short ddo9
;
; At the moment, the only possible error is a failure to allocate memory.
;
ddo8: mov es:[di].DDP_STATUS,DDSTAT_ERROR + DDERR_GENFAIL
ret
ddo9: mov es:[di].DDP_CONTEXT,ds
mov es:[di].DDP_STATUS,DDSTAT_DONE
ret
ENDPROC ddpipe_open
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ddpipe_close
;
; Inputs:
; ES:DI -> DDP
; DS = device context
;
; Outputs:
; None
;
; Modifies:
; AX, CX
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_close
mov cx,ds
jcxz ddc9 ; no context
push es
mov es,cx
mov ah,DOS_MEM_FREE
int INT_DOSFUNC
pop es
ddc9: mov es:[di].DDP_STATUS,DDSTAT_DONE
ret
ENDPROC ddpipe_close
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ddpipe_none (handler for unimplemented functions)
;
; Inputs:
; ES:DI -> DDP
;
; Outputs:
; None
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC ddpipe_none
mov es:[di].DDP_STATUS,DDSTAT_ERROR + DDERR_UNKCMD
stc
ret
ENDPROC ddpipe_none
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; pull_data
;
; Like a pipe read on UNIX, a read waits only until some data is available,
; and then returns as much as the request can hold; otherwise, a reader such
; as MORE, which requests a buffer at a time, would display nothing until the
; writer had filled that entire buffer.
;
; Inputs:
; ES:DI -> DDPRW
; CX = original request length
; DS = device context
;
; Outputs:
; Returns after some data has been read, or the pipe has been truncated
;
; Modifies:
; AX, BX, DX
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC pull_data
cli
pl0: mov bx,ds:[CT_HEAD]
cmp bx,ds:[CT_TAIL]
jne pl1
;
; There's no data, so if we've already read some, return it; otherwise,
; issue WAIT and try again when it returns.
;
cmp cx,es:[di].DDPRW_LENGTH ; have we read anything yet?
ja pl8 ; yes (and carry is clear)
test ds:[CT_STATUS],CTSTAT_TRUNC
jnz pl9
ASSERT Z,<test ds:[CT_STATUS],CTSTAT_EWAIT OR CTSTAT_FWAIT>
or ds:[CT_STATUS],CTSTAT_EWAIT
call wait_data
;
; If the WAIT was ABORT'ed (eg, CTRL-ALT-DEL), the writer never cleared our
; wait flag, so we clear it ourselves; and if the WAIT failed, bail.
;
pushf
and ds:[CT_STATUS],NOT CTSTAT_EWAIT
popf
jnc pl0
pl8: sti
ret
pl1: mov al,ds:[CT_DATA][bx] ; AL = data byte
inc bx
cmp bx,size CT_DATA
jne pl2
sub bx,bx
pl2: mov ds:[CT_HEAD],bx
;
; We just made some room. If CTSTAT_FWAIT is set, clear it and issue ENDWAIT.
;
test ds:[CT_STATUS],CTSTAT_FWAIT
jz pl3
and ds:[CT_STATUS],NOT CTSTAT_FWAIT
call endwait_data
pl3: sti
push ds
lds bx,es:[di].DDPRW_ADDR ; DS:BX -> next read/write address
mov [bx],al
inc bx
mov es:[di].DDPRW_ADDR.OFF,bx
pop ds
dec es:[di].DDPRW_LENGTH ; have we satisfied the request yet?
jnz pull_data ; no
clc
pl9: ret
ENDPROC pull_data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; push_data
;
; Inputs:
; ES:DI -> DDPRW
; DS = device context
;
; Outputs:
; Carry clear if request satisfied, set if not (eg, the reader is done)
;
; Modifies:
; AX, BX, DX
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
DEFPROC push_data
cli
ps0: test ds:[CT_STATUS],CTSTAT_DONE
stc
jnz ps9 ; the reader is done
mov bx,ds:[CT_TAIL]
mov dx,bx
inc dx
cmp dx,size CT_DATA
jne ps1
sub dx,dx
ps1: cmp dx,ds:[CT_HEAD]
jne ps2
;
; The buffer is full, so issue WAIT and try again when it returns.
;
ASSERT Z,<test ds:[CT_STATUS],CTSTAT_EWAIT OR CTSTAT_FWAIT>
or ds:[CT_STATUS],CTSTAT_FWAIT
call wait_data
;
; If the WAIT was ABORT'ed (eg, CTRL-ALT-DEL), the reader never cleared our
; wait flag, so we clear it ourselves; and if the WAIT failed, bail.
;
pushf
and ds:[CT_STATUS],NOT CTSTAT_FWAIT
popf
jnc ps0
ps9: sti
ret
ps2: mov ds:[CT_TAIL],dx
push ds
push bx
lds bx,es:[di].DDPRW_ADDR ; DS:BX -> next read/write address
mov al,[bx]
inc bx
mov es:[di].DDPRW_ADDR.OFF,bx
pop bx
pop ds
mov ds:[CT_DATA][bx],al ; AL = data byte
;
; We just added some data. If CTSTAT_EWAIT is set, clear it and issue ENDWAIT.
;
test ds:[CT_STATUS],CTSTAT_EWAIT
jz ps3
and ds:[CT_STATUS],NOT CTSTAT_EWAIT
call endwait_data
ps3: sti
dec es:[di].DDPRW_LENGTH ; have we satisfied the request yet?
jnz push_data ; no
clc
ret
ENDPROC push_data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; wait_data
;
; Inputs:
; DS = device context
;
; Outputs:
; Carry set if the wait failed (eg, ABORT or CTRLC)
;
; Modifies:
; AX, DX
;
DEFPROC wait_data
mov ah,DOS_UTL_WAIT
DEFLBL wait_call,near
push di
mov dx,ds
sub di,di
DOSUTIL ah
pop di
ret
ENDPROC wait_data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; endwait_data
;
; Inputs:
; DS = device context
;
; Outputs:
; None
;
; Modifies:
; AX, DX
;
DEFPROC endwait_data
mov ah,DOS_UTL_ENDWAIT
jmp wait_call
ENDPROC endwait_data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Driver initialization
;
; Inputs:
; ES:BX -> DDPI
;
; Outputs:
; DDPI's DDPI_END updated
;
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
;
; With 64K of memory or less, we'd take memory that BASIC programs need, for
; sessions that there's no room for (see def_sessions), so we keep nothing, as
; if CONFIG.SYS had SKIP=PIPE$.
;
DEFPROC ddpipe_init,far
mov ax,BIOS
mov ds,ax
ASSUME DS:BIOS
cmp [MEMORY_SIZE],64 ; more than 64K of memory?
ja dpi1 ; yes
mov es:[bx].DDPI_END.OFF,0 ; no, so keep nothing
ret
ASSUME DS:NOTHING
dpi1: mov es:[bx].DDPI_END.OFF,offset ddpipe_init
mov ds,cs:[0].DDH_DATA
mov ds:[0].DDH_REQUEST,offset DEV:ddpipe_req
ret
ENDPROC ddpipe_init
CODE ends
DATA segment para public 'DATA'
ddpipe_end db 16 dup(0)
DATA ends
end