GetLongPathName rewrite.
[wine.git] / msdos / devices.c
blob5d3632806f742afae3acb5f430aa6ad76bc34a00
1 /*
2 * DOS devices
4 * Copyright 1999 Ove Kåven
5 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include "wine/winbase16.h"
10 #include "msdos.h"
11 #include "miscemu.h"
12 #include "dosexe.h"
13 #include "debugtools.h"
15 #include "pshpack1.h"
17 typedef struct {
18 DOS_DEVICE_HEADER hdr;
19 BYTE ljmp1;
20 RMCBPROC strategy;
21 BYTE ljmp2;
22 RMCBPROC interrupt;
23 } WINEDEV;
25 typedef struct {
26 BYTE size; /* length of header + data */
27 BYTE unit; /* unit (block devices only) */
28 BYTE command;
29 WORD status;
30 BYTE reserved[8];
31 } REQUEST_HEADER;
33 typedef struct {
34 REQUEST_HEADER hdr;
35 BYTE media; /* media descriptor from BPB */
36 SEGPTR buffer;
37 WORD count; /* byte/sector count */
38 WORD sector; /* starting sector (block devices) */
39 DWORD volume; /* volume ID (block devices) */
40 } REQ_IO;
42 typedef struct {
43 REQUEST_HEADER hdr;
44 BYTE data;
45 } REQ_SAFEINPUT;
47 #include "poppack.h"
49 #define REQ_SCRATCH sizeof(REQ_IO)
51 #define SYSTEM_STRATEGY_NUL 0x0100
52 #define SYSTEM_STRATEGY_CON 0x0101
54 DWORD DOS_LOLSeg;
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
72 #define CMD_INIT 0
73 #define CMD_MEDIACHECK 1 /* block devices */
74 #define CMD_BUILDBPB 2 /* block devices */
75 #define CMD_INIOCTL 3
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
95 #define LJMP 0xea
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)
102 /* prototypes */
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={
113 NONEXT,
114 ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
115 STRATEGY_OFS,INTERRUPT_OFS,
116 "NUL "
119 static WINEDEV devs[]={
121 {NONEXT,
122 ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
123 STRATEGY_OFS,INTERRUPT_OFS,
124 "CON "},
125 LJMP,con_strategy,
126 LJMP,con_interrupt}
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 EIP_reg(ctx) = *(stack++);
136 CS_reg(ctx) = *(stack++);
137 ESP_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);
145 if (!hdr_ptr) {
146 hdr_ptr = calloc(1,sizeof(void *)+extra);
147 DOSVM_SetSystemData(id, hdr_ptr);
150 *hdr_ptr = hdr;
151 do_lret(ctx);
154 static REQUEST_HEADER * get_hdr(int id, void**extra)
156 void **hdr_ptr = DOSVM_GetSystemData(id);
157 if (extra)
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) {
172 case CMD_INPUT:
173 ((REQ_IO*)hdr)->count = 0;
174 hdr->status = STAT_DONE;
175 break;
176 case CMD_SAFEINPUT:
177 hdr->status = STAT_DONE|STAT_BUSY;
178 break;
179 default:
180 hdr->status = STAT_DONE;
182 do_lret(ctx);
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)
194 int *scan;
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) {
206 case CMD_INPUT:
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 */
216 if (curbuffer) {
217 /* yep, copy as much as we can */
218 BYTE data = 0;
219 while ((len<count) && (data != '\r')) {
220 data = *curbuffer++;
221 buffer[len++] = data;
223 if (data == '\r') {
224 /* line buffer emptied */
225 lol->offs_unread_CON = 0;
226 curbuffer = NULL;
227 /* if we're not in raw mode, call it a day*/
228 if (!(con->attr & ATTR_RAW)) {
229 hdr->status = STAT_DONE;
230 io->count = len;
231 break;
233 } else {
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;
238 io->count = len;
239 break;
243 /* if we're in raw mode, we just need to fill the buffer */
244 if (con->attr & ATTR_RAW) {
245 while (len<count) {
246 WORD data;
248 /* do we have a waiting scancode? */
249 if (*scan) {
250 /* yes, store scancode in buffer */
251 buffer[len++] = *scan;
252 *scan = 0;
253 if (len==count) break;
256 /* check for new keyboard input */
257 while (CurOfs == bios->FirstKbdCharPtr) {
258 /* no input available yet, so wait... */
259 DOSVM_Wait( -1, 0 );
261 /* read from keyboard queue (call int16?) */
262 data = ((WORD*)bios)[CurOfs];
263 CurOfs += 2;
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);
271 } else {
272 /* we're not in raw mode, so we need to do line input... */
273 while (TRUE) {
274 WORD data;
275 /* check for new keyboard input */
276 while (CurOfs == bios->FirstKbdCharPtr) {
277 /* no input available yet, so wait... */
278 DOSVM_Wait( -1, 0 );
280 /* read from keyboard queue (call int16?) */
281 data = ((WORD*)bios)[CurOfs];
282 CurOfs += 2;
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);
289 break;
291 else if (LOBYTE(data) >= ' ') {
292 /* a character */
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)) {
300 case '\b':
301 if (len>0) {
302 len--;
303 WriteFile(lpDosTask->hConOutput, "\b \b", 3, NULL, NULL);
305 break;
308 if (len > count) {
309 /* save rest of line for later */
310 lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
311 len = count;
313 memcpy(buffer, linebuffer, len);
315 hdr->status = STAT_DONE;
316 io->count = len;
318 break;
319 case CMD_SAFEINPUT:
320 if (curbuffer) {
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) {
327 /* no input */
328 hdr->status = STAT_DONE|STAT_BUSY;
329 } else {
330 /* some keyboard input waiting */
331 hdr->status = STAT_DONE;
332 ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
334 } else {
335 /* no line input */
336 hdr->status = STAT_DONE|STAT_BUSY;
338 break;
339 case CMD_INSTATUS:
340 if (curbuffer) {
341 /* we have data */
342 hdr->status = STAT_DONE;
344 else if (con->attr & ATTR_RAW) {
345 if (CurOfs == bios->FirstKbdCharPtr) {
346 /* no input */
347 hdr->status = STAT_DONE|STAT_BUSY;
348 } else {
349 /* some keyboard input waiting */
350 hdr->status = STAT_DONE;
352 } else {
353 /* no line input */
354 hdr->status = STAT_DONE|STAT_BUSY;
357 break;
358 case CMD_INFLUSH:
359 /* flush line and keyboard queue */
360 lol->offs_unread_CON = 0;
361 bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
362 break;
363 case CMD_OUTPUT:
364 case CMD_SAFEOUTPUT:
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));
370 DWORD result = 0;
371 WriteFile(lpDosTask->hConOutput, buffer, io->count, &result, NULL);
372 io->count = result;
373 hdr->status = STAT_DONE;
375 break;
376 default:
377 hdr->status = STAT_DONE;
379 do_lret(ctx);
382 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
385 Output of DOS 6.22:
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)
432 WINEDEV *dev;
433 DOS_DEVICE_HEADER *pdev;
434 UINT16 seg;
435 int n;
436 WORD ofs = DEV0_OFS;
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);
454 dev[0].ljmp1 = LJMP;
455 dev[0].strategy = (RMCBPROC)DPMI_AllocInternalRMCB(nul_strategy);
456 dev[0].ljmp2 = LJMP;
457 dev[0].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(nul_interrupt);
459 dev++;
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);
488 char dname[8];
489 int cnt;
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 */
498 while (cur &&
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);
505 return cur_ptr;
508 static void DOSDEV_DoReq(void*req, DWORD dev)
510 REQUEST_HEADER *hdr = (REQUEST_HEADER *)req;
511 DOS_DEVICE_HEADER *dhdr;
512 CONTEXT86 ctx;
513 char *phdr;
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 EBX_reg(&ctx) = ALLDEV_OFS;
528 /* call strategy routine */
529 CS_reg(&ctx) = SELECTOROF(dev);
530 EIP_reg(&ctx) = dhdr->strategy;
531 DPMI_CallRMProc(&ctx, 0, 0, 0);
533 /* call interrupt routine */
534 CS_reg(&ctx) = SELECTOROF(dev);
535 EIP_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);
546 break;
547 default:
548 SetLastError((hdr->status & STAT_MASK) + 0x13);
549 break;
554 static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen)
556 REQ_IO req;
558 req.hdr.size=sizeof(req);
559 req.hdr.unit=0; /* not dealing with block devices yet */
560 req.hdr.command=cmd;
561 req.hdr.status=STAT_BUSY;
562 req.media=0; /* not dealing with block devices yet */
563 req.buffer=buf;
564 req.count=buflen;
565 req.sector=0; /* block devices */
566 req.volume=0; /* block devices */
568 DOSDEV_DoReq(&req, dev);
570 return req.count;
573 int DOSDEV_Peek(DWORD dev, BYTE*data)
575 REQ_SAFEINPUT req;
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;
581 req.data=0;
583 DOSDEV_DoReq(&req, dev);
585 if (req.hdr.status & STAT_BUSY) return 0;
587 *data = req.data;
588 return 1;
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);