4 * Copyright 1999 Ove Kåven
9 #include "wine/winbase16.h"
18 DOS_DEVICE_HEADER hdr
;
26 BYTE size
; /* length of header + data */
27 BYTE unit
; /* unit (block devices only) */
35 BYTE media
; /* media descriptor from BPB */
37 WORD count
; /* byte/sector count */
38 WORD sector
; /* starting sector (block devices) */
39 DWORD volume
; /* volume ID (block devices) */
49 #define REQ_SCRATCH sizeof(REQ_IO)
51 #define SYSTEM_STRATEGY_NUL 0x0100
52 #define SYSTEM_STRATEGY_CON 0x0101
56 #define NONEXT ((DWORD)-1)
58 #define ATTR_STDIN 0x0001
59 #define ATTR_STDOUT 0x0002
60 #define ATTR_NUL 0x0004
61 #define ATTR_CLOCK 0x0008
62 #define ATTR_FASTCON 0x0010
63 #define ATTR_RAW 0x0020
64 #define ATTR_NOTEOF 0x0040
65 #define ATTR_DEVICE 0x0080
66 #define ATTR_REMOVABLE 0x0800
67 #define ATTR_NONIBM 0x2000 /* block devices */
68 #define ATTR_UNTILBUSY 0x2000 /* char devices */
69 #define ATTR_IOCTL 0x4000
70 #define ATTR_CHAR 0x8000
73 #define CMD_MEDIACHECK 1 /* block devices */
74 #define CMD_BUILDBPB 2 /* block devices */
76 #define CMD_INPUT 4 /* read data */
77 #define CMD_SAFEINPUT 5 /* "non-destructive input no wait", char devices */
78 #define CMD_INSTATUS 6 /* char devices */
79 #define CMD_INFLUSH 7 /* char devices */
80 #define CMD_OUTPUT 8 /* write data */
81 #define CMD_SAFEOUTPUT 9 /* write data with verify */
82 #define CMD_OUTSTATUS 10 /* char devices */
83 #define CMD_OUTFLUSH 11 /* char devices */
84 #define CMD_OUTIOCTL 12
85 #define CMD_DEVOPEN 13
86 #define CMD_DEVCLOSE 14
87 #define CMD_REMOVABLE 15 /* block devices */
88 #define CMD_UNTILBUSY 16 /* output until busy */
90 #define STAT_MASK 0x00FF
91 #define STAT_DONE 0x0100
92 #define STAT_BUSY 0x0200
93 #define STAT_ERROR 0x8000
97 #define DEV0_OFS (sizeof(DOS_LISTOFLISTS) - sizeof(DOS_DEVICE_HEADER))
98 #define DEV1_OFS (DEV0_OFS + sizeof(WINEDEV))
99 #define ALLDEV_OFS (DEV1_OFS + sizeof(devs))
100 #define ALL_OFS (ALLDEV_OFS + REQ_SCRATCH)
103 static void WINAPI
nul_strategy(CONTEXT86
*ctx
);
104 static void WINAPI
nul_interrupt(CONTEXT86
*ctx
);
105 static void WINAPI
con_strategy(CONTEXT86
*ctx
);
106 static void WINAPI
con_interrupt(CONTEXT86
*ctx
);
108 /* the device headers */
109 #define STRATEGY_OFS sizeof(DOS_DEVICE_HEADER)
110 #define INTERRUPT_OFS STRATEGY_OFS+5
112 static DOS_DEVICE_HEADER dev_nul_hdr
={
114 ATTR_CHAR
|ATTR_NUL
|ATTR_DEVICE
,
115 STRATEGY_OFS
,INTERRUPT_OFS
,
119 static WINEDEV devs
[]={
122 ATTR_CHAR
|ATTR_STDIN
|ATTR_STDOUT
|ATTR_FASTCON
|ATTR_NOTEOF
|ATTR_DEVICE
,
123 STRATEGY_OFS
,INTERRUPT_OFS
,
128 #define nr_devs (sizeof(devs)/sizeof(WINEDEV))
130 /* the device implementations */
131 static void do_lret(CONTEXT86
*ctx
)
133 WORD
*stack
= CTX_SEG_OFF_TO_LIN(ctx
, SS_reg(ctx
), ESP_reg(ctx
));
135 IP_reg(ctx
) = *(stack
++);
136 CS_reg(ctx
) = *(stack
++);
137 SP_reg(ctx
) += 2*sizeof(WORD
);
140 static void do_strategy(CONTEXT86
*ctx
, int id
, int extra
)
142 REQUEST_HEADER
*hdr
= CTX_SEG_OFF_TO_LIN(ctx
, ES_reg(ctx
), EBX_reg(ctx
));
143 void **hdr_ptr
= DOSVM_GetSystemData(id
);
146 hdr_ptr
= calloc(1,sizeof(void *)+extra
);
147 DOSVM_SetSystemData(id
, hdr_ptr
);
154 static REQUEST_HEADER
* get_hdr(int id
, void**extra
)
156 void **hdr_ptr
= DOSVM_GetSystemData(id
);
158 *extra
= hdr_ptr
? (void*)(hdr_ptr
+1) : (void *)NULL
;
159 return hdr_ptr
? *hdr_ptr
: (void *)NULL
;
162 static void WINAPI
nul_strategy(CONTEXT86
*ctx
)
164 do_strategy(ctx
, SYSTEM_STRATEGY_NUL
, 0);
167 static void WINAPI
nul_interrupt(CONTEXT86
*ctx
)
169 REQUEST_HEADER
*hdr
= get_hdr(SYSTEM_STRATEGY_NUL
, NULL
);
170 /* eat everything and recycle nothing */
171 switch (hdr
->command
) {
173 ((REQ_IO
*)hdr
)->count
= 0;
174 hdr
->status
= STAT_DONE
;
177 hdr
->status
= STAT_DONE
|STAT_BUSY
;
180 hdr
->status
= STAT_DONE
;
185 #define CON_BUFFER 128
187 static void WINAPI
con_strategy(CONTEXT86
*ctx
)
189 do_strategy(ctx
, SYSTEM_STRATEGY_CON
, sizeof(int));
192 static void WINAPI
con_interrupt(CONTEXT86
*ctx
)
195 REQUEST_HEADER
*hdr
= get_hdr(SYSTEM_STRATEGY_CON
,(void **)&scan
);
196 BIOSDATA
*bios
= DOSMEM_BiosData();
197 WORD CurOfs
= bios
->NextKbdCharPtr
;
198 DOS_LISTOFLISTS
*lol
= DOSMEM_LOL();
199 BYTE
*linebuffer
= ((BYTE
*)lol
) + ALL_OFS
;
200 BYTE
*curbuffer
= (lol
->offs_unread_CON
) ?
201 (((BYTE
*)lol
) + lol
->offs_unread_CON
) : (BYTE
*)NULL
;
202 DOS_DEVICE_HEADER
*con
= (DOS_DEVICE_HEADER
*)(((BYTE
*)lol
) + DEV1_OFS
);
203 LPDOSTASK lpDosTask
= MZ_Current();
205 switch (hdr
->command
) {
208 REQ_IO
*io
= (REQ_IO
*)hdr
;
209 WORD count
= io
->count
, len
= 0;
210 BYTE
*buffer
= CTX_SEG_OFF_TO_LIN(ctx
,
211 SELECTOROF(io
->buffer
),
212 (DWORD
)OFFSETOF(io
->buffer
));
214 hdr
->status
= STAT_BUSY
;
215 /* first, check whether we already have data in line buffer */
217 /* yep, copy as much as we can */
219 while ((len
<count
) && (data
!= '\r')) {
221 buffer
[len
++] = data
;
224 /* line buffer emptied */
225 lol
->offs_unread_CON
= 0;
227 /* if we're not in raw mode, call it a day*/
228 if (!(con
->attr
& ATTR_RAW
)) {
229 hdr
->status
= STAT_DONE
;
234 /* still some data left */
235 lol
->offs_unread_CON
= curbuffer
- (BYTE
*)lol
;
236 /* but buffer was filled, we're done */
237 hdr
->status
= STAT_DONE
;
243 /* if we're in raw mode, we just need to fill the buffer */
244 if (con
->attr
& ATTR_RAW
) {
248 /* do we have a waiting scancode? */
250 /* yes, store scancode in buffer */
251 buffer
[len
++] = *scan
;
253 if (len
==count
) break;
256 /* check for new keyboard input */
257 while (CurOfs
== bios
->FirstKbdCharPtr
) {
258 /* no input available yet, so wait... */
261 /* read from keyboard queue (call int16?) */
262 data
= ((WORD
*)bios
)[CurOfs
];
264 if (CurOfs
>= bios
->KbdBufferEnd
) CurOfs
= bios
->KbdBufferStart
;
265 bios
->NextKbdCharPtr
= CurOfs
;
266 /* if it's an extended key, save scancode */
267 if (LOBYTE(data
) == 0) *scan
= HIBYTE(data
);
268 /* store ASCII char in buffer */
269 buffer
[len
++] = LOBYTE(data
);
272 /* we're not in raw mode, so we need to do line input... */
275 /* check for new keyboard input */
276 while (CurOfs
== bios
->FirstKbdCharPtr
) {
277 /* no input available yet, so wait... */
280 /* read from keyboard queue (call int16?) */
281 data
= ((WORD
*)bios
)[CurOfs
];
283 if (CurOfs
>= bios
->KbdBufferEnd
) CurOfs
= bios
->KbdBufferStart
;
284 bios
->NextKbdCharPtr
= CurOfs
;
286 if (LOBYTE(data
) == '\r') {
287 /* it's the return key, we're done */
288 linebuffer
[len
++] = LOBYTE(data
);
291 else if (LOBYTE(data
) >= ' ') {
293 if ((len
+1)<CON_BUFFER
) {
294 linebuffer
[len
] = LOBYTE(data
);
295 WriteFile(lpDosTask
->hConOutput
, &linebuffer
[len
++], 1, NULL
, NULL
);
297 /* else beep, but I don't like noise */
299 else switch (LOBYTE(data
)) {
303 WriteFile(lpDosTask
->hConOutput
, "\b \b", 3, NULL
, NULL
);
309 /* save rest of line for later */
310 lol
->offs_unread_CON
= linebuffer
- (BYTE
*)lol
+ count
;
313 memcpy(buffer
, linebuffer
, len
);
315 hdr
->status
= STAT_DONE
;
321 /* some line input waiting */
322 hdr
->status
= STAT_DONE
;
323 ((REQ_SAFEINPUT
*)hdr
)->data
= *curbuffer
;
325 else if (con
->attr
& ATTR_RAW
) {
326 if (CurOfs
== bios
->FirstKbdCharPtr
) {
328 hdr
->status
= STAT_DONE
|STAT_BUSY
;
330 /* some keyboard input waiting */
331 hdr
->status
= STAT_DONE
;
332 ((REQ_SAFEINPUT
*)hdr
)->data
= ((BYTE
*)bios
)[CurOfs
];
336 hdr
->status
= STAT_DONE
|STAT_BUSY
;
342 hdr
->status
= STAT_DONE
;
344 else if (con
->attr
& ATTR_RAW
) {
345 if (CurOfs
== bios
->FirstKbdCharPtr
) {
347 hdr
->status
= STAT_DONE
|STAT_BUSY
;
349 /* some keyboard input waiting */
350 hdr
->status
= STAT_DONE
;
354 hdr
->status
= STAT_DONE
|STAT_BUSY
;
359 /* flush line and keyboard queue */
360 lol
->offs_unread_CON
= 0;
361 bios
->NextKbdCharPtr
= bios
->FirstKbdCharPtr
;
366 REQ_IO
*io
= (REQ_IO
*)hdr
;
367 BYTE
*buffer
= CTX_SEG_OFF_TO_LIN(ctx
,
368 SELECTOROF(io
->buffer
),
369 (DWORD
)OFFSETOF(io
->buffer
));
371 WriteFile(lpDosTask
->hConOutput
, buffer
, io
->count
, &result
, NULL
);
373 hdr
->status
= STAT_DONE
;
377 hdr
->status
= STAT_DONE
;
382 static void InitListOfLists(DOS_LISTOFLISTS
*DOS_LOL
)
387 0133:0020 6A 13-33 01 CC 00 33 01 59 00 j.3...3.Y.
388 0133:0030 70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05 p...r...m.3.....
389 0133:0040 00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D .........!......
390 0133:0050 CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00 ..NUL ......
391 0133:0060 00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF .K...........p..
392 0133:0070 FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00 ................
393 0133:0080 00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02 ..............p.
394 0133:0090 D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD .D...D...D...D..
395 0133:00A0 D0 44 C8 FD D0 44 .D...D
397 DOS_LOL
->CX_Int21_5e01
= 0x0;
398 DOS_LOL
->LRU_count_FCB_cache
= 0x0;
399 DOS_LOL
->LRU_count_FCB_open
= 0x0;
400 DOS_LOL
->OEM_func_handler
= -1; /* not available */
401 DOS_LOL
->INT21_offset
= 0x0;
402 DOS_LOL
->sharing_retry_count
= 3;
403 DOS_LOL
->sharing_retry_delay
= 1;
404 DOS_LOL
->ptr_disk_buf
= 0x0;
405 DOS_LOL
->offs_unread_CON
= 0x0;
406 DOS_LOL
->seg_first_MCB
= 0x0;
407 DOS_LOL
->ptr_first_DPB
= 0x0;
408 DOS_LOL
->ptr_first_SysFileTable
= 0x0;
409 DOS_LOL
->ptr_clock_dev_hdr
= 0x0;
410 DOS_LOL
->ptr_CON_dev_hdr
= 0x0;
411 DOS_LOL
->max_byte_per_sec
= 512;
412 DOS_LOL
->ptr_disk_buf_info
= 0x0;
413 DOS_LOL
->ptr_array_CDS
= 0x0;
414 DOS_LOL
->ptr_sys_FCB
= 0x0;
415 DOS_LOL
->nr_protect_FCB
= 0x0;
416 DOS_LOL
->nr_block_dev
= 0x0;
417 DOS_LOL
->nr_avail_drive_letters
= 26; /* A - Z */
418 DOS_LOL
->nr_drives_JOINed
= 0x0;
419 DOS_LOL
->ptr_spec_prg_names
= 0x0;
420 DOS_LOL
->ptr_SETVER_prg_list
= 0x0; /* no SETVER list */
421 DOS_LOL
->DOS_HIGH_A20_func_offs
= 0x0;
422 DOS_LOL
->PSP_last_exec
= 0x0;
423 DOS_LOL
->BUFFERS_val
= 99; /* maximum: 99 */
424 DOS_LOL
->BUFFERS_nr_lookahead
= 8; /* maximum: 8 */
425 DOS_LOL
->boot_drive
= 3; /* C: */
426 DOS_LOL
->flag_DWORD_moves
= 0x01; /* i386+ */
427 DOS_LOL
->size_extended_mem
= 0xf000; /* very high value */
430 void DOSDEV_InstallDOSDevices(void)
433 DOS_DEVICE_HEADER
*pdev
;
437 DOS_LISTOFLISTS
*DOS_LOL
;
439 /* allocate DOS data segment or something */
440 DOS_LOLSeg
= GlobalDOSAlloc16(ALL_OFS
+CON_BUFFER
);
441 seg
= HIWORD(DOS_LOLSeg
);
442 DOS_LOL
= PTR_SEG_OFF_TO_LIN(LOWORD(DOS_LOLSeg
), 0);
444 /* initialize the magnificent List Of Lists */
445 InitListOfLists(DOS_LOL
);
447 /* copy first device (NUL) */
448 pdev
= &(DOS_LOL
->NUL_dev
);
449 memcpy(pdev
,&dev_nul_hdr
,sizeof(DOS_DEVICE_HEADER
));
450 pdev
->strategy
+= ofs
;
451 pdev
->interrupt
+= ofs
;
452 /* set up dev so we can copy over the rest */
453 dev
= (WINEDEV
*)(((char*)DOS_LOL
)+ofs
);
455 dev
[0].strategy
= (RMCBPROC
)DPMI_AllocInternalRMCB(nul_strategy
);
457 dev
[0].interrupt
= (RMCBPROC
)DPMI_AllocInternalRMCB(nul_interrupt
);
460 ofs
+= sizeof(WINEDEV
);
462 /* first of remaining devices is CON */
463 DOS_LOL
->ptr_CON_dev_hdr
= PTR_SEG_OFF_TO_SEGPTR(seg
, ofs
);
465 /* copy remaining devices */
466 memcpy(dev
,&devs
,sizeof(devs
));
467 for (n
=0; n
<nr_devs
; n
++) {
468 pdev
->next_dev
= PTR_SEG_OFF_TO_SEGPTR(seg
, ofs
);
469 dev
[n
].hdr
.strategy
+= ofs
;
470 dev
[n
].hdr
.interrupt
+= ofs
;
471 dev
[n
].strategy
= (RMCBPROC
)DPMI_AllocInternalRMCB(dev
[n
].strategy
);
472 dev
[n
].interrupt
= (RMCBPROC
)DPMI_AllocInternalRMCB(dev
[n
].interrupt
);
473 ofs
+= sizeof(WINEDEV
);
474 pdev
= &(dev
[n
].hdr
);
478 DWORD
DOSDEV_Console(void)
480 return DOSMEM_LOL()->ptr_CON_dev_hdr
;
483 DWORD
DOSDEV_FindCharDevice(char*name
)
485 SEGPTR cur_ptr
= PTR_SEG_OFF_TO_SEGPTR(HIWORD(DOS_LOLSeg
),
486 FIELD_OFFSET(DOS_LISTOFLISTS
,NUL_dev
));
487 DOS_DEVICE_HEADER
*cur
= DOSMEM_MapRealToLinear(cur_ptr
);
491 /* get first 8 characters */
492 strncpy(dname
,name
,8);
493 /* if less than 8 characters, pad with spaces */
494 for (cnt
=0; cnt
<8; cnt
++)
495 if (!dname
[cnt
]) dname
[cnt
]=' ';
497 /* search for char devices with the right name */
499 ((!(cur
->attr
& ATTR_CHAR
)) ||
500 memcmp(cur
->name
,dname
,8))) {
501 cur_ptr
= cur
->next_dev
;
502 if (cur_ptr
== NONEXT
) cur
=NULL
;
503 else cur
= DOSMEM_MapRealToLinear(cur_ptr
);
508 static void DOSDEV_DoReq(void*req
, DWORD dev
)
510 REQUEST_HEADER
*hdr
= (REQUEST_HEADER
*)req
;
511 DOS_DEVICE_HEADER
*dhdr
;
515 dhdr
= DOSMEM_MapRealToLinear(dev
);
516 phdr
= ((char*)DOSMEM_LOL()) + ALLDEV_OFS
;
518 /* copy request to request scratch area */
519 memcpy(phdr
, req
, hdr
->size
);
521 /* prepare to call device driver */
522 memset(&ctx
, 0, sizeof(ctx
));
524 /* ES:BX points to request for strategy routine */
525 ES_reg(&ctx
) = HIWORD(DOS_LOLSeg
);
526 BX_reg(&ctx
) = ALLDEV_OFS
;
528 /* call strategy routine */
529 CS_reg(&ctx
) = SELECTOROF(dev
);
530 IP_reg(&ctx
) = dhdr
->strategy
;
531 DPMI_CallRMProc(&ctx
, 0, 0, 0);
533 /* call interrupt routine */
534 CS_reg(&ctx
) = SELECTOROF(dev
);
535 IP_reg(&ctx
) = dhdr
->interrupt
;
536 DPMI_CallRMProc(&ctx
, 0, 0, 0);
538 /* completed, copy request back */
539 memcpy(req
, phdr
, hdr
->size
);
541 if (hdr
->status
& STAT_ERROR
) {
542 switch (hdr
->status
& STAT_MASK
) {
543 case 0x0F: /* invalid disk change */
544 /* this error seems to fit the bill */
545 SetLastError(ER_NotSameDevice
);
548 SetLastError((hdr
->status
& STAT_MASK
) + 0x13);
554 static int DOSDEV_IO(unsigned cmd
, DWORD dev
, DWORD buf
, int buflen
)
558 req
.hdr
.size
=sizeof(req
);
559 req
.hdr
.unit
=0; /* not dealing with block devices yet */
561 req
.hdr
.status
=STAT_BUSY
;
562 req
.media
=0; /* not dealing with block devices yet */
565 req
.sector
=0; /* block devices */
566 req
.volume
=0; /* block devices */
568 DOSDEV_DoReq(&req
, dev
);
573 int DOSDEV_Peek(DWORD dev
, BYTE
*data
)
577 req
.hdr
.size
=sizeof(req
);
578 req
.hdr
.unit
=0; /* not dealing with block devices yet */
579 req
.hdr
.command
=CMD_SAFEINPUT
;
580 req
.hdr
.status
=STAT_BUSY
;
583 DOSDEV_DoReq(&req
, dev
);
585 if (req
.hdr
.status
& STAT_BUSY
) return 0;
591 int DOSDEV_Read(DWORD dev
, DWORD buf
, int buflen
)
593 return DOSDEV_IO(CMD_INPUT
, dev
, buf
, buflen
);
596 int DOSDEV_Write(DWORD dev
, DWORD buf
, int buflen
, int verify
)
598 return DOSDEV_IO(verify
?CMD_SAFEOUTPUT
:CMD_OUTPUT
, dev
, buf
, buflen
);
601 int DOSDEV_IoctlRead(DWORD dev
, DWORD buf
, int buflen
)
603 return DOSDEV_IO(CMD_INIOCTL
, dev
, buf
, buflen
);
606 int DOSDEV_IoctlWrite(DWORD dev
, DWORD buf
, int buflen
)
608 return DOSDEV_IO(CMD_OUTIOCTL
, dev
, buf
, buflen
);