4 * Copyright 1999 Ove Kåven
9 #include "wine/winbase16.h"
13 #include "debugtools.h"
25 BYTE size
; /* length of header + data */
26 BYTE unit
; /* unit (block devices only) */
34 BYTE media
; /* media descriptor from BPB */
36 WORD count
; /* byte/sector count */
37 WORD sector
; /* starting sector (block devices) */
38 DWORD volume
; /* volume ID (block devices) */
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
71 #define CMD_MEDIACHECK 1 /* block devices */
72 #define CMD_BUILDBPB 2 /* block devices */
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
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
);
111 static WINEDEV devs
[] =
114 ATTR_CHAR
|ATTR_NUL
|ATTR_DEVICE
,
115 nul_strategy
, nul_interrupt
},
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 */
128 DOS_DEVICE_HEADER dev
[NR_DEVS
-1];
129 WINEDEV_THUNK thunk
[NR_DEVS
];
131 BYTE buffer
[CON_BUFFER
];
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
];
160 hdr_ptr
= calloc(1,sizeof(void *)+extra
);
161 strategy_data
[id
] = hdr_ptr
;
167 static REQUEST_HEADER
* get_hdr(int id
, void**extra
)
169 void **hdr_ptr
= strategy_data
[id
];
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
) {
186 ((REQ_IO
*)hdr
)->count
= 0;
187 hdr
->status
= STAT_DONE
;
190 hdr
->status
= STAT_DONE
|STAT_BUSY
;
193 hdr
->status
= STAT_DONE
;
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
)
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
) {
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 */
228 /* yep, copy as much as we can */
230 while ((len
<count
) && (data
!= '\r')) {
232 buffer
[len
++] = data
;
235 /* line buffer emptied */
236 lol
->offs_unread_CON
= 0;
238 /* if we're not in raw mode, call it a day*/
239 if (!(con
->attr
& ATTR_RAW
)) {
240 hdr
->status
= STAT_DONE
;
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
;
254 /* if we're in raw mode, we just need to fill the buffer */
255 if (con
->attr
& ATTR_RAW
) {
259 /* do we have a waiting scancode? */
261 /* yes, store scancode in buffer */
262 buffer
[len
++] = *scan
;
264 if (len
==count
) break;
267 /* check for new keyboard input */
268 while (CurOfs
== bios
->FirstKbdCharPtr
) {
269 /* no input available yet, so wait... */
272 /* read from keyboard queue (call int16?) */
273 data
= ((WORD
*)bios
)[CurOfs
];
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
);
283 /* we're not in raw mode, so we need to do line input... */
286 /* check for new keyboard input */
287 while (CurOfs
== bios
->FirstKbdCharPtr
) {
288 /* no input available yet, so wait... */
291 /* read from keyboard queue (call int16?) */
292 data
= ((WORD
*)bios
)[CurOfs
];
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
);
302 else if (LOBYTE(data
) >= ' ') {
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
)) {
314 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), "\b \b", 3, NULL
, NULL
);
320 /* save rest of line for later */
321 lol
->offs_unread_CON
= linebuffer
- (BYTE
*)lol
+ count
;
324 memcpy(buffer
, linebuffer
, len
);
326 hdr
->status
= STAT_DONE
;
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
) {
339 hdr
->status
= STAT_DONE
|STAT_BUSY
;
341 /* some keyboard input waiting */
342 hdr
->status
= STAT_DONE
;
343 ((REQ_SAFEINPUT
*)hdr
)->data
= ((BYTE
*)bios
)[CurOfs
];
347 hdr
->status
= STAT_DONE
|STAT_BUSY
;
353 hdr
->status
= STAT_DONE
;
355 else if (con
->attr
& ATTR_RAW
) {
356 if (CurOfs
== bios
->FirstKbdCharPtr
) {
358 hdr
->status
= STAT_DONE
|STAT_BUSY
;
360 /* some keyboard input waiting */
361 hdr
->status
= STAT_DONE
;
365 hdr
->status
= STAT_DONE
|STAT_BUSY
;
370 /* flush line and keyboard queue */
371 lol
->offs_unread_CON
= 0;
372 bios
->NextKbdCharPtr
= bios
->FirstKbdCharPtr
;
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
));
382 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), buffer
, io
->count
, &result
, NULL
);
384 hdr
->status
= STAT_DONE
;
388 hdr
->status
= STAT_DONE
;
393 static void InitListOfLists(DOS_LISTOFLISTS
*DOS_LOL
)
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
;
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);
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
);
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 */
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
);
515 static void DOSDEV_DoReq(void*req
, DWORD dev
)
517 REQUEST_HEADER
*hdr
= (REQUEST_HEADER
*)req
;
518 DOS_DEVICE_HEADER
*dhdr
;
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
);
555 SetLastError((hdr
->status
& STAT_MASK
) + 0x13);
561 static int DOSDEV_IO(unsigned cmd
, DWORD dev
, DWORD buf
, int buflen
)
565 req
.hdr
.size
=sizeof(req
);
566 req
.hdr
.unit
=0; /* not dealing with block devices yet */
568 req
.hdr
.status
=STAT_BUSY
;
569 req
.media
=0; /* not dealing with block devices yet */
572 req
.sector
=0; /* block devices */
573 req
.volume
=0; /* block devices */
575 DOSDEV_DoReq(&req
, dev
);
580 int DOSDEV_Peek(DWORD dev
, BYTE
*data
)
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
;
590 DOSDEV_DoReq(&req
, dev
);
592 if (req
.hdr
.status
& STAT_BUSY
) return 0;
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
);