msvcrt/tests: Remove a space before a '\n'.
[wine/gsoc-2012-control.git] / dlls / winedos / devices.c
blob44bafbe1b6aecb64e23f95bd8a194951132c1229
1 /*
2 * DOS devices
4 * Copyright 1999 Ove Kåven
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.
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.
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
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/winbase16.h"
24 #include "dosexe.h"
25 #include "wine/debug.h"
27 #include "pshpack1.h"
29 /* Warning: need to return LOL ptr w/ offset 0 (&ptr_first_DPB) to programs ! */
30 typedef struct _DOS_LISTOFLISTS
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;
68 #include "poppack.h"
70 #define CON_BUFFER 128
72 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
74 static void *strategy_data[NB_SYSTEM_STRATEGIES];
76 #define LJMP 0xea
79 /* prototypes */
80 static void WINAPI nul_strategy(CONTEXT86*ctx);
81 static void WINAPI nul_interrupt(CONTEXT86*ctx);
82 static void WINAPI con_strategy(CONTEXT86*ctx);
83 static void WINAPI con_interrupt(CONTEXT86*ctx);
85 /* devices */
86 static const WINEDEV devs[] =
88 { "NUL ",
89 ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
90 nul_strategy, nul_interrupt },
92 { "CON ",
93 ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
94 con_strategy, con_interrupt }
97 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
99 /* DOS data segment */
100 typedef struct
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];
109 } DOS_DATASEG;
111 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
113 static DWORD DOS_LOLSeg;
115 static struct _DOS_LISTOFLISTS * DOSMEM_LOL(void)
117 return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0);
121 /* the device implementations */
122 static void do_lret(CONTEXT86*ctx)
124 WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
126 ctx->Eip = *(stack++);
127 ctx->SegCs = *(stack++);
128 ctx->Esp += 2*sizeof(WORD);
131 static void do_strategy(CONTEXT86*ctx, int id, int extra)
133 REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
134 void **hdr_ptr = strategy_data[id];
136 if (!hdr_ptr) {
137 hdr_ptr = calloc(1,sizeof(void *)+extra);
138 strategy_data[id] = hdr_ptr;
140 *hdr_ptr = hdr;
141 do_lret(ctx);
144 static REQUEST_HEADER * get_hdr(int id, void**extra)
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;
152 static void WINAPI nul_strategy(CONTEXT86*ctx)
154 do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
157 static void WINAPI nul_interrupt(CONTEXT86*ctx)
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;
172 do_lret(ctx);
175 static void WINAPI con_strategy(CONTEXT86*ctx)
177 do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
180 static void WINAPI con_interrupt(CONTEXT86*ctx)
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;
194 switch (hdr->command) {
195 case CMD_INPUT:
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));
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;
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;
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;
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;
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;
245 /* check for new keyboard input */
246 while (CurOfs == bios->FirstKbdCharPtr) {
247 /* no input available yet, so wait... */
248 DOSVM_Wait( ctx );
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);
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 );
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;
275 if (LOBYTE(data) == '\r') {
276 /* it's the return key, we're done */
277 linebuffer[len++] = LOBYTE(data);
278 break;
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);
286 /* else beep, but I don't like noise */
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);
294 break;
297 if (len > count) {
298 /* save rest of line for later */
299 lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
300 len = count;
302 memcpy(buffer, linebuffer, len);
304 hdr->status = STAT_DONE;
305 io->count = len;
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;
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];
323 } else {
324 /* no line input */
325 hdr->status = STAT_DONE|STAT_BUSY;
327 break;
328 case CMD_INSTATUS:
329 if (curbuffer) {
330 /* we have data */
331 hdr->status = STAT_DONE;
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;
341 } else {
342 /* no line input */
343 hdr->status = STAT_DONE|STAT_BUSY;
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:
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;
364 break;
365 default:
366 hdr->status = STAT_DONE;
368 do_lret(ctx);
371 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
374 Output of DOS 6.22:
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
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 */
419 void DOSDEV_SetupDevice(const WINEDEV * devinfo,
420 WORD seg, WORD off_dev, WORD off_thunk)
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();
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);
431 thunk->ljmp1 = LJMP;
432 thunk->strategy = DPMI_AllocInternalRMCB(devinfo->strategy);
433 thunk->ljmp2 = LJMP;
434 thunk->interrupt = DPMI_AllocInternalRMCB(devinfo->interrupt);
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;
442 void DOSDEV_InstallDOSDevices(void)
444 DOS_DATASEG *dataseg;
445 WORD seg;
446 WORD selector;
447 unsigned int n;
449 /* allocate DOS data segment or something */
450 dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );
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) );
458 /* initialize the magnificent List Of Lists */
459 InitListOfLists(&dataseg->lol);
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]) );
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]) );
475 /* CON is device 1 */
476 dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
479 void DOSDEV_SetSharingRetry(WORD delay, WORD count)
481 DOSMEM_LOL()->sharing_retry_delay = delay;
482 if (count) DOSMEM_LOL()->sharing_retry_count = count;
485 SEGPTR DOSDEV_GetLOL(BOOL v86)
487 if (v86) return DOSMEM_LOL()->wine_rm_lol;
488 else return DOSMEM_LOL()->wine_pm_lol;