dosdev.c 14.4 KB
Newer Older
1 2 3
/*
 * DOS devices
 *
4
 * Copyright 1999 Ove Kåven
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
 */

#include <stdlib.h>
#include <string.h>
#include "wine/winbase16.h"
24
#include "dosexe.h"
25
#include "wine/debug.h"
26 27 28

#include "pshpack1.h"

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/* Warning: need to return LOL ptr w/ offset 0 (&ptr_first_DPB) to programs ! */
typedef struct _DOS_LISTOFLISTS
{
    WORD  CX_Int21_5e01;        /* -24d contents of CX from INT 21/AX=5E01h */
    WORD  LRU_count_FCB_cache;  /* -22d */
    WORD  LRU_count_FCB_open;   /* -20d */
    DWORD OEM_func_handler;     /* -18d OEM function of INT 21/AH=F8h */
    WORD  INT21_offset;         /* -14d offset in DOS CS of code to return from INT 21 call */
    WORD  sharing_retry_count;  /* -12d */
    WORD  sharing_retry_delay;  /* -10d */
    DWORD ptr_disk_buf;         /* -8d ptr to current disk buf */
    WORD  offs_unread_CON;      /* -4d pointer in DOS data segment of unread CON input */
    WORD  seg_first_MCB;        /* -2d */
    DWORD ptr_first_DPB;        /* 00 */
    DWORD ptr_first_SysFileTable; /* 04 */
    DWORD ptr_clock_dev_hdr;    /* 08 */
    DWORD ptr_CON_dev_hdr;      /* 0C */
    WORD  max_byte_per_sec;     /* 10 maximum bytes per sector of any block device */
    DWORD ptr_disk_buf_info;    /* 12 */
    DWORD ptr_array_CDS;        /* 16 current directory structure */
    DWORD ptr_sys_FCB;          /* 1A */
    WORD  nr_protect_FCB;       /* 1E */
    BYTE  nr_block_dev;         /* 20 */
    BYTE  nr_avail_drive_letters; /* 21 */
    DOS_DEVICE_HEADER NUL_dev;  /* 22 */
    BYTE  nr_drives_JOINed;     /* 34 */
    WORD  ptr_spec_prg_names;   /* 35 */
    DWORD ptr_SETVER_prg_list;  /* 37 */
    WORD DOS_HIGH_A20_func_offs;/* 3B */
    WORD PSP_last_exec;         /* 3D if DOS in HMA: PSP of program executed last; if DOS low: 0000h */
    WORD BUFFERS_val;           /* 3F */
    WORD BUFFERS_nr_lookahead;  /* 41 */
    BYTE boot_drive;            /* 43 */
    BYTE flag_DWORD_moves;      /* 44 01h for 386+, 00h otherwise */
    WORD size_extended_mem;     /* 45 size of extended mem in KB */
    SEGPTR wine_rm_lol;         /* -- wine: Real mode pointer to LOL */
    SEGPTR wine_pm_lol;         /* -- wine: Protected mode pointer to LOL */
} DOS_LISTOFLISTS;

68 69
#include "poppack.h"

70
#define CON_BUFFER 128
71

72 73 74
enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };

static void *strategy_data[NB_SYSTEM_STRATEGIES];
75 76

#define LJMP 0xea
77 78


79
/* prototypes */
80 81 82 83
static void WINAPI nul_strategy(CONTEXT*ctx);
static void WINAPI nul_interrupt(CONTEXT*ctx);
static void WINAPI con_strategy(CONTEXT*ctx);
static void WINAPI con_interrupt(CONTEXT*ctx);
84

85
/* devices */
86
static const WINEDEV devs[] =
87 88 89 90
{
  { "NUL     ",
    ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
    nul_strategy, nul_interrupt },
91

92 93 94
  { "CON     ",
    ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
    con_strategy, con_interrupt }
95 96
};

97 98 99 100
#define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))

/* DOS data segment */
typedef struct
101
{
102 103
    DOS_LISTOFLISTS    lol;
    DOS_DEVICE_HEADER  dev[NR_DEVS-1];
104
    DOS_DEVICE_HEADER *last_dev; /* ptr to last registered device driver */
105 106 107 108 109 110 111 112
    WINEDEV_THUNK      thunk[NR_DEVS];
    REQ_IO             req;
    BYTE               buffer[CON_BUFFER];

} DOS_DATASEG;

#define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)

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 127 128
  ctx->Eip   = *(stack++);
  ctx->SegCs = *(stack++);
  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 141 142 143 144 145
  }
  *hdr_ptr = hdr;
  do_lret(ctx);
}

static REQUEST_HEADER * get_hdr(int id, void**extra)
{
146
  void **hdr_ptr = strategy_data[id];
147
  if (extra)
148 149
    *extra = hdr_ptr ? (void*)(hdr_ptr+1) : NULL;
  return hdr_ptr ? *hdr_ptr : NULL;
150 151
}

152
static void WINAPI nul_strategy(CONTEXT*ctx)
153 154 155 156
{
  do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
}

157
static void WINAPI nul_interrupt(CONTEXT*ctx)
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
{
  REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
  /* eat everything and recycle nothing */
  switch (hdr->command) {
  case CMD_INPUT:
    ((REQ_IO*)hdr)->count = 0;
    hdr->status = STAT_DONE;
    break;
  case CMD_SAFEINPUT:
    hdr->status = STAT_DONE|STAT_BUSY;
    break;
  default:
    hdr->status = STAT_DONE;
  }
  do_lret(ctx);
}

175
static void WINAPI con_strategy(CONTEXT*ctx)
176 177 178 179
{
  do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
}

180
static void WINAPI con_interrupt(CONTEXT*ctx)
181 182 183
{
  int *scan;
  REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
184
  BIOSDATA *bios = DOSVM_BiosData();
185 186
  WORD CurOfs = bios->NextKbdCharPtr;
  DOS_LISTOFLISTS *lol = DOSMEM_LOL();
187 188
  DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
  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 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

  switch (hdr->command) {
  case CMD_INPUT:
    {
      REQ_IO *io = (REQ_IO *)hdr;
      WORD count = io->count, len = 0;
      BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
					SELECTOROF(io->buffer),
					(DWORD)OFFSETOF(io->buffer));

      hdr->status = STAT_BUSY;
      /* first, check whether we already have data in line buffer */
      if (curbuffer) {
	/* yep, copy as much as we can */
	BYTE data = 0;
	while ((len<count) && (data != '\r')) {
	  data = *curbuffer++;
	  buffer[len++] = data;
	}
	if (data == '\r') {
	  /* line buffer emptied */
	  lol->offs_unread_CON = 0;
	  curbuffer = NULL;
Andreas Mohr's avatar
Andreas Mohr committed
216
	  /* if we're not in raw mode, call it a day */
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	  if (!(con->attr & ATTR_RAW)) {
	    hdr->status = STAT_DONE;
	    io->count = len;
	    break;
	  }
	} else {
	  /* still some data left */
	  lol->offs_unread_CON = curbuffer - (BYTE*)lol;
	  /* but buffer was filled, we're done */
	  hdr->status = STAT_DONE;
	  io->count = len;
	  break;
	}
      }

      /* if we're in raw mode, we just need to fill the buffer */
      if (con->attr & ATTR_RAW) {
	while (len<count) {
	  WORD data;

	  /* do we have a waiting scancode? */
	  if (*scan) {
	    /* yes, store scancode in buffer */
	    buffer[len++] = *scan;
	    *scan = 0;
	    if (len==count) break;
	  }

	  /* check for new keyboard input */
	  while (CurOfs == bios->FirstKbdCharPtr) {
	    /* no input available yet, so wait... */
248
	    DOSVM_Wait( ctx );
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
	  }
	  /* read from keyboard queue (call int16?) */
	  data = ((WORD*)bios)[CurOfs];
	  CurOfs += 2;
	  if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
	  bios->NextKbdCharPtr = CurOfs;
	  /* if it's an extended key, save scancode */
	  if (LOBYTE(data) == 0) *scan = HIBYTE(data);
	  /* store ASCII char in buffer */
	  buffer[len++] = LOBYTE(data);
	}
      } else {
	/* we're not in raw mode, so we need to do line input... */
	while (TRUE) {
	  WORD data;
	  /* check for new keyboard input */
	  while (CurOfs == bios->FirstKbdCharPtr) {
	    /* no input available yet, so wait... */
267
	    DOSVM_Wait( ctx );
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	  }
	  /* read from keyboard queue (call int16?) */
	  data = ((WORD*)bios)[CurOfs];
	  CurOfs += 2;
	  if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
	  bios->NextKbdCharPtr = CurOfs;

	  if (LOBYTE(data) == '\r') {
	    /* it's the return key, we're done */
	    linebuffer[len++] = LOBYTE(data);
	    break;
	  }
	  else if (LOBYTE(data) >= ' ') {
	    /* a character */
	    if ((len+1)<CON_BUFFER) {
	      linebuffer[len] = LOBYTE(data);
284
	      WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, &w, NULL);
285 286 287 288 289 290 291
	    }
	    /* else beep, but I don't like noise */
	  }
	  else switch (LOBYTE(data)) {
	  case '\b':
	    if (len>0) {
	      len--;
292
	      WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, &w, NULL);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	    }
	    break;
	  }
	}
	if (len > count) {
	  /* save rest of line for later */
	  lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
	  len = count;
	}
	memcpy(buffer, linebuffer, len);
      }
      hdr->status = STAT_DONE;
      io->count = len;
    }
    break;
  case CMD_SAFEINPUT:
    if (curbuffer) {
      /* some line input waiting */
      hdr->status = STAT_DONE;
      ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
    }
    else if (con->attr & ATTR_RAW) {
      if (CurOfs == bios->FirstKbdCharPtr) {
	/* no input */
	hdr->status = STAT_DONE|STAT_BUSY;
      } else {
	/* some keyboard input waiting */
	hdr->status = STAT_DONE;
	((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
      }
    } else {
      /* no line input */
      hdr->status = STAT_DONE|STAT_BUSY;
    }
    break;
  case CMD_INSTATUS:
    if (curbuffer) {
      /* we have data */
      hdr->status = STAT_DONE;
    }
    else if (con->attr & ATTR_RAW) {
      if (CurOfs == bios->FirstKbdCharPtr) {
	/* no input */
	hdr->status = STAT_DONE|STAT_BUSY;
      } else {
	/* some keyboard input waiting */
	hdr->status = STAT_DONE;
      }
    } else {
      /* no line input */
      hdr->status = STAT_DONE|STAT_BUSY;
    }

    break;
  case CMD_INFLUSH:
    /* flush line and keyboard queue */
    lol->offs_unread_CON = 0;
    bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
    break;
  case CMD_OUTPUT:
  case CMD_SAFEOUTPUT:
    {
      REQ_IO *io = (REQ_IO *)hdr;
      BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
					SELECTOROF(io->buffer),
					(DWORD)OFFSETOF(io->buffer));
      DWORD result = 0;
360
      WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
361 362 363 364 365 366 367 368 369 370 371
      io->count = result;
      hdr->status = STAT_DONE;
    }
    break;
  default:
    hdr->status = STAT_DONE;
  }
  do_lret(ctx);
}

static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
{
/*
Output of DOS 6.22:

0133:0020                    6A 13-33 01 CC 00 33 01 59 00         j.3...3.Y.
0133:0030  70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05   p...r...m.3.....
0133:0040  00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D   .........!......
0133:0050  CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00   ..NUL     ......
0133:0060  00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF   .K...........p..
0133:0070  FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00   ................
0133:0080  00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02   ..............p.
0133:0090  D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD   .D...D...D...D..
0133:00A0  D0 44 C8 FD D0 44                                 .D...D
*/
  DOS_LOL->CX_Int21_5e01		= 0x0;
  DOS_LOL->LRU_count_FCB_cache	= 0x0;
  DOS_LOL->LRU_count_FCB_open		= 0x0;
  DOS_LOL->OEM_func_handler		= -1; /* not available */
  DOS_LOL->INT21_offset		= 0x0;
  DOS_LOL->sharing_retry_count	= 3;
  DOS_LOL->sharing_retry_delay	= 1;
  DOS_LOL->ptr_disk_buf		= 0x0;
  DOS_LOL->offs_unread_CON		= 0x0;
  DOS_LOL->seg_first_MCB		= 0x0;
  DOS_LOL->ptr_first_DPB		= 0x0;
  DOS_LOL->ptr_first_SysFileTable	= 0x0;
  DOS_LOL->ptr_clock_dev_hdr		= 0x0;
  DOS_LOL->ptr_CON_dev_hdr		= 0x0;
  DOS_LOL->max_byte_per_sec		= 512;
  DOS_LOL->ptr_disk_buf_info		= 0x0;
  DOS_LOL->ptr_array_CDS		= 0x0;
  DOS_LOL->ptr_sys_FCB		= 0x0;
  DOS_LOL->nr_protect_FCB		= 0x0;
  DOS_LOL->nr_block_dev		= 0x0;
  DOS_LOL->nr_avail_drive_letters	= 26; /* A - Z */
  DOS_LOL->nr_drives_JOINed		= 0x0;
  DOS_LOL->ptr_spec_prg_names		= 0x0;
  DOS_LOL->ptr_SETVER_prg_list	= 0x0; /* no SETVER list */
  DOS_LOL->DOS_HIGH_A20_func_offs	= 0x0;
  DOS_LOL->PSP_last_exec		= 0x0;
  DOS_LOL->BUFFERS_val		= 99; /* maximum: 99 */
  DOS_LOL->BUFFERS_nr_lookahead	= 8; /* maximum: 8 */
  DOS_LOL->boot_drive			= 3; /* C: */
  DOS_LOL->flag_DWORD_moves		= 0x01; /* i386+ */
  DOS_LOL->size_extended_mem		= 0xf000; /* very high value */
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
void DOSDEV_SetupDevice(const WINEDEV * devinfo,
			WORD seg, WORD off_dev, WORD off_thunk)
{
  DOS_DEVICE_HEADER *dev = PTR_REAL_TO_LIN(seg, off_dev);
  WINEDEV_THUNK *thunk = PTR_REAL_TO_LIN(seg, off_thunk);
  DOS_DATASEG *dataseg = (DOS_DATASEG*)DOSMEM_LOL();

  dev->attr = devinfo->attr;
  dev->strategy  = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp1);
  dev->interrupt = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp2);
  memcpy(dev->name, devinfo->name, 8);

  thunk->ljmp1     = LJMP;
  thunk->strategy  = DPMI_AllocInternalRMCB(devinfo->strategy);
  thunk->ljmp2     = LJMP;
  thunk->interrupt = DPMI_AllocInternalRMCB(devinfo->interrupt);

  dev->next_dev = NONEXT;
  if (dataseg->last_dev)
      dataseg->last_dev->next_dev = MAKESEGPTR(seg, off_dev);
  dataseg->last_dev = dev;
}

442 443
void DOSDEV_InstallDOSDevices(void)
{
444
  DOS_DATASEG *dataseg;
445 446
  WORD seg;
  WORD selector;
447
  unsigned int n;
448 449

  /* allocate DOS data segment or something */
450 451 452 453 454 455 456
  dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );

  DOS_LOLSeg = MAKESEGPTR( seg, 0 );
  DOSMEM_LOL()->wine_rm_lol = 
      MAKESEGPTR( seg, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
  DOSMEM_LOL()->wine_pm_lol = 
      MAKESEGPTR( selector, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
457

458
  /* initialize the magnificent List Of Lists */
459 460 461
  InitListOfLists(&dataseg->lol);

  /* Set up first device (NUL) */
462 463 464 465 466
  dataseg->last_dev = NULL;
  DOSDEV_SetupDevice( &devs[0],
		      seg,
		      DOS_DATASEG_OFF(lol.NUL_dev),
		      DOS_DATASEG_OFF(thunk[0]) );
467 468 469

  /* Set up the remaining devices */
  for (n = 1; n < NR_DEVS; n++)
470 471 472 473
    DOSDEV_SetupDevice( &devs[n],
			seg,
			DOS_DATASEG_OFF(dev[n-1]),
			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 480 481 482 483 484 485 486 487 488 489
void DOSDEV_SetSharingRetry(WORD delay, WORD count)
{
    DOSMEM_LOL()->sharing_retry_delay = delay;
    if (count) DOSMEM_LOL()->sharing_retry_count = count;
}

SEGPTR DOSDEV_GetLOL(BOOL v86)
{
    if (v86) return DOSMEM_LOL()->wine_rm_lol;
    else return DOSMEM_LOL()->wine_pm_lol;
}