1 /*
2 * Copyright (C) 2002-2010 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 /* $Id: dos_ioctl.cpp,v 1.35 2009-04-16 12:16:52 qbix79 Exp $ */
20
21 #include <string.h>
22 #include "dosbox.h"
23 #include "callback.h"
24 #include "mem.h"
25 #include "regs.h"
26 #include "dos_inc.h"
27
DOS_IOCTL(void)28 bool DOS_IOCTL(void) {
29 Bitu handle=0;Bit8u drive=0;
30 /* calls 0-4,6,7,10,12,16 use a file handle */
31 if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) {
32 handle=RealHandle(reg_bx);
33 if (handle>=DOS_FILES) {
34 DOS_SetError(DOSERR_INVALID_HANDLE);
35 return false;
36 }
37 if (!Files[handle]) {
38 DOS_SetError(DOSERR_INVALID_HANDLE);
39 return false;
40 }
41 } else if (reg_al<0x12) { /* those use a diskdrive except 0x0b */
42 if (reg_al!=0x0b) {
43 drive=reg_bl;if (!drive) drive = DOS_GetDefaultDrive();else drive--;
44 if( !(( drive < DOS_DRIVES ) && Drives[drive]) ) {
45 DOS_SetError(DOSERR_INVALID_DRIVE);
46 return false;
47 }
48 }
49 } else {
50 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
51 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
52 return false;
53 }
54 switch(reg_al) {
55 case 0x00: /* Get Device Information */
56 if (Files[handle]->GetInformation() & 0x8000) { //Check for device
57 reg_dx=Files[handle]->GetInformation();
58 } else {
59 Bit8u hdrive=Files[handle]->GetDrive();
60 if (hdrive==0xff) {
61 LOG(LOG_IOCTL,LOG_NORMAL)("00:No drive set");
62 hdrive=2; // defaulting to C:
63 }
64 /* return drive number in lower 5 bits for block devices */
65 reg_dx=(Files[handle]->GetInformation()&0xffe0)|hdrive;
66 }
67 reg_ax=reg_dx; //Destroyed officially
68 return true;
69 case 0x01: /* Set Device Information */
70 if (reg_dh != 0) {
71 DOS_SetError(DOSERR_DATA_INVALID);
72 return false;
73 } else {
74 if (Files[handle]->GetInformation() & 0x8000) { //Check for device
75 reg_al=(Bit8u)(Files[handle]->GetInformation() & 0xff);
76 } else {
77 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
78 return false;
79 }
80 }
81 return true;
82 case 0x02: /* Read from Device Control Channel */
83 if (Files[handle]->GetInformation() & 0xc000) {
84 /* is character device with IOCTL support */
85 PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
86 Bit16u retcode=0;
87 if (((DOS_Device*)(Files[handle]))->ReadFromControlChannel(bufptr,reg_cx,&retcode)) {
88 reg_ax=retcode;
89 return true;
90 }
91 }
92 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
93 return false;
94 case 0x03: /* Write to Device Control Channel */
95 if (Files[handle]->GetInformation() & 0xc000) {
96 /* is character device with IOCTL support */
97 PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
98 Bit16u retcode=0;
99 if (((DOS_Device*)(Files[handle]))->WriteToControlChannel(bufptr,reg_cx,&retcode)) {
100 reg_ax=retcode;
101 return true;
102 }
103 }
104 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
105 return false;
106 case 0x06: /* Get Input Status */
107 if (Files[handle]->GetInformation() & 0x8000) { //Check for device
108 reg_al=(Files[handle]->GetInformation() & 0x40) ? 0x0 : 0xff;
109 } else { // FILE
110 Bit32u oldlocation=0;
111 Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR);
112 Bit32u endlocation=0;
113 Files[handle]->Seek(&endlocation, DOS_SEEK_END);
114 if(oldlocation < endlocation){//Still data available
115 reg_al=0xff;
116 } else {
117 reg_al=0x0; //EOF or beyond
118 }
119 Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); //restore filelocation
120 LOG(LOG_IOCTL,LOG_NORMAL)("06:Used Get Input Status on regular file with handle %d",handle);
121 }
122 return true;
123 case 0x07: /* Get Output Status */
124 LOG(LOG_IOCTL,LOG_NORMAL)("07:Fakes output status is ready for handle %d",handle);
125 reg_al=0xff;
126 return true;
127 case 0x08: /* Check if block device removable */
128 /* cdrom drives and drive a&b are removable */
129 if (drive < 2) reg_ax=0;
130 else if (!Drives[drive]->isRemovable()) reg_ax=1;
131 else {
132 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
133 return false;
134 }
135 return true;
136 case 0x09: /* Check if block device remote */
137 if (Drives[drive]->isRemote()) {
138 reg_dx=0x1000; // device is remote
139 // undocumented bits always clear
140 } else {
141 reg_dx=0x0802; // Open/Close supported; 32bit access supported (any use? fixes Fable installer)
142 // undocumented bits from device attribute word
143 // TODO Set bit 9 on drives that don't support direct I/O
144 }
145 reg_ax=0x300;
146 return true;
147 case 0x0B: /* Set sharing retry count */
148 if (reg_dx==0) {
149 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
150 return false;
151 }
152 return true;
153 case 0x0D: /* Generic block device request */
154 {
155 if (Drives[drive]->isRemovable()) {
156 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
157 return false;
158 }
159 PhysPt ptr = SegPhys(ds)+reg_dx;
160 switch (reg_cl) {
161 case 0x60: /* Get Device parameters */
162 mem_writeb(ptr ,0x03); // special function
163 mem_writeb(ptr+1,(drive>=2)?0x05:0x14); // fixed disc(5), 1.44 floppy(14)
164 mem_writew(ptr+2,drive>=2); // nonremovable ?
165 mem_writew(ptr+4,0x0000); // num of cylinders
166 mem_writeb(ptr+6,0x00); // media type (00=other type)
167 // drive parameter block following
168 mem_writeb(ptr+7,drive); // drive
169 mem_writeb(ptr+8,0x00); // unit number
170 mem_writed(ptr+0x1f,0xffffffff); // next parameter block
171 break;
172 case 0x46:
173 case 0x66: /* Volume label */
174 {
175 char const* bufin=Drives[drive]->GetLabel();
176 char buffer[11] ={' '};
177
178 char const* find_ext=strchr(bufin,'.');
179 if (find_ext) {
180 Bitu size=(Bitu)(find_ext-bufin);
181 if (size>8) size=8;
182 memcpy(buffer,bufin,size);
183 find_ext++;
184 memcpy(buffer+size,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
185 } else {
186 memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
187 }
188
189 char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
190 if(drive<2) buf2[4] = '2'; //FAT12 for floppies
191
192 mem_writew(ptr+0,0); // 0
193 mem_writed(ptr+2,0x1234); //Serial number
194 MEM_BlockWrite(ptr+6,buffer,11);//volumename
195 if(reg_cl == 0x66) MEM_BlockWrite(ptr+0x11, buf2,8);//filesystem
196 }
197 break;
198 default :
199 LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive);
200 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
201 return false;
202 }
203 return true;
204 }
205 case 0x0E: /* Get Logical Drive Map */
206 if (Drives[drive]->isRemovable()) {
207 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
208 return false;
209 }
210 reg_al = 0; /* Only 1 logical drive assigned */
211 return true;
212 default:
213 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
214 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
215 break;
216 }
217 return false;
218 }
219
220
DOS_GetSTDINStatus(void)221 bool DOS_GetSTDINStatus(void) {
222 Bit32u handle=RealHandle(STDIN);
223 if (handle==0xFF) return false;
224 if (Files[handle] && (Files[handle]->GetInformation() & 64)) return false;
225 return true;
226 }
227