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
23 #include "wine/winbase16.h"
25 #include "wine/debug.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 */
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
];
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
);
86 static const WINEDEV devs
[] =
89 ATTR_CHAR
|ATTR_NUL
|ATTR_DEVICE
,
90 nul_strategy
, nul_interrupt
},
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 */
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
];
107 BYTE buffer
[CON_BUFFER
];
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
];
137 hdr_ptr
= calloc(1,sizeof(void *)+extra
);
138 strategy_data
[id
] = hdr_ptr
;
144 static REQUEST_HEADER
* get_hdr(int id
, void**extra
)
146 void **hdr_ptr
= strategy_data
[id
];
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
) {
163 ((REQ_IO
*)hdr
)->count
= 0;
164 hdr
->status
= STAT_DONE
;
167 hdr
->status
= STAT_DONE
|STAT_BUSY
;
170 hdr
->status
= STAT_DONE
;
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
)
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
;
194 switch (hdr
->command
) {
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 */
206 /* yep, copy as much as we can */
208 while ((len
<count
) && (data
!= '\r')) {
210 buffer
[len
++] = data
;
213 /* line buffer emptied */
214 lol
->offs_unread_CON
= 0;
216 /* if we're not in raw mode, call it a day */
217 if (!(con
->attr
& ATTR_RAW
)) {
218 hdr
->status
= STAT_DONE
;
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
;
232 /* if we're in raw mode, we just need to fill the buffer */
233 if (con
->attr
& ATTR_RAW
) {
237 /* do we have a waiting scancode? */
239 /* yes, store scancode in buffer */
240 buffer
[len
++] = *scan
;
242 if (len
==count
) break;
245 /* check for new keyboard input */
246 while (CurOfs
== bios
->FirstKbdCharPtr
) {
247 /* no input available yet, so wait... */
250 /* read from keyboard queue (call int16?) */
251 data
= ((WORD
*)bios
)[CurOfs
];
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
);
261 /* we're not in raw mode, so we need to do line input... */
264 /* check for new keyboard input */
265 while (CurOfs
== bios
->FirstKbdCharPtr
) {
266 /* no input available yet, so wait... */
269 /* read from keyboard queue (call int16?) */
270 data
= ((WORD
*)bios
)[CurOfs
];
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
);
280 else if (LOBYTE(data
) >= ' ') {
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
)) {
292 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), "\b \b", 3, &w
, NULL
);
298 /* save rest of line for later */
299 lol
->offs_unread_CON
= linebuffer
- (BYTE
*)lol
+ count
;
302 memcpy(buffer
, linebuffer
, len
);
304 hdr
->status
= STAT_DONE
;
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
) {
317 hdr
->status
= STAT_DONE
|STAT_BUSY
;
319 /* some keyboard input waiting */
320 hdr
->status
= STAT_DONE
;
321 ((REQ_SAFEINPUT
*)hdr
)->data
= ((BYTE
*)bios
)[CurOfs
];
325 hdr
->status
= STAT_DONE
|STAT_BUSY
;
331 hdr
->status
= STAT_DONE
;
333 else if (con
->attr
& ATTR_RAW
) {
334 if (CurOfs
== bios
->FirstKbdCharPtr
) {
336 hdr
->status
= STAT_DONE
|STAT_BUSY
;
338 /* some keyboard input waiting */
339 hdr
->status
= STAT_DONE
;
343 hdr
->status
= STAT_DONE
|STAT_BUSY
;
348 /* flush line and keyboard queue */
349 lol
->offs_unread_CON
= 0;
350 bios
->NextKbdCharPtr
= bios
->FirstKbdCharPtr
;
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
));
360 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), buffer
, io
->count
, &result
, NULL
);
362 hdr
->status
= STAT_DONE
;
366 hdr
->status
= STAT_DONE
;
371 static void InitListOfLists(DOS_LISTOFLISTS
*DOS_LOL
)
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);
432 thunk
->strategy
= DPMI_AllocInternalRMCB(devinfo
->strategy
);
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
;
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],
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
],
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 DWORD
DOSDEV_Console(void)
481 return DOSMEM_LOL()->ptr_CON_dev_hdr
;
484 DWORD
DOSDEV_FindCharDevice(char*name
)
486 SEGPTR cur_ptr
= MAKESEGPTR(HIWORD(DOS_LOLSeg
), FIELD_OFFSET(DOS_LISTOFLISTS
,NUL_dev
));
487 DOS_DEVICE_HEADER
*cur
= PTR_REAL_TO_LIN(SELECTOROF(cur_ptr
),OFFSETOF(cur_ptr
));
491 /* get first 8 characters */
492 /* if less than 8 characters, pad with spaces */
493 for (cnt
=0; name
[cnt
] && cnt
<8; cnt
++)
494 dname
[cnt
]=name
[cnt
];
496 while(cnt
<8) dname
[cnt
++] = ' ';
498 /* search for char devices with the right name */
500 ((!(cur
->attr
& ATTR_CHAR
)) ||
501 memcmp(cur
->name
,dname
,8))) {
502 cur_ptr
= cur
->next_dev
;
503 if (cur_ptr
== NONEXT
) cur
=NULL
;
504 else cur
= PTR_REAL_TO_LIN(SELECTOROF(cur_ptr
),OFFSETOF(cur_ptr
));
509 static void DOSDEV_DoReq(void*req
, DWORD dev
)
511 REQUEST_HEADER
*hdr
= (REQUEST_HEADER
*)req
;
512 DOS_DEVICE_HEADER
*dhdr
;
516 dhdr
= PTR_REAL_TO_LIN(SELECTOROF(dev
),OFFSETOF(dev
));
517 phdr
= ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req
);
519 /* copy request to request scratch area */
520 memcpy(phdr
, req
, hdr
->size
);
522 /* prepare to call device driver */
523 memset(&ctx
, 0, sizeof(ctx
));
524 ctx
.EFlags
|= V86_FLAG
;
526 /* ES:BX points to request for strategy routine */
527 ctx
.SegEs
= HIWORD(DOS_LOLSeg
);
528 ctx
.Ebx
= DOS_DATASEG_OFF(req
);
530 /* call strategy routine */
531 ctx
.SegCs
= SELECTOROF(dev
);
532 ctx
.Eip
= dhdr
->strategy
;
533 DPMI_CallRMProc(&ctx
, 0, 0, 0);
535 /* call interrupt routine */
536 ctx
.SegCs
= SELECTOROF(dev
);
537 ctx
.Eip
= dhdr
->interrupt
;
538 DPMI_CallRMProc(&ctx
, 0, 0, 0);
540 /* completed, copy request back */
541 memcpy(req
, phdr
, hdr
->size
);
543 if (hdr
->status
& STAT_ERROR
) {
544 switch (hdr
->status
& STAT_MASK
) {
545 case 0x0F: /* invalid disk change */
546 /* this error seems to fit the bill */
547 SetLastError(ERROR_NOT_SAME_DEVICE
);
550 SetLastError((hdr
->status
& STAT_MASK
) + 0x13);
556 static int DOSDEV_IO(unsigned cmd
, DWORD dev
, DWORD buf
, int buflen
)
560 req
.hdr
.size
=sizeof(req
);
561 req
.hdr
.unit
=0; /* not dealing with block devices yet */
563 req
.hdr
.status
=STAT_BUSY
;
564 req
.media
=0; /* not dealing with block devices yet */
567 req
.sector
=0; /* block devices */
568 req
.volume
=0; /* block devices */
570 DOSDEV_DoReq(&req
, dev
);
575 int DOSDEV_Peek(DWORD dev
, BYTE
*data
)
579 req
.hdr
.size
=sizeof(req
);
580 req
.hdr
.unit
=0; /* not dealing with block devices yet */
581 req
.hdr
.command
=CMD_SAFEINPUT
;
582 req
.hdr
.status
=STAT_BUSY
;
585 DOSDEV_DoReq(&req
, dev
);
587 if (req
.hdr
.status
& STAT_BUSY
) return 0;
593 int DOSDEV_Read(DWORD dev
, DWORD buf
, int buflen
)
595 return DOSDEV_IO(CMD_INPUT
, dev
, buf
, buflen
);
598 int DOSDEV_Write(DWORD dev
, DWORD buf
, int buflen
, int verify
)
600 return DOSDEV_IO(verify
?CMD_SAFEOUTPUT
:CMD_OUTPUT
, dev
, buf
, buflen
);
603 int DOSDEV_IoctlRead(DWORD dev
, DWORD buf
, int buflen
)
605 return DOSDEV_IO(CMD_INIOCTL
, dev
, buf
, buflen
);
608 int DOSDEV_IoctlWrite(DWORD dev
, DWORD buf
, int buflen
)
610 return DOSDEV_IO(CMD_OUTIOCTL
, dev
, buf
, buflen
);
613 void DOSDEV_SetSharingRetry(WORD delay
, WORD count
)
615 DOSMEM_LOL()->sharing_retry_delay
= delay
;
616 if (count
) DOSMEM_LOL()->sharing_retry_count
= count
;
619 SEGPTR
DOSDEV_GetLOL(BOOL v86
)
621 if (v86
) return DOSMEM_LOL()->wine_rm_lol
;
622 else return DOSMEM_LOL()->wine_pm_lol
;