|  | /* | 
|  | * DOS devices | 
|  | * | 
|  | * Copyright 1999 Ove Kåven | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include "wine/winbase16.h" | 
|  | #include "msdos.h" | 
|  | #include "miscemu.h" | 
|  | #include "dosexe.h" | 
|  | #include "debugtools.h" | 
|  |  | 
|  | #include "pshpack1.h" | 
|  |  | 
|  | typedef struct { | 
|  | BYTE ljmp1; | 
|  | RMCBPROC strategy; | 
|  | BYTE ljmp2; | 
|  | RMCBPROC interrupt; | 
|  | } WINEDEV_THUNK; | 
|  |  | 
|  | typedef struct { | 
|  | BYTE size; /* length of header + data */ | 
|  | BYTE unit; /* unit (block devices only) */ | 
|  | BYTE command; | 
|  | WORD status; | 
|  | BYTE reserved[8]; | 
|  | } REQUEST_HEADER; | 
|  |  | 
|  | typedef struct { | 
|  | REQUEST_HEADER hdr; | 
|  | BYTE media; /* media descriptor from BPB */ | 
|  | SEGPTR buffer; | 
|  | WORD count; /* byte/sector count */ | 
|  | WORD sector; /* starting sector (block devices) */ | 
|  | DWORD volume; /* volume ID (block devices) */ | 
|  | } REQ_IO; | 
|  |  | 
|  | typedef struct { | 
|  | REQUEST_HEADER hdr; | 
|  | BYTE data; | 
|  | } REQ_SAFEINPUT; | 
|  |  | 
|  | #include "poppack.h" | 
|  |  | 
|  | #define CON_BUFFER 128 | 
|  |  | 
|  | #define SYSTEM_STRATEGY_NUL 0x0100 | 
|  | #define SYSTEM_STRATEGY_CON 0x0101 | 
|  |  | 
|  | #define NONEXT ((DWORD)-1) | 
|  |  | 
|  | #define ATTR_STDIN     0x0001 | 
|  | #define ATTR_STDOUT    0x0002 | 
|  | #define ATTR_NUL       0x0004 | 
|  | #define ATTR_CLOCK     0x0008 | 
|  | #define ATTR_FASTCON   0x0010 | 
|  | #define ATTR_RAW       0x0020 | 
|  | #define ATTR_NOTEOF    0x0040 | 
|  | #define ATTR_DEVICE    0x0080 | 
|  | #define ATTR_REMOVABLE 0x0800 | 
|  | #define ATTR_NONIBM    0x2000 /* block devices */ | 
|  | #define ATTR_UNTILBUSY 0x2000 /* char devices */ | 
|  | #define ATTR_IOCTL     0x4000 | 
|  | #define ATTR_CHAR      0x8000 | 
|  |  | 
|  | #define CMD_INIT       0 | 
|  | #define CMD_MEDIACHECK 1 /* block devices */ | 
|  | #define CMD_BUILDBPB   2 /* block devices */ | 
|  | #define CMD_INIOCTL    3 | 
|  | #define CMD_INPUT      4 /* read data */ | 
|  | #define CMD_SAFEINPUT  5 /* "non-destructive input no wait", char devices */ | 
|  | #define CMD_INSTATUS   6 /* char devices */ | 
|  | #define CMD_INFLUSH    7 /* char devices */ | 
|  | #define CMD_OUTPUT     8 /* write data */ | 
|  | #define CMD_SAFEOUTPUT 9 /* write data with verify */ | 
|  | #define CMD_OUTSTATUS 10 /* char devices */ | 
|  | #define CMD_OUTFLUSH  11 /* char devices */ | 
|  | #define CMD_OUTIOCTL  12 | 
|  | #define CMD_DEVOPEN   13 | 
|  | #define CMD_DEVCLOSE  14 | 
|  | #define CMD_REMOVABLE 15 /* block devices */ | 
|  | #define CMD_UNTILBUSY 16 /* output until busy */ | 
|  |  | 
|  | #define STAT_MASK  0x00FF | 
|  | #define STAT_DONE  0x0100 | 
|  | #define STAT_BUSY  0x0200 | 
|  | #define STAT_ERROR 0x8000 | 
|  |  | 
|  | #define LJMP 0xea | 
|  |  | 
|  |  | 
|  | /* prototypes */ | 
|  | static void WINAPI nul_strategy(CONTEXT86*ctx); | 
|  | static void WINAPI nul_interrupt(CONTEXT86*ctx); | 
|  | static void WINAPI con_strategy(CONTEXT86*ctx); | 
|  | static void WINAPI con_interrupt(CONTEXT86*ctx); | 
|  |  | 
|  | /* devices */ | 
|  | typedef struct | 
|  | { | 
|  | char name[8]; | 
|  | WORD attr; | 
|  | RMCBPROC strategy; | 
|  | RMCBPROC interrupt; | 
|  |  | 
|  | } WINEDEV; | 
|  |  | 
|  | static WINEDEV devs[] = | 
|  | { | 
|  | { "NUL     ", | 
|  | ATTR_CHAR|ATTR_NUL|ATTR_DEVICE, | 
|  | nul_strategy, nul_interrupt }, | 
|  |  | 
|  | { "CON     ", | 
|  | ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE, | 
|  | con_strategy, con_interrupt } | 
|  | }; | 
|  |  | 
|  | #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV)) | 
|  |  | 
|  | /* DOS data segment */ | 
|  | typedef struct | 
|  | { | 
|  | DOS_LISTOFLISTS    lol; | 
|  | DOS_DEVICE_HEADER  dev[NR_DEVS-1]; | 
|  | WINEDEV_THUNK      thunk[NR_DEVS]; | 
|  | REQ_IO             req; | 
|  | BYTE               buffer[CON_BUFFER]; | 
|  |  | 
|  | } DOS_DATASEG; | 
|  |  | 
|  | #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx) | 
|  |  | 
|  | DWORD DOS_LOLSeg; | 
|  |  | 
|  |  | 
|  | /* the device implementations */ | 
|  | static void do_lret(CONTEXT86*ctx) | 
|  | { | 
|  | WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, SS_reg(ctx), ESP_reg(ctx)); | 
|  |  | 
|  | EIP_reg(ctx) = *(stack++); | 
|  | CS_reg(ctx)  = *(stack++); | 
|  | ESP_reg(ctx) += 2*sizeof(WORD); | 
|  | } | 
|  |  | 
|  | static void do_strategy(CONTEXT86*ctx, int id, int extra) | 
|  | { | 
|  | REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ES_reg(ctx), EBX_reg(ctx)); | 
|  | void **hdr_ptr = DOSVM_GetSystemData(id); | 
|  |  | 
|  | if (!hdr_ptr) { | 
|  | hdr_ptr = calloc(1,sizeof(void *)+extra); | 
|  | DOSVM_SetSystemData(id, hdr_ptr); | 
|  | } | 
|  |  | 
|  | *hdr_ptr = hdr; | 
|  | do_lret(ctx); | 
|  | } | 
|  |  | 
|  | static REQUEST_HEADER * get_hdr(int id, void**extra) | 
|  | { | 
|  | void **hdr_ptr = DOSVM_GetSystemData(id); | 
|  | if (extra) | 
|  | *extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL; | 
|  | return hdr_ptr ? *hdr_ptr : (void *)NULL; | 
|  | } | 
|  |  | 
|  | static void WINAPI nul_strategy(CONTEXT86*ctx) | 
|  | { | 
|  | do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0); | 
|  | } | 
|  |  | 
|  | static void WINAPI nul_interrupt(CONTEXT86*ctx) | 
|  | { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | static void WINAPI con_strategy(CONTEXT86*ctx) | 
|  | { | 
|  | do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int)); | 
|  | } | 
|  |  | 
|  | static void WINAPI con_interrupt(CONTEXT86*ctx) | 
|  | { | 
|  | int *scan; | 
|  | REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan); | 
|  | BIOSDATA *bios = DOSMEM_BiosData(); | 
|  | WORD CurOfs = bios->NextKbdCharPtr; | 
|  | DOS_LISTOFLISTS *lol = DOSMEM_LOL(); | 
|  | DOS_DATASEG *dataseg = (DOS_DATASEG *)lol; | 
|  | BYTE *linebuffer = dataseg->buffer; | 
|  | BYTE *curbuffer = (lol->offs_unread_CON) ? | 
|  | (((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL; | 
|  | DOS_DEVICE_HEADER *con = dataseg->dev; | 
|  | LPDOSTASK lpDosTask = MZ_Current(); | 
|  |  | 
|  | 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; | 
|  | /* if we're not in raw mode, call it a day*/ | 
|  | 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... */ | 
|  | DOSVM_Wait( -1, 0 ); | 
|  | } | 
|  | /* 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... */ | 
|  | DOSVM_Wait( -1, 0 ); | 
|  | } | 
|  | /* 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); | 
|  | WriteFile(lpDosTask->hConOutput, &linebuffer[len++], 1, NULL, NULL); | 
|  | } | 
|  | /* else beep, but I don't like noise */ | 
|  | } | 
|  | else switch (LOBYTE(data)) { | 
|  | case '\b': | 
|  | if (len>0) { | 
|  | len--; | 
|  | WriteFile(lpDosTask->hConOutput, "\b \b", 3, NULL, NULL); | 
|  | } | 
|  | 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; | 
|  | WriteFile(lpDosTask->hConOutput, buffer, io->count, &result, NULL); | 
|  | io->count = result; | 
|  | hdr->status = STAT_DONE; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | hdr->status = STAT_DONE; | 
|  | } | 
|  | do_lret(ctx); | 
|  | } | 
|  |  | 
|  | static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL) | 
|  | { | 
|  | /* | 
|  | 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 */ | 
|  | } | 
|  |  | 
|  | void DOSDEV_InstallDOSDevices(void) | 
|  | { | 
|  | DOS_DATASEG *dataseg; | 
|  | UINT16 seg; | 
|  | int n; | 
|  |  | 
|  | /* allocate DOS data segment or something */ | 
|  | DOS_LOLSeg = GlobalDOSAlloc16(sizeof(DOS_DATASEG)); | 
|  | seg = HIWORD(DOS_LOLSeg); | 
|  | dataseg = PTR_SEG_OFF_TO_LIN(LOWORD(DOS_LOLSeg), 0); | 
|  |  | 
|  | /* initialize the magnificent List Of Lists */ | 
|  | InitListOfLists(&dataseg->lol); | 
|  |  | 
|  | /* Set up first device (NUL) */ | 
|  | dataseg->lol.NUL_dev.next_dev  = PTR_SEG_OFF_TO_SEGPTR(seg, DOS_DATASEG_OFF(dev[0])); | 
|  | dataseg->lol.NUL_dev.attr      = devs[0].attr; | 
|  | dataseg->lol.NUL_dev.strategy  = DOS_DATASEG_OFF(thunk[0].ljmp1); | 
|  | dataseg->lol.NUL_dev.interrupt = DOS_DATASEG_OFF(thunk[0].ljmp2); | 
|  | memcpy(dataseg->lol.NUL_dev.name, devs[0].name, 8); | 
|  |  | 
|  | /* Set up the remaining devices */ | 
|  | for (n = 1; n < NR_DEVS; n++) | 
|  | { | 
|  | dataseg->dev[n-1].next_dev  = (n+1) == NR_DEVS ? NONEXT : | 
|  | PTR_SEG_OFF_TO_SEGPTR(seg, DOS_DATASEG_OFF(dev[n])); | 
|  | dataseg->dev[n-1].attr      = devs[n].attr; | 
|  | dataseg->dev[n-1].strategy  = DOS_DATASEG_OFF(thunk[n].ljmp1); | 
|  | dataseg->dev[n-1].interrupt = DOS_DATASEG_OFF(thunk[n].ljmp2); | 
|  | memcpy(dataseg->dev[n-1].name, devs[n].name, 8); | 
|  | } | 
|  |  | 
|  | /* Set up thunks */ | 
|  | for (n = 0; n < NR_DEVS; n++) | 
|  | { | 
|  | dataseg->thunk[n].ljmp1     = LJMP; | 
|  | dataseg->thunk[n].strategy  = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].strategy); | 
|  | dataseg->thunk[n].ljmp2     = LJMP; | 
|  | dataseg->thunk[n].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].interrupt); | 
|  | } | 
|  |  | 
|  | /* CON is device 1 */ | 
|  | dataseg->lol.ptr_CON_dev_hdr = PTR_SEG_OFF_TO_SEGPTR(seg, DOS_DATASEG_OFF(dev[0])); | 
|  | } | 
|  |  | 
|  | DWORD DOSDEV_Console(void) | 
|  | { | 
|  | return DOSMEM_LOL()->ptr_CON_dev_hdr; | 
|  | } | 
|  |  | 
|  | DWORD DOSDEV_FindCharDevice(char*name) | 
|  | { | 
|  | SEGPTR cur_ptr = PTR_SEG_OFF_TO_SEGPTR(HIWORD(DOS_LOLSeg), | 
|  | FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev)); | 
|  | DOS_DEVICE_HEADER *cur = DOSMEM_MapRealToLinear(cur_ptr); | 
|  | char dname[8]; | 
|  | int cnt; | 
|  |  | 
|  | /* get first 8 characters */ | 
|  | strncpy(dname,name,8); | 
|  | /* if less than 8 characters, pad with spaces */ | 
|  | for (cnt=0; cnt<8; cnt++) | 
|  | if (!dname[cnt]) dname[cnt]=' '; | 
|  |  | 
|  | /* search for char devices with the right name */ | 
|  | while (cur && | 
|  | ((!(cur->attr & ATTR_CHAR)) || | 
|  | memcmp(cur->name,dname,8))) { | 
|  | cur_ptr = cur->next_dev; | 
|  | if (cur_ptr == NONEXT) cur=NULL; | 
|  | else cur = DOSMEM_MapRealToLinear(cur_ptr); | 
|  | } | 
|  | return cur_ptr; | 
|  | } | 
|  |  | 
|  | static void DOSDEV_DoReq(void*req, DWORD dev) | 
|  | { | 
|  | REQUEST_HEADER *hdr = (REQUEST_HEADER *)req; | 
|  | DOS_DEVICE_HEADER *dhdr; | 
|  | CONTEXT86 ctx; | 
|  | char *phdr; | 
|  |  | 
|  | dhdr = DOSMEM_MapRealToLinear(dev); | 
|  | phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req); | 
|  |  | 
|  | /* copy request to request scratch area */ | 
|  | memcpy(phdr, req, hdr->size); | 
|  |  | 
|  | /* prepare to call device driver */ | 
|  | memset(&ctx, 0, sizeof(ctx)); | 
|  |  | 
|  | /* ES:BX points to request for strategy routine */ | 
|  | ES_reg(&ctx)  = HIWORD(DOS_LOLSeg); | 
|  | EBX_reg(&ctx) = DOS_DATASEG_OFF(req); | 
|  |  | 
|  | /* call strategy routine */ | 
|  | CS_reg(&ctx) = SELECTOROF(dev); | 
|  | EIP_reg(&ctx) = dhdr->strategy; | 
|  | DPMI_CallRMProc(&ctx, 0, 0, 0); | 
|  |  | 
|  | /* call interrupt routine */ | 
|  | CS_reg(&ctx) = SELECTOROF(dev); | 
|  | EIP_reg(&ctx) = dhdr->interrupt; | 
|  | DPMI_CallRMProc(&ctx, 0, 0, 0); | 
|  |  | 
|  | /* completed, copy request back */ | 
|  | memcpy(req, phdr, hdr->size); | 
|  |  | 
|  | if (hdr->status & STAT_ERROR) { | 
|  | switch (hdr->status & STAT_MASK) { | 
|  | case 0x0F: /* invalid disk change */ | 
|  | /* this error seems to fit the bill */ | 
|  | SetLastError(ER_NotSameDevice); | 
|  | break; | 
|  | default: | 
|  | SetLastError((hdr->status & STAT_MASK) + 0x13); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen) | 
|  | { | 
|  | REQ_IO req; | 
|  |  | 
|  | req.hdr.size=sizeof(req); | 
|  | req.hdr.unit=0; /* not dealing with block devices yet */ | 
|  | req.hdr.command=cmd; | 
|  | req.hdr.status=STAT_BUSY; | 
|  | req.media=0; /* not dealing with block devices yet */ | 
|  | req.buffer=buf; | 
|  | req.count=buflen; | 
|  | req.sector=0; /* block devices */ | 
|  | req.volume=0; /* block devices */ | 
|  |  | 
|  | DOSDEV_DoReq(&req, dev); | 
|  |  | 
|  | return req.count; | 
|  | } | 
|  |  | 
|  | int DOSDEV_Peek(DWORD dev, BYTE*data) | 
|  | { | 
|  | REQ_SAFEINPUT req; | 
|  |  | 
|  | req.hdr.size=sizeof(req); | 
|  | req.hdr.unit=0; /* not dealing with block devices yet */ | 
|  | req.hdr.command=CMD_SAFEINPUT; | 
|  | req.hdr.status=STAT_BUSY; | 
|  | req.data=0; | 
|  |  | 
|  | DOSDEV_DoReq(&req, dev); | 
|  |  | 
|  | if (req.hdr.status & STAT_BUSY) return 0; | 
|  |  | 
|  | *data = req.data; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int DOSDEV_Read(DWORD dev, DWORD buf, int buflen) | 
|  | { | 
|  | return DOSDEV_IO(CMD_INPUT, dev, buf, buflen); | 
|  | } | 
|  |  | 
|  | int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify) | 
|  | { | 
|  | return DOSDEV_IO(verify?CMD_SAFEOUTPUT:CMD_OUTPUT, dev, buf, buflen); | 
|  | } | 
|  |  | 
|  | int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen) | 
|  | { | 
|  | return DOSDEV_IO(CMD_INIOCTL, dev, buf, buflen); | 
|  | } | 
|  |  | 
|  | int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen) | 
|  | { | 
|  | return DOSDEV_IO(CMD_OUTIOCTL, dev, buf, buflen); | 
|  | } |