DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 /* FIXME: At some point I would like to roll the Disney Sound Source emulation into this code */ 00019 00020 #include <string.h> 00021 #include <ctype.h> 00022 00023 #include "dosbox.h" 00024 00025 #include "support.h" 00026 #include "inout.h" 00027 #include "pic.h" 00028 #include "setup.h" 00029 #include "timer.h" 00030 #include "bios.h" // SetLPTPort(..) 00031 #include "hardware.h" // OpenCaptureFile 00032 00033 #include "parport.h" 00034 #include "directlpt_win32.h" 00035 #include "directlpt_linux.h" 00036 #include "printer_redir.h" 00037 #include "filelpt.h" 00038 #include "dos_inc.h" 00039 00040 bool device_LPT::Read(Bit8u * data,Bit16u * size) { 00041 (void)data;//UNUSED 00042 *size=0; 00043 LOG(LOG_DOSMISC,LOG_NORMAL)("LPTDEVICE:Read called"); 00044 return true; 00045 } 00046 00047 00048 bool device_LPT::Write(const Bit8u * data,Bit16u * size) { 00049 for (Bit16u i=0; i<*size; i++) 00050 { 00051 if(!pportclass->Putchar(data[i])) return false; 00052 } 00053 return true; 00054 } 00055 00056 bool device_LPT::Seek(Bit32u * pos,Bit32u type) { 00057 (void)type;//UNUSED 00058 *pos = 0; 00059 return true; 00060 } 00061 00062 bool device_LPT::Close() { 00063 return false; 00064 } 00065 00066 Bit16u device_LPT::GetInformation(void) { 00067 return 0x80A0; 00068 } 00069 00070 const char* lptname[]={"LPT1","LPT2","LPT3"}; 00071 device_LPT::device_LPT(Bit8u num, class CParallel* pp) { 00072 pportclass = pp; 00073 SetName(lptname[num]); 00074 this->num = num; 00075 } 00076 00077 device_LPT::~device_LPT() { 00078 /* clear reference to myself so that CParallel does not attempt to free me or ask DOS to delete me */ 00079 if (pportclass != NULL && pportclass->mydosdevice == this) 00080 pportclass->mydosdevice = NULL; 00081 00082 // LOG_MSG("~device_LPT\n"); 00083 //LOG_MSG("del"); 00084 } 00085 00086 static void Parallel_EventHandler(Bitu val) { 00087 Bitu serclassid=val&0x3; 00088 if(parallelPortObjects[serclassid]!=0) 00089 parallelPortObjects[serclassid]->handleEvent((Bit16u)(val>>2ul)); 00090 } 00091 00092 void CParallel::setEvent(Bit16u type, float duration) { 00093 PIC_AddEvent(Parallel_EventHandler,duration,((Bitu)type<<2u)|(Bitu)port_nr); 00094 } 00095 00096 void CParallel::removeEvent(Bit16u type) { 00097 // TODO 00098 PIC_RemoveSpecificEvents(Parallel_EventHandler,((Bitu)type<<2u)|(Bitu)port_nr); 00099 } 00100 00101 void CParallel::handleEvent(Bit16u type) { 00102 handleUpperEvent(type); 00103 } 00104 00105 static Bitu PARALLEL_Read (Bitu port, Bitu iolen) { 00106 (void)iolen;//UNUSED 00107 for(Bitu i = 0; i < 3; i++) { 00108 if(parallel_baseaddr[i]==(port&0xfffc) && (parallelPortObjects[i]!=0)) { 00109 Bitu retval=0xff; 00110 switch (port & 0x7) { 00111 case 0: 00112 retval = parallelPortObjects[i]->Read_PR(); 00113 break; 00114 case 1: 00115 retval = parallelPortObjects[i]->Read_SR(); 00116 break; 00117 case 2: 00118 retval = parallelPortObjects[i]->Read_COM(); 00119 break; 00120 } 00121 00122 #if PARALLEL_DEBUG 00123 const char* const dbgtext[]= {"DAT","STA","COM","???"}; 00124 parallelPortObjects[i]->log_par(parallelPortObjects[i]->dbg_cregs, 00125 "read 0x%2x from %s.",retval,dbgtext[port&3]); 00126 #endif 00127 return retval; 00128 } 00129 } 00130 return 0xff; 00131 } 00132 00133 static void PARALLEL_Write (Bitu port, Bitu val, Bitu) { 00134 for(Bitu i = 0; i < 3; i++) { 00135 if(parallel_baseaddr[i]==(port&0xfffc) && parallelPortObjects[i]) { 00136 #if PARALLEL_DEBUG 00137 const char* const dbgtext[]={"DAT","IOS","CON","???"}; 00138 parallelPortObjects[i]->log_par(parallelPortObjects[i]->dbg_cregs, 00139 "write 0x%2x to %s.",val,dbgtext[port&3]); 00140 if(parallelPortObjects[i]->dbg_plaindr &&!(port & 0x3)) { 00141 fprintf(parallelPortObjects[i]->debugfp,"%c",val); 00142 } 00143 #endif 00144 switch (port & 0x3) { 00145 case 0: 00146 parallelPortObjects[i]->Write_PR (val); 00147 return; 00148 case 1: 00149 parallelPortObjects[i]->Write_IOSEL (val); 00150 return; 00151 case 2: 00152 parallelPortObjects[i]->Write_CON (val); 00153 return; 00154 } 00155 } 00156 } 00157 } 00158 00159 //The Functions 00160 00161 #if PARALLEL_DEBUG 00162 #include <stdarg.h> 00163 void CParallel::log_par(bool active, char const* format,...) { 00164 if(active) { 00165 // copied from DEBUG_SHOWMSG 00166 char buf[512]; 00167 buf[0]=0; 00168 sprintf(buf,"%12.3f ",PIC_FullIndex()); 00169 va_list msg; 00170 va_start(msg,format); 00171 vsprintf(buf+strlen(buf),format,msg); 00172 va_end(msg); 00173 // Add newline if not present 00174 Bitu len=strlen(buf); 00175 if(buf[len-1]!='\n') strcat(buf,"\r\n"); 00176 fputs(buf,debugfp); 00177 } 00178 } 00179 #endif 00180 00181 // Initialisation 00182 CParallel::CParallel(CommandLine* cmd, Bitu portnr, Bit8u initirq) { 00183 (void)cmd;//UNUSED 00184 base = parallel_baseaddr[portnr]; 00185 irq = initirq; 00186 port_nr = portnr; 00187 00188 #if PARALLEL_DEBUG 00189 dbg_data = cmd->FindExist("dbgdata", false); 00190 dbg_putchar = cmd->FindExist("dbgput", false); 00191 dbg_cregs = cmd->FindExist("dbgregs", false); 00192 dbg_plainputchar = cmd->FindExist("dbgputplain", false); 00193 dbg_plaindr = cmd->FindExist("dbgdataplain", false); 00194 00195 if(cmd->FindExist("dbgall", false)) { 00196 dbg_data= 00197 dbg_putchar= 00198 dbg_cregs=true; 00199 dbg_plainputchar=dbg_plaindr=false; 00200 } 00201 00202 if(dbg_data||dbg_putchar||dbg_cregs||dbg_plainputchar||dbg_plaindr) 00203 debugfp=OpenCaptureFile("parlog",".parlog.txt"); 00204 else debugfp=0; 00205 00206 if(debugfp == 0) { 00207 dbg_data= 00208 dbg_putchar=dbg_plainputchar= 00209 dbg_cregs=false; 00210 } else { 00211 std::string cleft; 00212 cmd->GetStringRemain(cleft); 00213 00214 log_par(true,"Parallel%d: BASE %xh, initstring \"%s\"\r\n\r\n", 00215 portnr+1,base,cleft.c_str()); 00216 } 00217 #endif 00218 LOG_MSG("Parallel%d: BASE %xh",(int)portnr+1,(int)base); 00219 00220 for (Bitu i = 0; i < 3; i++) { 00221 /* bugfix: do not register I/O write handler for the status port. it's a *status* port. 00222 * also, this is needed for ISA PnP emulation to work properly even if DOSBox 00223 * is emulating more than one parallel port. */ 00224 if (i != 1) WriteHandler[i].Install (i + base, PARALLEL_Write, IO_MB); 00225 ReadHandler[i].Install (i + base, PARALLEL_Read, IO_MB); 00226 } 00227 00228 mydosdevice = NULL; 00229 } 00230 00231 void CParallel::registerDOSDevice() { 00232 if (mydosdevice == NULL) { 00233 LOG(LOG_MISC,LOG_DEBUG)("LPT%d: Registering DOS device",(int)port_nr+1); 00234 mydosdevice = new device_LPT((Bit8u)port_nr, this); 00235 DOS_AddDevice(mydosdevice); 00236 } 00237 } 00238 00239 void CParallel::unregisterDOSDevice() { 00240 if (mydosdevice != NULL) { 00241 LOG(LOG_MISC,LOG_DEBUG)("LPT%d: Unregistering DOS device",(int)port_nr+1); 00242 DOS_DelDevice(mydosdevice); // deletes the pointer for us! 00243 mydosdevice=NULL; 00244 } 00245 } 00246 00247 CParallel::~CParallel(void) { 00248 unregisterDOSDevice(); 00249 } 00250 00251 Bit8u CParallel::getPrinterStatus() 00252 { 00253 /* 7 not busy 00254 6 acknowledge 00255 5 out of paper 00256 4 selected 00257 3 I/O error 00258 2-1 unused 00259 0 timeout */ 00260 Bit8u statusreg=(Bit8u)Read_SR(); 00261 00262 //LOG_MSG("get printer status: %x",statusreg); 00263 statusreg^=0x48; 00264 return statusreg&~0x7; 00265 } 00266 00267 #include "callback.h" 00268 00269 void RunIdleTime(Bitu milliseconds) 00270 { 00271 Bitu time=SDL_GetTicks()+milliseconds; 00272 while(SDL_GetTicks()<time) 00273 CALLBACK_Idle(); 00274 } 00275 00276 void CParallel::initialize() 00277 { 00278 Write_IOSEL(0x55); // output mode 00279 Write_CON(0x08); // init low 00280 Write_PR(0); 00281 RunIdleTime(10); 00282 Write_CON(0x0c); // init high 00283 RunIdleTime(500); 00284 //LOG_MSG("printer init"); 00285 } 00286 00287 00288 00289 CParallel* parallelPortObjects[3]={NULL,NULL,NULL}; 00290 00291 bool DISNEY_HasInit(); 00292 Bitu DISNEY_BasePort(); 00293 bool DISNEY_ShouldInit(); 00294 void DISNEY_Init(unsigned int base_port); 00295 00296 Bitu bios_post_parport_count() { 00297 Bitu count = 0; 00298 unsigned int i; 00299 00300 for (i=0;i < 3;i++) { 00301 if (parallelPortObjects[i] != NULL) 00302 count++; 00303 else if (DISNEY_HasInit() && parallel_baseaddr[i] == DISNEY_BasePort()) 00304 count++; 00305 } 00306 00307 return count; 00308 } 00309 00310 /* at BIOS POST stage, write parallel ports to bios data area */ 00311 void BIOS_Post_register_parports() { 00312 unsigned int i; 00313 00314 for (i=0;i < 3;i++) { 00315 if (parallelPortObjects[i] != NULL) 00316 BIOS_SetLPTPort(i,(Bit16u)parallelPortObjects[i]->base); 00317 else if (DISNEY_HasInit() && parallel_baseaddr[i] == (Bit16u)DISNEY_BasePort()) 00318 BIOS_SetLPTPort(i,(Bit16u)DISNEY_BasePort()); 00319 } 00320 } 00321 00322 class PARPORTS:public Module_base { 00323 public: 00324 00325 PARPORTS (Section * configuration):Module_base (configuration) { 00326 00327 // TODO: PC-98 does have one parallel port, if at all 00328 if (IS_PC98_ARCH) return; 00329 00330 #if C_PRINTER 00331 // we can only have one printer redirection, hence the variable 00332 bool printer_used = false; 00333 #endif 00334 00335 // default ports & interrupts 00336 Bit8u defaultirq[] = { 7, 5, 12}; 00337 Section_prop *section = static_cast <Section_prop*>(configuration); 00338 00339 char pname[]="parallelx"; 00340 // iterate through all 3 lpt ports 00341 for (Bitu i = 0; i < 3; i++) { 00342 pname[8] = '1' + (char)i; 00343 CommandLine cmd(0,section->Get_string(pname)); 00344 00345 std::string str; 00346 cmd.FindCommand(1,str); 00347 #if C_DIRECTLPT 00348 if(str=="reallpt") { 00349 CDirectLPT* cdlpt= new CDirectLPT(i, defaultirq[i],&cmd); 00350 if(cdlpt->InstallationSuccessful) 00351 parallelPortObjects[i]=cdlpt; 00352 else { 00353 delete cdlpt; 00354 parallelPortObjects[i]=0; 00355 } 00356 } 00357 else 00358 #endif 00359 if(!str.compare("file")) { 00360 CFileLPT* cflpt= new CFileLPT(i, defaultirq[i], &cmd); 00361 if(cflpt->InstallationSuccessful) 00362 parallelPortObjects[i]=cflpt; 00363 else { 00364 delete cflpt; 00365 parallelPortObjects[i]=0; 00366 } 00367 } 00368 else 00369 #if C_PRINTER 00370 // allow printer redirection on a single port 00371 if (str == "printer" && !printer_used) 00372 { 00373 CPrinterRedir* cprd = new CPrinterRedir(i, defaultirq[i], &cmd); 00374 if (cprd->InstallationSuccessful) 00375 { 00376 parallelPortObjects[i] = cprd; 00377 printer_used = true; 00378 } 00379 else 00380 { 00381 LOG_MSG("Error: printer is not enabled."); 00382 delete cprd; 00383 parallelPortObjects[i] = 0; 00384 } 00385 } 00386 else 00387 #endif 00388 if(str=="disabled") { 00389 parallelPortObjects[i] = 0; 00390 } else if (str == "disney") { 00391 if (!DISNEY_HasInit()) { 00392 LOG_MSG("LPT%d: User explicitly assigned Disney Sound Source to this port",(int)i+1); 00393 DISNEY_Init(parallel_baseaddr[i]); 00394 } 00395 else { 00396 LOG_MSG("LPT%d: Disney Sound Source already initialized on a port, cannot init again",(int)i+1); 00397 } 00398 } else { 00399 LOG_MSG ("Invalid type for LPT%d.",(int)i + 1); 00400 parallelPortObjects[i] = 0; 00401 } 00402 } // for lpt 1-3 00403 } 00404 00405 ~PARPORTS () { 00406 // LOG_MSG("Parports destructor\n"); 00407 for (Bitu i = 0; i < 3; i++) { 00408 if (parallelPortObjects[i]) { 00409 delete parallelPortObjects[i]; 00410 parallelPortObjects[i] = 0; 00411 } 00412 } 00413 } 00414 }; 00415 00416 static PARPORTS *testParallelPortsBaseclass = NULL; 00417 00418 void PARALLEL_Destroy (Section * sec) { 00419 (void)sec;//UNUSED 00420 if (testParallelPortsBaseclass != NULL) { 00421 delete testParallelPortsBaseclass; 00422 testParallelPortsBaseclass = NULL; 00423 } 00424 } 00425 00426 void PARALLEL_OnPowerOn (Section * sec) { 00427 (void)sec;//UNUSED 00428 LOG(LOG_MISC,LOG_DEBUG)("Reinitializing parallel port emulation"); 00429 00430 if (testParallelPortsBaseclass) delete testParallelPortsBaseclass; 00431 testParallelPortsBaseclass = new PARPORTS (control->GetSection("parallel")); 00432 00433 /* Mainline DOSBox 0.74 compatible support for "disney=true" setting. 00434 * But, don't allocate the Disney Sound Source if LPT1 is already taken. */ 00435 if (!DISNEY_HasInit() && DISNEY_ShouldInit() && parallelPortObjects[0] == NULL) { 00436 LOG_MSG("LPT: LPT1 not taken, and dosbox.conf says to emulate Disney Sound Source"); 00437 DISNEY_Init(parallel_baseaddr[0]); 00438 } 00439 } 00440 00441 void PARALLEL_OnDOSKernelInit (Section * sec) { 00442 (void)sec;//UNUSED 00443 unsigned int i; 00444 00445 LOG(LOG_MISC,LOG_DEBUG)("DOS kernel initializing, creating LPTx devices"); 00446 00447 for (i=0;i < 3;i++) { 00448 if (parallelPortObjects[i] != NULL) 00449 parallelPortObjects[i]->registerDOSDevice(); 00450 } 00451 } 00452 00453 void PARALLEL_OnDOSKernelExit (Section * sec) { 00454 (void)sec;//UNUSED 00455 unsigned int i; 00456 00457 for (i=0;i < 3;i++) { 00458 if (parallelPortObjects[i] != NULL) 00459 parallelPortObjects[i]->unregisterDOSDevice(); 00460 } 00461 } 00462 00463 void PARALLEL_OnReset (Section * sec) { 00464 (void)sec;//UNUSED 00465 unsigned int i; 00466 00467 // FIXME: Unregister/destroy the DOS devices, but consider that the DOS kernel at reset is gone. 00468 for (i=0;i < 3;i++) { 00469 if (parallelPortObjects[i] != NULL) 00470 parallelPortObjects[i]->unregisterDOSDevice(); 00471 } 00472 } 00473 00474 void PARALLEL_Init () { 00475 LOG(LOG_MISC,LOG_DEBUG)("Initializing parallel port emulation"); 00476 00477 AddExitFunction(AddExitFunctionFuncPair(PARALLEL_Destroy),true); 00478 00479 if (!IS_PC98_ARCH) { 00480 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PARALLEL_OnReset)); 00481 AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(PARALLEL_OnPowerOn)); 00482 AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(PARALLEL_OnDOSKernelExit)); 00483 AddVMEventFunction(VM_EVENT_DOS_INIT_KERNEL_READY,AddVMEventFunctionFuncPair(PARALLEL_OnDOSKernelInit)); 00484 } 00485 } 00486