| /* | |
| * 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 "callback.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 | |
| enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES }; | |
| static void *strategy_data[NB_SYSTEM_STRATEGIES]; | |
| #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, ctx->SegSs, ctx->Esp); | |
| ctx->Eip = *(stack++); | |
| ctx->SegCs = *(stack++); | |
| ctx->Esp += 2*sizeof(WORD); | |
| } | |
| static void do_strategy(CONTEXT86*ctx, int id, int extra) | |
| { | |
| REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx); | |
| void **hdr_ptr = strategy_data[id]; | |
| if (!hdr_ptr) { | |
| hdr_ptr = calloc(1,sizeof(void *)+extra); | |
| strategy_data[id] = hdr_ptr; | |
| } | |
| *hdr_ptr = hdr; | |
| do_lret(ctx); | |
| } | |
| static REQUEST_HEADER * get_hdr(int id, void**extra) | |
| { | |
| void **hdr_ptr = strategy_data[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; | |
| 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(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, NULL, NULL); | |
| } | |
| /* else beep, but I don't like noise */ | |
| } | |
| else switch (LOBYTE(data)) { | |
| case '\b': | |
| if (len>0) { | |
| len--; | |
| WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\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(GetStdHandle(STD_OUTPUT_HANDLE), 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; | |
| unsigned int n; | |
| /* allocate DOS data segment or something */ | |
| DOS_LOLSeg = GlobalDOSAlloc16(sizeof(DOS_DATASEG)); | |
| seg = HIWORD(DOS_LOLSeg); | |
| dataseg = MapSL( MAKESEGPTR(LOWORD(DOS_LOLSeg), 0) ); | |
| /* initialize the magnificent List Of Lists */ | |
| InitListOfLists(&dataseg->lol); | |
| /* Set up first device (NUL) */ | |
| dataseg->lol.NUL_dev.next_dev = MAKESEGPTR(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 : | |
| MAKESEGPTR(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 = MAKESEGPTR(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 = MAKESEGPTR(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 */ | |
| ctx.SegEs = HIWORD(DOS_LOLSeg); | |
| ctx.Ebx = DOS_DATASEG_OFF(req); | |
| /* call strategy routine */ | |
| ctx.SegCs = SELECTOROF(dev); | |
| ctx.Eip = dhdr->strategy; | |
| DPMI_CallRMProc(&ctx, 0, 0, 0); | |
| /* call interrupt routine */ | |
| ctx.SegCs = SELECTOROF(dev); | |
| ctx.Eip = 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); | |
| } |