Move get current drive int21 function to winedos.
[wine] / dlls / winedos / devices.c
1 /*
2  * DOS devices
3  *
4  * Copyright 1999 Ove Kåven
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/winbase16.h"
24 #include "msdos.h"
25 #include "miscemu.h"
26 #include "dosexe.h"
27 #include "wine/debug.h"
28
29 #include "pshpack1.h"
30
31 typedef struct {
32   BYTE ljmp1;
33   RMCBPROC strategy;
34   BYTE ljmp2;
35   RMCBPROC interrupt;
36 } WINEDEV_THUNK;
37
38 typedef struct {
39   BYTE size; /* length of header + data */
40   BYTE unit; /* unit (block devices only) */
41   BYTE command;
42   WORD status;
43   BYTE reserved[8];
44 } REQUEST_HEADER;
45
46 typedef struct {
47   REQUEST_HEADER hdr;
48   BYTE media; /* media descriptor from BPB */
49   SEGPTR buffer;
50   WORD count; /* byte/sector count */
51   WORD sector; /* starting sector (block devices) */
52   DWORD volume; /* volume ID (block devices) */
53 } REQ_IO;
54
55 typedef struct {
56   REQUEST_HEADER hdr;
57   BYTE data;
58 } REQ_SAFEINPUT;
59
60 #include "poppack.h"
61
62 #define CON_BUFFER 128
63
64 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
65
66 static void *strategy_data[NB_SYSTEM_STRATEGIES];
67
68 #define NONEXT ((DWORD)-1)
69
70 #define ATTR_STDIN     0x0001
71 #define ATTR_STDOUT    0x0002
72 #define ATTR_NUL       0x0004
73 #define ATTR_CLOCK     0x0008
74 #define ATTR_FASTCON   0x0010
75 #define ATTR_RAW       0x0020
76 #define ATTR_NOTEOF    0x0040
77 #define ATTR_DEVICE    0x0080
78 #define ATTR_REMOVABLE 0x0800
79 #define ATTR_NONIBM    0x2000 /* block devices */
80 #define ATTR_UNTILBUSY 0x2000 /* char devices */
81 #define ATTR_IOCTL     0x4000
82 #define ATTR_CHAR      0x8000
83
84 #define CMD_INIT       0
85 #define CMD_MEDIACHECK 1 /* block devices */
86 #define CMD_BUILDBPB   2 /* block devices */
87 #define CMD_INIOCTL    3
88 #define CMD_INPUT      4 /* read data */
89 #define CMD_SAFEINPUT  5 /* "non-destructive input no wait", char devices */
90 #define CMD_INSTATUS   6 /* char devices */
91 #define CMD_INFLUSH    7 /* char devices */
92 #define CMD_OUTPUT     8 /* write data */
93 #define CMD_SAFEOUTPUT 9 /* write data with verify */
94 #define CMD_OUTSTATUS 10 /* char devices */
95 #define CMD_OUTFLUSH  11 /* char devices */
96 #define CMD_OUTIOCTL  12
97 #define CMD_DEVOPEN   13
98 #define CMD_DEVCLOSE  14
99 #define CMD_REMOVABLE 15 /* block devices */
100 #define CMD_UNTILBUSY 16 /* output until busy */
101
102 #define STAT_MASK  0x00FF
103 #define STAT_DONE  0x0100
104 #define STAT_BUSY  0x0200
105 #define STAT_ERROR 0x8000
106
107 #define LJMP 0xea
108
109
110 /* prototypes */
111 static void WINAPI nul_strategy(CONTEXT86*ctx);
112 static void WINAPI nul_interrupt(CONTEXT86*ctx);
113 static void WINAPI con_strategy(CONTEXT86*ctx);
114 static void WINAPI con_interrupt(CONTEXT86*ctx);
115
116 /* devices */
117 typedef struct
118 {
119     char name[8];
120     WORD attr;
121     RMCBPROC strategy;
122     RMCBPROC interrupt;
123 } WINEDEV;
124
125 static WINEDEV devs[] =
126 {
127   { "NUL     ",
128     ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
129     nul_strategy, nul_interrupt },
130
131   { "CON     ",
132     ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
133     con_strategy, con_interrupt }
134 };
135
136 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
137
138 /* DOS data segment */
139 typedef struct
140 {
141     DOS_LISTOFLISTS    lol;
142     DOS_DEVICE_HEADER  dev[NR_DEVS-1];
143     WINEDEV_THUNK      thunk[NR_DEVS];
144     REQ_IO             req;
145     BYTE               buffer[CON_BUFFER];
146
147 } DOS_DATASEG;
148
149 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
150
151 DWORD DOS_LOLSeg;
152
153 struct _DOS_LISTOFLISTS * DOSMEM_LOL()
154 {
155     return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0);
156 }
157
158
159 /* the device implementations */
160 static void do_lret(CONTEXT86*ctx)
161 {
162   WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
163
164   ctx->Eip   = *(stack++);
165   ctx->SegCs = *(stack++);
166   ctx->Esp  += 2*sizeof(WORD);
167 }
168
169 static void do_strategy(CONTEXT86*ctx, int id, int extra)
170 {
171   REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
172   void **hdr_ptr = strategy_data[id];
173
174   if (!hdr_ptr) {
175     hdr_ptr = calloc(1,sizeof(void *)+extra);
176     strategy_data[id] = hdr_ptr;
177   }
178   *hdr_ptr = hdr;
179   do_lret(ctx);
180 }
181
182 static REQUEST_HEADER * get_hdr(int id, void**extra)
183 {
184   void **hdr_ptr = strategy_data[id];
185   if (extra)
186     *extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL;
187   return hdr_ptr ? *hdr_ptr : (void *)NULL;
188 }
189
190 static void WINAPI nul_strategy(CONTEXT86*ctx)
191 {
192   do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
193 }
194
195 static void WINAPI nul_interrupt(CONTEXT86*ctx)
196 {
197   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
198   /* eat everything and recycle nothing */
199   switch (hdr->command) {
200   case CMD_INPUT:
201     ((REQ_IO*)hdr)->count = 0;
202     hdr->status = STAT_DONE;
203     break;
204   case CMD_SAFEINPUT:
205     hdr->status = STAT_DONE|STAT_BUSY;
206     break;
207   default:
208     hdr->status = STAT_DONE;
209   }
210   do_lret(ctx);
211 }
212
213 static void WINAPI con_strategy(CONTEXT86*ctx)
214 {
215   do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
216 }
217
218 static void WINAPI con_interrupt(CONTEXT86*ctx)
219 {
220   int *scan;
221   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
222   BIOSDATA *bios = BIOS_DATA;
223   WORD CurOfs = bios->NextKbdCharPtr;
224   DOS_LISTOFLISTS *lol = DOSMEM_LOL();
225   DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
226   BYTE *linebuffer = dataseg->buffer;
227   BYTE *curbuffer = (lol->offs_unread_CON) ?
228     (((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL;
229   DOS_DEVICE_HEADER *con = dataseg->dev;
230
231   switch (hdr->command) {
232   case CMD_INPUT:
233     {
234       REQ_IO *io = (REQ_IO *)hdr;
235       WORD count = io->count, len = 0;
236       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
237                                         SELECTOROF(io->buffer),
238                                         (DWORD)OFFSETOF(io->buffer));
239
240       hdr->status = STAT_BUSY;
241       /* first, check whether we already have data in line buffer */
242       if (curbuffer) {
243         /* yep, copy as much as we can */
244         BYTE data = 0;
245         while ((len<count) && (data != '\r')) {
246           data = *curbuffer++;
247           buffer[len++] = data;
248         }
249         if (data == '\r') {
250           /* line buffer emptied */
251           lol->offs_unread_CON = 0;
252           curbuffer = NULL;
253           /* if we're not in raw mode, call it a day */
254           if (!(con->attr & ATTR_RAW)) {
255             hdr->status = STAT_DONE;
256             io->count = len;
257             break;
258           }
259         } else {
260           /* still some data left */
261           lol->offs_unread_CON = curbuffer - (BYTE*)lol;
262           /* but buffer was filled, we're done */
263           hdr->status = STAT_DONE;
264           io->count = len;
265           break;
266         }
267       }
268
269       /* if we're in raw mode, we just need to fill the buffer */
270       if (con->attr & ATTR_RAW) {
271         while (len<count) {
272           WORD data;
273
274           /* do we have a waiting scancode? */
275           if (*scan) {
276             /* yes, store scancode in buffer */
277             buffer[len++] = *scan;
278             *scan = 0;
279             if (len==count) break;
280           }
281
282           /* check for new keyboard input */
283           while (CurOfs == bios->FirstKbdCharPtr) {
284             /* no input available yet, so wait... */
285             DOSVM_Wait( ctx );
286           }
287           /* read from keyboard queue (call int16?) */
288           data = ((WORD*)bios)[CurOfs];
289           CurOfs += 2;
290           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
291           bios->NextKbdCharPtr = CurOfs;
292           /* if it's an extended key, save scancode */
293           if (LOBYTE(data) == 0) *scan = HIBYTE(data);
294           /* store ASCII char in buffer */
295           buffer[len++] = LOBYTE(data);
296         }
297       } else {
298         /* we're not in raw mode, so we need to do line input... */
299         while (TRUE) {
300           WORD data;
301           /* check for new keyboard input */
302           while (CurOfs == bios->FirstKbdCharPtr) {
303             /* no input available yet, so wait... */
304             DOSVM_Wait( ctx );
305           }
306           /* read from keyboard queue (call int16?) */
307           data = ((WORD*)bios)[CurOfs];
308           CurOfs += 2;
309           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
310           bios->NextKbdCharPtr = CurOfs;
311
312           if (LOBYTE(data) == '\r') {
313             /* it's the return key, we're done */
314             linebuffer[len++] = LOBYTE(data);
315             break;
316           }
317           else if (LOBYTE(data) >= ' ') {
318             /* a character */
319             if ((len+1)<CON_BUFFER) {
320               linebuffer[len] = LOBYTE(data);
321               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, NULL, NULL);
322             }
323             /* else beep, but I don't like noise */
324           }
325           else switch (LOBYTE(data)) {
326           case '\b':
327             if (len>0) {
328               len--;
329               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, NULL, NULL);
330             }
331             break;
332           }
333         }
334         if (len > count) {
335           /* save rest of line for later */
336           lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
337           len = count;
338         }
339         memcpy(buffer, linebuffer, len);
340       }
341       hdr->status = STAT_DONE;
342       io->count = len;
343     }
344     break;
345   case CMD_SAFEINPUT:
346     if (curbuffer) {
347       /* some line input waiting */
348       hdr->status = STAT_DONE;
349       ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
350     }
351     else if (con->attr & ATTR_RAW) {
352       if (CurOfs == bios->FirstKbdCharPtr) {
353         /* no input */
354         hdr->status = STAT_DONE|STAT_BUSY;
355       } else {
356         /* some keyboard input waiting */
357         hdr->status = STAT_DONE;
358         ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
359       }
360     } else {
361       /* no line input */
362       hdr->status = STAT_DONE|STAT_BUSY;
363     }
364     break;
365   case CMD_INSTATUS:
366     if (curbuffer) {
367       /* we have data */
368       hdr->status = STAT_DONE;
369     }
370     else if (con->attr & ATTR_RAW) {
371       if (CurOfs == bios->FirstKbdCharPtr) {
372         /* no input */
373         hdr->status = STAT_DONE|STAT_BUSY;
374       } else {
375         /* some keyboard input waiting */
376         hdr->status = STAT_DONE;
377       }
378     } else {
379       /* no line input */
380       hdr->status = STAT_DONE|STAT_BUSY;
381     }
382
383     break;
384   case CMD_INFLUSH:
385     /* flush line and keyboard queue */
386     lol->offs_unread_CON = 0;
387     bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
388     break;
389   case CMD_OUTPUT:
390   case CMD_SAFEOUTPUT:
391     {
392       REQ_IO *io = (REQ_IO *)hdr;
393       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
394                                         SELECTOROF(io->buffer),
395                                         (DWORD)OFFSETOF(io->buffer));
396       DWORD result = 0;
397       WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
398       io->count = result;
399       hdr->status = STAT_DONE;
400     }
401     break;
402   default:
403     hdr->status = STAT_DONE;
404   }
405   do_lret(ctx);
406 }
407
408 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
409 {
410 /*
411 Output of DOS 6.22:
412
413 0133:0020                    6A 13-33 01 CC 00 33 01 59 00         j.3...3.Y.
414 0133:0030  70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05   p...r...m.3.....
415 0133:0040  00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D   .........!......
416 0133:0050  CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00   ..NUL     ......
417 0133:0060  00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF   .K...........p..
418 0133:0070  FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00   ................
419 0133:0080  00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02   ..............p.
420 0133:0090  D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD   .D...D...D...D..
421 0133:00A0  D0 44 C8 FD D0 44                                 .D...D
422 */
423   DOS_LOL->CX_Int21_5e01                = 0x0;
424   DOS_LOL->LRU_count_FCB_cache  = 0x0;
425   DOS_LOL->LRU_count_FCB_open           = 0x0;
426   DOS_LOL->OEM_func_handler             = -1; /* not available */
427   DOS_LOL->INT21_offset         = 0x0;
428   DOS_LOL->sharing_retry_count  = 3;
429   DOS_LOL->sharing_retry_delay  = 1;
430   DOS_LOL->ptr_disk_buf         = 0x0;
431   DOS_LOL->offs_unread_CON              = 0x0;
432   DOS_LOL->seg_first_MCB                = 0x0;
433   DOS_LOL->ptr_first_DPB                = 0x0;
434   DOS_LOL->ptr_first_SysFileTable       = 0x0;
435   DOS_LOL->ptr_clock_dev_hdr            = 0x0;
436   DOS_LOL->ptr_CON_dev_hdr              = 0x0;
437   DOS_LOL->max_byte_per_sec             = 512;
438   DOS_LOL->ptr_disk_buf_info            = 0x0;
439   DOS_LOL->ptr_array_CDS                = 0x0;
440   DOS_LOL->ptr_sys_FCB          = 0x0;
441   DOS_LOL->nr_protect_FCB               = 0x0;
442   DOS_LOL->nr_block_dev         = 0x0;
443   DOS_LOL->nr_avail_drive_letters       = 26; /* A - Z */
444   DOS_LOL->nr_drives_JOINed             = 0x0;
445   DOS_LOL->ptr_spec_prg_names           = 0x0;
446   DOS_LOL->ptr_SETVER_prg_list  = 0x0; /* no SETVER list */
447   DOS_LOL->DOS_HIGH_A20_func_offs       = 0x0;
448   DOS_LOL->PSP_last_exec                = 0x0;
449   DOS_LOL->BUFFERS_val          = 99; /* maximum: 99 */
450   DOS_LOL->BUFFERS_nr_lookahead = 8; /* maximum: 8 */
451   DOS_LOL->boot_drive                   = 3; /* C: */
452   DOS_LOL->flag_DWORD_moves             = 0x01; /* i386+ */
453   DOS_LOL->size_extended_mem            = 0xf000; /* very high value */
454 }
455
456 void DOSDEV_InstallDOSDevices(void)
457 {
458   DOS_DATASEG *dataseg;
459   WORD seg;
460   WORD selector;
461   unsigned int n;
462
463   /* allocate DOS data segment or something */
464   dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );
465
466   DOS_LOLSeg = MAKESEGPTR( seg, 0 );
467   DOSMEM_LOL()->wine_rm_lol = 
468       MAKESEGPTR( seg, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
469   DOSMEM_LOL()->wine_pm_lol = 
470       MAKESEGPTR( selector, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
471
472   /* initialize the magnificent List Of Lists */
473   InitListOfLists(&dataseg->lol);
474
475   /* Set up first device (NUL) */
476   dataseg->lol.NUL_dev.next_dev  = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
477   dataseg->lol.NUL_dev.attr      = devs[0].attr;
478   dataseg->lol.NUL_dev.strategy  = DOS_DATASEG_OFF(thunk[0].ljmp1);
479   dataseg->lol.NUL_dev.interrupt = DOS_DATASEG_OFF(thunk[0].ljmp2);
480   memcpy(dataseg->lol.NUL_dev.name, devs[0].name, 8);
481
482   /* Set up the remaining devices */
483   for (n = 1; n < NR_DEVS; n++)
484   {
485     dataseg->dev[n-1].next_dev  = (n+1) == NR_DEVS ? NONEXT :
486                                   MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[n]));
487     dataseg->dev[n-1].attr      = devs[n].attr;
488     dataseg->dev[n-1].strategy  = DOS_DATASEG_OFF(thunk[n].ljmp1);
489     dataseg->dev[n-1].interrupt = DOS_DATASEG_OFF(thunk[n].ljmp2);
490     memcpy(dataseg->dev[n-1].name, devs[n].name, 8);
491   }
492
493   /* Set up thunks */
494   for (n = 0; n < NR_DEVS; n++)
495   {
496     dataseg->thunk[n].ljmp1     = LJMP;
497     dataseg->thunk[n].strategy  = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].strategy);
498     dataseg->thunk[n].ljmp2     = LJMP;
499     dataseg->thunk[n].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].interrupt);
500   }
501
502   /* CON is device 1 */
503   dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
504 }
505
506 DWORD DOSDEV_Console(void)
507 {
508   return DOSMEM_LOL()->ptr_CON_dev_hdr;
509 }
510
511 DWORD DOSDEV_FindCharDevice(char*name)
512 {
513   SEGPTR cur_ptr = MAKESEGPTR(HIWORD(DOS_LOLSeg), FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev));
514   DOS_DEVICE_HEADER *cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
515   char dname[8];
516   int cnt;
517
518   /* get first 8 characters */
519   strncpy(dname,name,8);
520   /* if less than 8 characters, pad with spaces */
521   for (cnt=0; cnt<8; cnt++)
522     if (!dname[cnt]) dname[cnt]=' ';
523
524   /* search for char devices with the right name */
525   while (cur &&
526          ((!(cur->attr & ATTR_CHAR)) ||
527           memcmp(cur->name,dname,8))) {
528     cur_ptr = cur->next_dev;
529     if (cur_ptr == NONEXT) cur=NULL;
530     else cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
531   }
532   return cur_ptr;
533 }
534
535 static void DOSDEV_DoReq(void*req, DWORD dev)
536 {
537   REQUEST_HEADER *hdr = (REQUEST_HEADER *)req;
538   DOS_DEVICE_HEADER *dhdr;
539   CONTEXT86 ctx;
540   char *phdr;
541
542   dhdr = PTR_REAL_TO_LIN(SELECTOROF(dev),OFFSETOF(dev));
543   phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req);
544
545   /* copy request to request scratch area */
546   memcpy(phdr, req, hdr->size);
547
548   /* prepare to call device driver */
549   memset(&ctx, 0, sizeof(ctx));
550
551   /* ES:BX points to request for strategy routine */
552   ctx.SegEs = HIWORD(DOS_LOLSeg);
553   ctx.Ebx   = DOS_DATASEG_OFF(req);
554
555   /* call strategy routine */
556   ctx.SegCs = SELECTOROF(dev);
557   ctx.Eip   = dhdr->strategy;
558   DPMI_CallRMProc(&ctx, 0, 0, 0);
559
560   /* call interrupt routine */
561   ctx.SegCs = SELECTOROF(dev);
562   ctx.Eip   = dhdr->interrupt;
563   DPMI_CallRMProc(&ctx, 0, 0, 0);
564
565   /* completed, copy request back */
566   memcpy(req, phdr, hdr->size);
567
568   if (hdr->status & STAT_ERROR) {
569     switch (hdr->status & STAT_MASK) {
570     case 0x0F: /* invalid disk change */
571       /* this error seems to fit the bill */
572       SetLastError(ER_NotSameDevice);
573       break;
574     default:
575       SetLastError((hdr->status & STAT_MASK) + 0x13);
576       break;
577     }
578   }
579 }
580
581 static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen)
582 {
583   REQ_IO req;
584
585   req.hdr.size=sizeof(req);
586   req.hdr.unit=0; /* not dealing with block devices yet */
587   req.hdr.command=cmd;
588   req.hdr.status=STAT_BUSY;
589   req.media=0; /* not dealing with block devices yet */
590   req.buffer=buf;
591   req.count=buflen;
592   req.sector=0; /* block devices */
593   req.volume=0; /* block devices */
594
595   DOSDEV_DoReq(&req, dev);
596
597   return req.count;
598 }
599
600 int DOSDEV_Peek(DWORD dev, BYTE*data)
601 {
602   REQ_SAFEINPUT req;
603
604   req.hdr.size=sizeof(req);
605   req.hdr.unit=0; /* not dealing with block devices yet */
606   req.hdr.command=CMD_SAFEINPUT;
607   req.hdr.status=STAT_BUSY;
608   req.data=0;
609
610   DOSDEV_DoReq(&req, dev);
611
612   if (req.hdr.status & STAT_BUSY) return 0;
613
614   *data = req.data;
615   return 1;
616 }
617
618 int DOSDEV_Read(DWORD dev, DWORD buf, int buflen)
619 {
620   return DOSDEV_IO(CMD_INPUT, dev, buf, buflen);
621 }
622
623 int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify)
624 {
625   return DOSDEV_IO(verify?CMD_SAFEOUTPUT:CMD_OUTPUT, dev, buf, buflen);
626 }
627
628 int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen)
629 {
630   return DOSDEV_IO(CMD_INIOCTL, dev, buf, buflen);
631 }
632
633 int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen)
634 {
635   return DOSDEV_IO(CMD_OUTIOCTL, dev, buf, buflen);
636 }