Removed IsBadStringPtrA call that caused trouble.
[wine/hacks.git] / dlls / winedos / devices.c
blob451e7bf39f1b4ae57312af975591434ac09ed625
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 BYTE ljmp1;
19 RMCBPROC strategy;
20 BYTE ljmp2;
21 RMCBPROC interrupt;
22 } WINEDEV_THUNK;
24 typedef struct {
25 BYTE size; /* length of header + data */
26 BYTE unit; /* unit (block devices only) */
27 BYTE command;
28 WORD status;
29 BYTE reserved[8];
30 } REQUEST_HEADER;
32 typedef struct {
33 REQUEST_HEADER hdr;
34 BYTE media; /* media descriptor from BPB */
35 SEGPTR buffer;
36 WORD count; /* byte/sector count */
37 WORD sector; /* starting sector (block devices) */
38 DWORD volume; /* volume ID (block devices) */
39 } REQ_IO;
41 typedef struct {
42 REQUEST_HEADER hdr;
43 BYTE data;
44 } REQ_SAFEINPUT;
46 #include "poppack.h"
48 #define CON_BUFFER 128
50 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
52 static void *strategy_data[NB_SYSTEM_STRATEGIES];
54 #define NONEXT ((DWORD)-1)
56 #define ATTR_STDIN 0x0001
57 #define ATTR_STDOUT 0x0002
58 #define ATTR_NUL 0x0004
59 #define ATTR_CLOCK 0x0008
60 #define ATTR_FASTCON 0x0010
61 #define ATTR_RAW 0x0020
62 #define ATTR_NOTEOF 0x0040
63 #define ATTR_DEVICE 0x0080
64 #define ATTR_REMOVABLE 0x0800
65 #define ATTR_NONIBM 0x2000 /* block devices */
66 #define ATTR_UNTILBUSY 0x2000 /* char devices */
67 #define ATTR_IOCTL 0x4000
68 #define ATTR_CHAR 0x8000
70 #define CMD_INIT 0
71 #define CMD_MEDIACHECK 1 /* block devices */
72 #define CMD_BUILDBPB 2 /* block devices */
73 #define CMD_INIOCTL 3
74 #define CMD_INPUT 4 /* read data */
75 #define CMD_SAFEINPUT 5 /* "non-destructive input no wait", char devices */
76 #define CMD_INSTATUS 6 /* char devices */
77 #define CMD_INFLUSH 7 /* char devices */
78 #define CMD_OUTPUT 8 /* write data */
79 #define CMD_SAFEOUTPUT 9 /* write data with verify */
80 #define CMD_OUTSTATUS 10 /* char devices */
81 #define CMD_OUTFLUSH 11 /* char devices */
82 #define CMD_OUTIOCTL 12
83 #define CMD_DEVOPEN 13
84 #define CMD_DEVCLOSE 14
85 #define CMD_REMOVABLE 15 /* block devices */
86 #define CMD_UNTILBUSY 16 /* output until busy */
88 #define STAT_MASK 0x00FF
89 #define STAT_DONE 0x0100
90 #define STAT_BUSY 0x0200
91 #define STAT_ERROR 0x8000
93 #define LJMP 0xea
96 /* prototypes */
97 static void WINAPI nul_strategy(CONTEXT86*ctx);
98 static void WINAPI nul_interrupt(CONTEXT86*ctx);
99 static void WINAPI con_strategy(CONTEXT86*ctx);
100 static void WINAPI con_interrupt(CONTEXT86*ctx);
102 /* devices */
103 typedef struct
105 char name[8];
106 WORD attr;
107 RMCBPROC strategy;
108 RMCBPROC interrupt;
109 } WINEDEV;
111 static WINEDEV devs[] =
113 { "NUL ",
114 ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
115 nul_strategy, nul_interrupt },
117 { "CON ",
118 ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
119 con_strategy, con_interrupt }
122 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
124 /* DOS data segment */
125 typedef struct
127 DOS_LISTOFLISTS lol;
128 DOS_DEVICE_HEADER dev[NR_DEVS-1];
129 WINEDEV_THUNK thunk[NR_DEVS];
130 REQ_IO req;
131 BYTE buffer[CON_BUFFER];
133 } DOS_DATASEG;
135 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
137 struct _DOS_LISTOFLISTS * DOSMEM_LOL()
139 return (struct _DOS_LISTOFLISTS *)DOSMEM_MapRealToLinear
140 (MAKESEGPTR(HIWORD(DOS_LOLSeg),0));
144 /* the device implementations */
145 static void do_lret(CONTEXT86*ctx)
147 WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
149 ctx->Eip = *(stack++);
150 ctx->SegCs = *(stack++);
151 ctx->Esp += 2*sizeof(WORD);
154 static void do_strategy(CONTEXT86*ctx, int id, int extra)
156 REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
157 void **hdr_ptr = strategy_data[id];
159 if (!hdr_ptr) {
160 hdr_ptr = calloc(1,sizeof(void *)+extra);
161 strategy_data[id] = hdr_ptr;
163 *hdr_ptr = hdr;
164 do_lret(ctx);
167 static REQUEST_HEADER * get_hdr(int id, void**extra)
169 void **hdr_ptr = strategy_data[id];
170 if (extra)
171 *extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL;
172 return hdr_ptr ? *hdr_ptr : (void *)NULL;
175 static void WINAPI nul_strategy(CONTEXT86*ctx)
177 do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
180 static void WINAPI nul_interrupt(CONTEXT86*ctx)
182 REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
183 /* eat everything and recycle nothing */
184 switch (hdr->command) {
185 case CMD_INPUT:
186 ((REQ_IO*)hdr)->count = 0;
187 hdr->status = STAT_DONE;
188 break;
189 case CMD_SAFEINPUT:
190 hdr->status = STAT_DONE|STAT_BUSY;
191 break;
192 default:
193 hdr->status = STAT_DONE;
195 do_lret(ctx);
198 static void WINAPI con_strategy(CONTEXT86*ctx)
200 do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
203 static void WINAPI con_interrupt(CONTEXT86*ctx)
205 int *scan;
206 REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
207 BIOSDATA *bios = DOSMEM_BiosData();
208 WORD CurOfs = bios->NextKbdCharPtr;
209 DOS_LISTOFLISTS *lol = DOSMEM_LOL();
210 DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
211 BYTE *linebuffer = dataseg->buffer;
212 BYTE *curbuffer = (lol->offs_unread_CON) ?
213 (((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL;
214 DOS_DEVICE_HEADER *con = dataseg->dev;
216 switch (hdr->command) {
217 case CMD_INPUT:
219 REQ_IO *io = (REQ_IO *)hdr;
220 WORD count = io->count, len = 0;
221 BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
222 SELECTOROF(io->buffer),
223 (DWORD)OFFSETOF(io->buffer));
225 hdr->status = STAT_BUSY;
226 /* first, check whether we already have data in line buffer */
227 if (curbuffer) {
228 /* yep, copy as much as we can */
229 BYTE data = 0;
230 while ((len<count) && (data != '\r')) {
231 data = *curbuffer++;
232 buffer[len++] = data;
234 if (data == '\r') {
235 /* line buffer emptied */
236 lol->offs_unread_CON = 0;
237 curbuffer = NULL;
238 /* if we're not in raw mode, call it a day*/
239 if (!(con->attr & ATTR_RAW)) {
240 hdr->status = STAT_DONE;
241 io->count = len;
242 break;
244 } else {
245 /* still some data left */
246 lol->offs_unread_CON = curbuffer - (BYTE*)lol;
247 /* but buffer was filled, we're done */
248 hdr->status = STAT_DONE;
249 io->count = len;
250 break;
254 /* if we're in raw mode, we just need to fill the buffer */
255 if (con->attr & ATTR_RAW) {
256 while (len<count) {
257 WORD data;
259 /* do we have a waiting scancode? */
260 if (*scan) {
261 /* yes, store scancode in buffer */
262 buffer[len++] = *scan;
263 *scan = 0;
264 if (len==count) break;
267 /* check for new keyboard input */
268 while (CurOfs == bios->FirstKbdCharPtr) {
269 /* no input available yet, so wait... */
270 DOSVM_Wait( -1, 0 );
272 /* read from keyboard queue (call int16?) */
273 data = ((WORD*)bios)[CurOfs];
274 CurOfs += 2;
275 if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
276 bios->NextKbdCharPtr = CurOfs;
277 /* if it's an extended key, save scancode */
278 if (LOBYTE(data) == 0) *scan = HIBYTE(data);
279 /* store ASCII char in buffer */
280 buffer[len++] = LOBYTE(data);
282 } else {
283 /* we're not in raw mode, so we need to do line input... */
284 while (TRUE) {
285 WORD data;
286 /* check for new keyboard input */
287 while (CurOfs == bios->FirstKbdCharPtr) {
288 /* no input available yet, so wait... */
289 DOSVM_Wait( -1, 0 );
291 /* read from keyboard queue (call int16?) */
292 data = ((WORD*)bios)[CurOfs];
293 CurOfs += 2;
294 if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
295 bios->NextKbdCharPtr = CurOfs;
297 if (LOBYTE(data) == '\r') {
298 /* it's the return key, we're done */
299 linebuffer[len++] = LOBYTE(data);
300 break;
302 else if (LOBYTE(data) >= ' ') {
303 /* a character */
304 if ((len+1)<CON_BUFFER) {
305 linebuffer[len] = LOBYTE(data);
306 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, NULL, NULL);
308 /* else beep, but I don't like noise */
310 else switch (LOBYTE(data)) {
311 case '\b':
312 if (len>0) {
313 len--;
314 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, NULL, NULL);
316 break;
319 if (len > count) {
320 /* save rest of line for later */
321 lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
322 len = count;
324 memcpy(buffer, linebuffer, len);
326 hdr->status = STAT_DONE;
327 io->count = len;
329 break;
330 case CMD_SAFEINPUT:
331 if (curbuffer) {
332 /* some line input waiting */
333 hdr->status = STAT_DONE;
334 ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
336 else if (con->attr & ATTR_RAW) {
337 if (CurOfs == bios->FirstKbdCharPtr) {
338 /* no input */
339 hdr->status = STAT_DONE|STAT_BUSY;
340 } else {
341 /* some keyboard input waiting */
342 hdr->status = STAT_DONE;
343 ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
345 } else {
346 /* no line input */
347 hdr->status = STAT_DONE|STAT_BUSY;
349 break;
350 case CMD_INSTATUS:
351 if (curbuffer) {
352 /* we have data */
353 hdr->status = STAT_DONE;
355 else if (con->attr & ATTR_RAW) {
356 if (CurOfs == bios->FirstKbdCharPtr) {
357 /* no input */
358 hdr->status = STAT_DONE|STAT_BUSY;
359 } else {
360 /* some keyboard input waiting */
361 hdr->status = STAT_DONE;
363 } else {
364 /* no line input */
365 hdr->status = STAT_DONE|STAT_BUSY;
368 break;
369 case CMD_INFLUSH:
370 /* flush line and keyboard queue */
371 lol->offs_unread_CON = 0;
372 bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
373 break;
374 case CMD_OUTPUT:
375 case CMD_SAFEOUTPUT:
377 REQ_IO *io = (REQ_IO *)hdr;
378 BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
379 SELECTOROF(io->buffer),
380 (DWORD)OFFSETOF(io->buffer));
381 DWORD result = 0;
382 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
383 io->count = result;
384 hdr->status = STAT_DONE;
386 break;
387 default:
388 hdr->status = STAT_DONE;
390 do_lret(ctx);
393 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
396 Output of DOS 6.22:
398 0133:0020 6A 13-33 01 CC 00 33 01 59 00 j.3...3.Y.
399 0133:0030 70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05 p...r...m.3.....
400 0133:0040 00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D .........!......
401 0133:0050 CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00 ..NUL ......
402 0133:0060 00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF .K...........p..
403 0133:0070 FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00 ................
404 0133:0080 00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02 ..............p.
405 0133:0090 D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD .D...D...D...D..
406 0133:00A0 D0 44 C8 FD D0 44 .D...D
408 DOS_LOL->CX_Int21_5e01 = 0x0;
409 DOS_LOL->LRU_count_FCB_cache = 0x0;
410 DOS_LOL->LRU_count_FCB_open = 0x0;
411 DOS_LOL->OEM_func_handler = -1; /* not available */
412 DOS_LOL->INT21_offset = 0x0;
413 DOS_LOL->sharing_retry_count = 3;
414 DOS_LOL->sharing_retry_delay = 1;
415 DOS_LOL->ptr_disk_buf = 0x0;
416 DOS_LOL->offs_unread_CON = 0x0;
417 DOS_LOL->seg_first_MCB = 0x0;
418 DOS_LOL->ptr_first_DPB = 0x0;
419 DOS_LOL->ptr_first_SysFileTable = 0x0;
420 DOS_LOL->ptr_clock_dev_hdr = 0x0;
421 DOS_LOL->ptr_CON_dev_hdr = 0x0;
422 DOS_LOL->max_byte_per_sec = 512;
423 DOS_LOL->ptr_disk_buf_info = 0x0;
424 DOS_LOL->ptr_array_CDS = 0x0;
425 DOS_LOL->ptr_sys_FCB = 0x0;
426 DOS_LOL->nr_protect_FCB = 0x0;
427 DOS_LOL->nr_block_dev = 0x0;
428 DOS_LOL->nr_avail_drive_letters = 26; /* A - Z */
429 DOS_LOL->nr_drives_JOINed = 0x0;
430 DOS_LOL->ptr_spec_prg_names = 0x0;
431 DOS_LOL->ptr_SETVER_prg_list = 0x0; /* no SETVER list */
432 DOS_LOL->DOS_HIGH_A20_func_offs = 0x0;
433 DOS_LOL->PSP_last_exec = 0x0;
434 DOS_LOL->BUFFERS_val = 99; /* maximum: 99 */
435 DOS_LOL->BUFFERS_nr_lookahead = 8; /* maximum: 8 */
436 DOS_LOL->boot_drive = 3; /* C: */
437 DOS_LOL->flag_DWORD_moves = 0x01; /* i386+ */
438 DOS_LOL->size_extended_mem = 0xf000; /* very high value */
441 void DOSDEV_InstallDOSDevices(void)
443 DOS_DATASEG *dataseg;
444 UINT16 seg;
445 unsigned int n;
447 /* allocate DOS data segment or something */
448 DOS_LOLSeg = GlobalDOSAlloc16(sizeof(DOS_DATASEG));
449 seg = HIWORD(DOS_LOLSeg);
450 dataseg = MapSL( MAKESEGPTR(LOWORD(DOS_LOLSeg), 0) );
452 /* initialize the magnificent List Of Lists */
453 InitListOfLists(&dataseg->lol);
455 /* Set up first device (NUL) */
456 dataseg->lol.NUL_dev.next_dev = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
457 dataseg->lol.NUL_dev.attr = devs[0].attr;
458 dataseg->lol.NUL_dev.strategy = DOS_DATASEG_OFF(thunk[0].ljmp1);
459 dataseg->lol.NUL_dev.interrupt = DOS_DATASEG_OFF(thunk[0].ljmp2);
460 memcpy(dataseg->lol.NUL_dev.name, devs[0].name, 8);
462 /* Set up the remaining devices */
463 for (n = 1; n < NR_DEVS; n++)
465 dataseg->dev[n-1].next_dev = (n+1) == NR_DEVS ? NONEXT :
466 MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[n]));
467 dataseg->dev[n-1].attr = devs[n].attr;
468 dataseg->dev[n-1].strategy = DOS_DATASEG_OFF(thunk[n].ljmp1);
469 dataseg->dev[n-1].interrupt = DOS_DATASEG_OFF(thunk[n].ljmp2);
470 memcpy(dataseg->dev[n-1].name, devs[n].name, 8);
473 /* Set up thunks */
474 for (n = 0; n < NR_DEVS; n++)
476 dataseg->thunk[n].ljmp1 = LJMP;
477 dataseg->thunk[n].strategy = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].strategy);
478 dataseg->thunk[n].ljmp2 = LJMP;
479 dataseg->thunk[n].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].interrupt);
482 /* CON is device 1 */
483 dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
486 DWORD DOSDEV_Console(void)
488 return DOSMEM_LOL()->ptr_CON_dev_hdr;
491 DWORD DOSDEV_FindCharDevice(char*name)
493 SEGPTR cur_ptr = MAKESEGPTR(HIWORD(DOS_LOLSeg), FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev));
494 DOS_DEVICE_HEADER *cur = DOSMEM_MapRealToLinear(cur_ptr);
495 char dname[8];
496 int cnt;
498 /* get first 8 characters */
499 strncpy(dname,name,8);
500 /* if less than 8 characters, pad with spaces */
501 for (cnt=0; cnt<8; cnt++)
502 if (!dname[cnt]) dname[cnt]=' ';
504 /* search for char devices with the right name */
505 while (cur &&
506 ((!(cur->attr & ATTR_CHAR)) ||
507 memcmp(cur->name,dname,8))) {
508 cur_ptr = cur->next_dev;
509 if (cur_ptr == NONEXT) cur=NULL;
510 else cur = DOSMEM_MapRealToLinear(cur_ptr);
512 return cur_ptr;
515 static void DOSDEV_DoReq(void*req, DWORD dev)
517 REQUEST_HEADER *hdr = (REQUEST_HEADER *)req;
518 DOS_DEVICE_HEADER *dhdr;
519 CONTEXT86 ctx;
520 char *phdr;
522 dhdr = DOSMEM_MapRealToLinear(dev);
523 phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req);
525 /* copy request to request scratch area */
526 memcpy(phdr, req, hdr->size);
528 /* prepare to call device driver */
529 memset(&ctx, 0, sizeof(ctx));
531 /* ES:BX points to request for strategy routine */
532 ctx.SegEs = HIWORD(DOS_LOLSeg);
533 ctx.Ebx = DOS_DATASEG_OFF(req);
535 /* call strategy routine */
536 ctx.SegCs = SELECTOROF(dev);
537 ctx.Eip = dhdr->strategy;
538 DPMI_CallRMProc(&ctx, 0, 0, 0);
540 /* call interrupt routine */
541 ctx.SegCs = SELECTOROF(dev);
542 ctx.Eip = dhdr->interrupt;
543 DPMI_CallRMProc(&ctx, 0, 0, 0);
545 /* completed, copy request back */
546 memcpy(req, phdr, hdr->size);
548 if (hdr->status & STAT_ERROR) {
549 switch (hdr->status & STAT_MASK) {
550 case 0x0F: /* invalid disk change */
551 /* this error seems to fit the bill */
552 SetLastError(ER_NotSameDevice);
553 break;
554 default:
555 SetLastError((hdr->status & STAT_MASK) + 0x13);
556 break;
561 static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen)
563 REQ_IO req;
565 req.hdr.size=sizeof(req);
566 req.hdr.unit=0; /* not dealing with block devices yet */
567 req.hdr.command=cmd;
568 req.hdr.status=STAT_BUSY;
569 req.media=0; /* not dealing with block devices yet */
570 req.buffer=buf;
571 req.count=buflen;
572 req.sector=0; /* block devices */
573 req.volume=0; /* block devices */
575 DOSDEV_DoReq(&req, dev);
577 return req.count;
580 int DOSDEV_Peek(DWORD dev, BYTE*data)
582 REQ_SAFEINPUT req;
584 req.hdr.size=sizeof(req);
585 req.hdr.unit=0; /* not dealing with block devices yet */
586 req.hdr.command=CMD_SAFEINPUT;
587 req.hdr.status=STAT_BUSY;
588 req.data=0;
590 DOSDEV_DoReq(&req, dev);
592 if (req.hdr.status & STAT_BUSY) return 0;
594 *data = req.data;
595 return 1;
598 int DOSDEV_Read(DWORD dev, DWORD buf, int buflen)
600 return DOSDEV_IO(CMD_INPUT, dev, buf, buflen);
603 int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify)
605 return DOSDEV_IO(verify?CMD_SAFEOUTPUT:CMD_OUTPUT, dev, buf, buflen);
608 int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen)
610 return DOSDEV_IO(CMD_INIOCTL, dev, buf, buflen);
613 int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen)
615 return DOSDEV_IO(CMD_OUTIOCTL, dev, buf, buflen);