DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/parport/parport.cpp
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