msvcrt: Return child exit code in _pclose function.
[wine] / dlls / krnl386.exe16 / dosdev.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/winbase16.h"
24 #include "dosexe.h"
25 #include "wine/debug.h"
26
27 #include "pshpack1.h"
28
29 /* Warning: need to return LOL ptr w/ offset 0 (&ptr_first_DPB) to programs ! */
30 typedef struct _DOS_LISTOFLISTS
31 {
32     WORD  CX_Int21_5e01;        /* -24d contents of CX from INT 21/AX=5E01h */
33     WORD  LRU_count_FCB_cache;  /* -22d */
34     WORD  LRU_count_FCB_open;   /* -20d */
35     DWORD OEM_func_handler;     /* -18d OEM function of INT 21/AH=F8h */
36     WORD  INT21_offset;         /* -14d offset in DOS CS of code to return from INT 21 call */
37     WORD  sharing_retry_count;  /* -12d */
38     WORD  sharing_retry_delay;  /* -10d */
39     DWORD ptr_disk_buf;         /* -8d ptr to current disk buf */
40     WORD  offs_unread_CON;      /* -4d pointer in DOS data segment of unread CON input */
41     WORD  seg_first_MCB;        /* -2d */
42     DWORD ptr_first_DPB;        /* 00 */
43     DWORD ptr_first_SysFileTable; /* 04 */
44     DWORD ptr_clock_dev_hdr;    /* 08 */
45     DWORD ptr_CON_dev_hdr;      /* 0C */
46     WORD  max_byte_per_sec;     /* 10 maximum bytes per sector of any block device */
47     DWORD ptr_disk_buf_info;    /* 12 */
48     DWORD ptr_array_CDS;        /* 16 current directory structure */
49     DWORD ptr_sys_FCB;          /* 1A */
50     WORD  nr_protect_FCB;       /* 1E */
51     BYTE  nr_block_dev;         /* 20 */
52     BYTE  nr_avail_drive_letters; /* 21 */
53     DOS_DEVICE_HEADER NUL_dev;  /* 22 */
54     BYTE  nr_drives_JOINed;     /* 34 */
55     WORD  ptr_spec_prg_names;   /* 35 */
56     DWORD ptr_SETVER_prg_list;  /* 37 */
57     WORD DOS_HIGH_A20_func_offs;/* 3B */
58     WORD PSP_last_exec;         /* 3D if DOS in HMA: PSP of program executed last; if DOS low: 0000h */
59     WORD BUFFERS_val;           /* 3F */
60     WORD BUFFERS_nr_lookahead;  /* 41 */
61     BYTE boot_drive;            /* 43 */
62     BYTE flag_DWORD_moves;      /* 44 01h for 386+, 00h otherwise */
63     WORD size_extended_mem;     /* 45 size of extended mem in KB */
64     SEGPTR wine_rm_lol;         /* -- wine: Real mode pointer to LOL */
65     SEGPTR wine_pm_lol;         /* -- wine: Protected mode pointer to LOL */
66 } DOS_LISTOFLISTS;
67
68 #include "poppack.h"
69
70 #define CON_BUFFER 128
71
72 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
73
74 static void *strategy_data[NB_SYSTEM_STRATEGIES];
75
76 #define LJMP 0xea
77
78
79 /* prototypes */
80 static void WINAPI nul_strategy(CONTEXT*ctx);
81 static void WINAPI nul_interrupt(CONTEXT*ctx);
82 static void WINAPI con_strategy(CONTEXT*ctx);
83 static void WINAPI con_interrupt(CONTEXT*ctx);
84
85 /* devices */
86 static const WINEDEV devs[] =
87 {
88   { "NUL     ",
89     ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
90     nul_strategy, nul_interrupt },
91
92   { "CON     ",
93     ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
94     con_strategy, con_interrupt }
95 };
96
97 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
98
99 /* DOS data segment */
100 typedef struct
101 {
102     DOS_LISTOFLISTS    lol;
103     DOS_DEVICE_HEADER  dev[NR_DEVS-1];
104     DOS_DEVICE_HEADER *last_dev; /* ptr to last registered device driver */
105     WINEDEV_THUNK      thunk[NR_DEVS];
106     REQ_IO             req;
107     BYTE               buffer[CON_BUFFER];
108
109 } DOS_DATASEG;
110
111 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
112
113 static DWORD DOS_LOLSeg;
114
115 static struct _DOS_LISTOFLISTS * DOSMEM_LOL(void)
116 {
117     return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0);
118 }
119
120
121 /* the device implementations */
122 static void do_lret(CONTEXT*ctx)
123 {
124   WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
125
126   ctx->Eip   = *(stack++);
127   ctx->SegCs = *(stack++);
128   ctx->Esp  += 2*sizeof(WORD);
129 }
130
131 static void do_strategy(CONTEXT*ctx, int id, int extra)
132 {
133   REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
134   void **hdr_ptr = strategy_data[id];
135
136   if (!hdr_ptr) {
137     hdr_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(void *)+extra);
138     strategy_data[id] = hdr_ptr;
139   }
140   *hdr_ptr = hdr;
141   do_lret(ctx);
142 }
143
144 static REQUEST_HEADER * get_hdr(int id, void**extra)
145 {
146   void **hdr_ptr = strategy_data[id];
147   if (extra)
148     *extra = hdr_ptr ? (void*)(hdr_ptr+1) : NULL;
149   return hdr_ptr ? *hdr_ptr : NULL;
150 }
151
152 static void WINAPI nul_strategy(CONTEXT*ctx)
153 {
154   do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
155 }
156
157 static void WINAPI nul_interrupt(CONTEXT*ctx)
158 {
159   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
160   /* eat everything and recycle nothing */
161   switch (hdr->command) {
162   case CMD_INPUT:
163     ((REQ_IO*)hdr)->count = 0;
164     hdr->status = STAT_DONE;
165     break;
166   case CMD_SAFEINPUT:
167     hdr->status = STAT_DONE|STAT_BUSY;
168     break;
169   default:
170     hdr->status = STAT_DONE;
171   }
172   do_lret(ctx);
173 }
174
175 static void WINAPI con_strategy(CONTEXT*ctx)
176 {
177   do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
178 }
179
180 static void WINAPI con_interrupt(CONTEXT*ctx)
181 {
182   int *scan;
183   REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
184   BIOSDATA *bios = DOSVM_BiosData();
185   WORD CurOfs = bios->NextKbdCharPtr;
186   DOS_LISTOFLISTS *lol = DOSMEM_LOL();
187   DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
188   BYTE *linebuffer = dataseg->buffer;
189   BYTE *curbuffer = (lol->offs_unread_CON) ?
190     (((BYTE*)dataseg) + lol->offs_unread_CON) : NULL;
191   DOS_DEVICE_HEADER *con = dataseg->dev;
192   DWORD w;
193
194   switch (hdr->command) {
195   case CMD_INPUT:
196     {
197       REQ_IO *io = (REQ_IO *)hdr;
198       WORD count = io->count, len = 0;
199       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
200                                         SELECTOROF(io->buffer),
201                                         (DWORD)OFFSETOF(io->buffer));
202
203       hdr->status = STAT_BUSY;
204       /* first, check whether we already have data in line buffer */
205       if (curbuffer) {
206         /* yep, copy as much as we can */
207         BYTE data = 0;
208         while ((len<count) && (data != '\r')) {
209           data = *curbuffer++;
210           buffer[len++] = data;
211         }
212         if (data == '\r') {
213           /* line buffer emptied */
214           lol->offs_unread_CON = 0;
215           curbuffer = NULL;
216           /* if we're not in raw mode, call it a day */
217           if (!(con->attr & ATTR_RAW)) {
218             hdr->status = STAT_DONE;
219             io->count = len;
220             break;
221           }
222         } else {
223           /* still some data left */
224           lol->offs_unread_CON = curbuffer - (BYTE*)lol;
225           /* but buffer was filled, we're done */
226           hdr->status = STAT_DONE;
227           io->count = len;
228           break;
229         }
230       }
231
232       /* if we're in raw mode, we just need to fill the buffer */
233       if (con->attr & ATTR_RAW) {
234         while (len<count) {
235           WORD data;
236
237           /* do we have a waiting scancode? */
238           if (*scan) {
239             /* yes, store scancode in buffer */
240             buffer[len++] = *scan;
241             *scan = 0;
242             if (len==count) break;
243           }
244
245           /* check for new keyboard input */
246           while (CurOfs == bios->FirstKbdCharPtr) {
247             /* no input available yet, so wait... */
248             DOSVM_Wait( ctx );
249           }
250           /* read from keyboard queue (call int16?) */
251           data = ((WORD*)bios)[CurOfs];
252           CurOfs += 2;
253           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
254           bios->NextKbdCharPtr = CurOfs;
255           /* if it's an extended key, save scancode */
256           if (LOBYTE(data) == 0) *scan = HIBYTE(data);
257           /* store ASCII char in buffer */
258           buffer[len++] = LOBYTE(data);
259         }
260       } else {
261         /* we're not in raw mode, so we need to do line input... */
262         while (TRUE) {
263           WORD data;
264           /* check for new keyboard input */
265           while (CurOfs == bios->FirstKbdCharPtr) {
266             /* no input available yet, so wait... */
267             DOSVM_Wait( ctx );
268           }
269           /* read from keyboard queue (call int16?) */
270           data = ((WORD*)bios)[CurOfs];
271           CurOfs += 2;
272           if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
273           bios->NextKbdCharPtr = CurOfs;
274
275           if (LOBYTE(data) == '\r') {
276             /* it's the return key, we're done */
277             linebuffer[len++] = LOBYTE(data);
278             break;
279           }
280           else if (LOBYTE(data) >= ' ') {
281             /* a character */
282             if ((len+1)<CON_BUFFER) {
283               linebuffer[len] = LOBYTE(data);
284               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, &w, NULL);
285             }
286             /* else beep, but I don't like noise */
287           }
288           else switch (LOBYTE(data)) {
289           case '\b':
290             if (len>0) {
291               len--;
292               WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, &w, NULL);
293             }
294             break;
295           }
296         }
297         if (len > count) {
298           /* save rest of line for later */
299           lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
300           len = count;
301         }
302         memcpy(buffer, linebuffer, len);
303       }
304       hdr->status = STAT_DONE;
305       io->count = len;
306     }
307     break;
308   case CMD_SAFEINPUT:
309     if (curbuffer) {
310       /* some line input waiting */
311       hdr->status = STAT_DONE;
312       ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
313     }
314     else if (con->attr & ATTR_RAW) {
315       if (CurOfs == bios->FirstKbdCharPtr) {
316         /* no input */
317         hdr->status = STAT_DONE|STAT_BUSY;
318       } else {
319         /* some keyboard input waiting */
320         hdr->status = STAT_DONE;
321         ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
322       }
323     } else {
324       /* no line input */
325       hdr->status = STAT_DONE|STAT_BUSY;
326     }
327     break;
328   case CMD_INSTATUS:
329     if (curbuffer) {
330       /* we have data */
331       hdr->status = STAT_DONE;
332     }
333     else if (con->attr & ATTR_RAW) {
334       if (CurOfs == bios->FirstKbdCharPtr) {
335         /* no input */
336         hdr->status = STAT_DONE|STAT_BUSY;
337       } else {
338         /* some keyboard input waiting */
339         hdr->status = STAT_DONE;
340       }
341     } else {
342       /* no line input */
343       hdr->status = STAT_DONE|STAT_BUSY;
344     }
345
346     break;
347   case CMD_INFLUSH:
348     /* flush line and keyboard queue */
349     lol->offs_unread_CON = 0;
350     bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
351     break;
352   case CMD_OUTPUT:
353   case CMD_SAFEOUTPUT:
354     {
355       REQ_IO *io = (REQ_IO *)hdr;
356       BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
357                                         SELECTOROF(io->buffer),
358                                         (DWORD)OFFSETOF(io->buffer));
359       DWORD result = 0;
360       WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
361       io->count = result;
362       hdr->status = STAT_DONE;
363     }
364     break;
365   default:
366     hdr->status = STAT_DONE;
367   }
368   do_lret(ctx);
369 }
370
371 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
372 {
373 /*
374 Output of DOS 6.22:
375
376 0133:0020                    6A 13-33 01 CC 00 33 01 59 00         j.3...3.Y.
377 0133:0030  70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05   p...r...m.3.....
378 0133:0040  00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D   .........!......
379 0133:0050  CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00   ..NUL     ......
380 0133:0060  00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF   .K...........p..
381 0133:0070  FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00   ................
382 0133:0080  00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02   ..............p.
383 0133:0090  D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD   .D...D...D...D..
384 0133:00A0  D0 44 C8 FD D0 44                                 .D...D
385 */
386   DOS_LOL->CX_Int21_5e01                = 0x0;
387   DOS_LOL->LRU_count_FCB_cache  = 0x0;
388   DOS_LOL->LRU_count_FCB_open           = 0x0;
389   DOS_LOL->OEM_func_handler             = -1; /* not available */
390   DOS_LOL->INT21_offset         = 0x0;
391   DOS_LOL->sharing_retry_count  = 3;
392   DOS_LOL->sharing_retry_delay  = 1;
393   DOS_LOL->ptr_disk_buf         = 0x0;
394   DOS_LOL->offs_unread_CON              = 0x0;
395   DOS_LOL->seg_first_MCB                = 0x0;
396   DOS_LOL->ptr_first_DPB                = 0x0;
397   DOS_LOL->ptr_first_SysFileTable       = 0x0;
398   DOS_LOL->ptr_clock_dev_hdr            = 0x0;
399   DOS_LOL->ptr_CON_dev_hdr              = 0x0;
400   DOS_LOL->max_byte_per_sec             = 512;
401   DOS_LOL->ptr_disk_buf_info            = 0x0;
402   DOS_LOL->ptr_array_CDS                = 0x0;
403   DOS_LOL->ptr_sys_FCB          = 0x0;
404   DOS_LOL->nr_protect_FCB               = 0x0;
405   DOS_LOL->nr_block_dev         = 0x0;
406   DOS_LOL->nr_avail_drive_letters       = 26; /* A - Z */
407   DOS_LOL->nr_drives_JOINed             = 0x0;
408   DOS_LOL->ptr_spec_prg_names           = 0x0;
409   DOS_LOL->ptr_SETVER_prg_list  = 0x0; /* no SETVER list */
410   DOS_LOL->DOS_HIGH_A20_func_offs       = 0x0;
411   DOS_LOL->PSP_last_exec                = 0x0;
412   DOS_LOL->BUFFERS_val          = 99; /* maximum: 99 */
413   DOS_LOL->BUFFERS_nr_lookahead = 8; /* maximum: 8 */
414   DOS_LOL->boot_drive                   = 3; /* C: */
415   DOS_LOL->flag_DWORD_moves             = 0x01; /* i386+ */
416   DOS_LOL->size_extended_mem            = 0xf000; /* very high value */
417 }
418
419 void DOSDEV_SetupDevice(const WINEDEV * devinfo,
420                         WORD seg, WORD off_dev, WORD off_thunk)
421 {
422   DOS_DEVICE_HEADER *dev = PTR_REAL_TO_LIN(seg, off_dev);
423   WINEDEV_THUNK *thunk = PTR_REAL_TO_LIN(seg, off_thunk);
424   DOS_DATASEG *dataseg = (DOS_DATASEG*)DOSMEM_LOL();
425
426   dev->attr = devinfo->attr;
427   dev->strategy  = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp1);
428   dev->interrupt = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp2);
429   memcpy(dev->name, devinfo->name, 8);
430
431   thunk->ljmp1     = LJMP;
432   thunk->strategy  = DPMI_AllocInternalRMCB(devinfo->strategy);
433   thunk->ljmp2     = LJMP;
434   thunk->interrupt = DPMI_AllocInternalRMCB(devinfo->interrupt);
435
436   dev->next_dev = NONEXT;
437   if (dataseg->last_dev)
438       dataseg->last_dev->next_dev = MAKESEGPTR(seg, off_dev);
439   dataseg->last_dev = dev;
440 }
441
442 void DOSDEV_InstallDOSDevices(void)
443 {
444   DOS_DATASEG *dataseg;
445   WORD seg;
446   WORD selector;
447   unsigned int n;
448
449   /* allocate DOS data segment or something */
450   dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );
451
452   DOS_LOLSeg = MAKESEGPTR( seg, 0 );
453   DOSMEM_LOL()->wine_rm_lol = 
454       MAKESEGPTR( seg, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
455   DOSMEM_LOL()->wine_pm_lol = 
456       MAKESEGPTR( selector, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
457
458   /* initialize the magnificent List Of Lists */
459   InitListOfLists(&dataseg->lol);
460
461   /* Set up first device (NUL) */
462   dataseg->last_dev = NULL;
463   DOSDEV_SetupDevice( &devs[0],
464                       seg,
465                       DOS_DATASEG_OFF(lol.NUL_dev),
466                       DOS_DATASEG_OFF(thunk[0]) );
467
468   /* Set up the remaining devices */
469   for (n = 1; n < NR_DEVS; n++)
470     DOSDEV_SetupDevice( &devs[n],
471                         seg,
472                         DOS_DATASEG_OFF(dev[n-1]),
473                         DOS_DATASEG_OFF(thunk[n]) );
474
475   /* CON is device 1 */
476   dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
477 }
478
479 void DOSDEV_SetSharingRetry(WORD delay, WORD count)
480 {
481     DOSMEM_LOL()->sharing_retry_delay = delay;
482     if (count) DOSMEM_LOL()->sharing_retry_count = count;
483 }
484
485 SEGPTR DOSDEV_GetLOL(BOOL v86)
486 {
487     if (v86) return DOSMEM_LOL()->wine_rm_lol;
488     else return DOSMEM_LOL()->wine_pm_lol;
489 }